diff options
Diffstat (limited to 'src/burn/engine/apply.cpp')
-rw-r--r-- | src/burn/engine/apply.cpp | 3096 |
1 files changed, 3096 insertions, 0 deletions
diff --git a/src/burn/engine/apply.cpp b/src/burn/engine/apply.cpp new file mode 100644 index 00000000..58d41b28 --- /dev/null +++ b/src/burn/engine/apply.cpp | |||
@@ -0,0 +1,3096 @@ | |||
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 | #ifdef DEBUG | ||
7 | #define IgnoreRollbackError(x, f, ...) if (FAILED(x)) { TraceError(x, f, __VA_ARGS__); } | ||
8 | #else | ||
9 | #define IgnoreRollbackError(x, f, ...) | ||
10 | #endif | ||
11 | |||
12 | const DWORD BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS = 2; | ||
13 | |||
14 | enum BURN_CACHE_PROGRESS_TYPE | ||
15 | { | ||
16 | BURN_CACHE_PROGRESS_TYPE_ACQUIRE, | ||
17 | BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY, | ||
18 | BURN_CACHE_PROGRESS_TYPE_EXTRACT, | ||
19 | BURN_CACHE_PROGRESS_TYPE_FINALIZE, | ||
20 | BURN_CACHE_PROGRESS_TYPE_HASH, | ||
21 | BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY, | ||
22 | BURN_CACHE_PROGRESS_TYPE_STAGE, | ||
23 | }; | ||
24 | |||
25 | // structs | ||
26 | |||
27 | typedef struct _BURN_CACHE_CONTEXT | ||
28 | { | ||
29 | BURN_USER_EXPERIENCE* pUX; | ||
30 | BURN_VARIABLES* pVariables; | ||
31 | BURN_PAYLOADS* pPayloads; | ||
32 | HANDLE hPipe; | ||
33 | HANDLE hSourceEngineFile; | ||
34 | DWORD64 qwTotalCacheSize; | ||
35 | DWORD64 qwSuccessfulCacheProgress; | ||
36 | LPCWSTR wzLayoutDirectory; | ||
37 | LPWSTR* rgSearchPaths; | ||
38 | DWORD cSearchPaths; | ||
39 | DWORD cSearchPathsMax; | ||
40 | LPWSTR sczLastUsedFolderCandidate; | ||
41 | } BURN_CACHE_CONTEXT; | ||
42 | |||
43 | typedef struct _BURN_CACHE_PROGRESS_CONTEXT | ||
44 | { | ||
45 | BURN_CACHE_CONTEXT* pCacheContext; | ||
46 | BURN_CACHE_PROGRESS_TYPE type; | ||
47 | BURN_CONTAINER* pContainer; | ||
48 | BURN_PACKAGE* pPackage; | ||
49 | BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem; | ||
50 | BURN_PAYLOAD* pPayload; | ||
51 | |||
52 | BOOL fCancel; | ||
53 | HRESULT hrError; | ||
54 | } BURN_CACHE_PROGRESS_CONTEXT; | ||
55 | |||
56 | typedef struct _BURN_EXECUTE_CONTEXT | ||
57 | { | ||
58 | BURN_USER_EXPERIENCE* pUX; | ||
59 | BOOL fRollback; | ||
60 | BURN_PACKAGE* pExecutingPackage; | ||
61 | DWORD cExecutedPackages; | ||
62 | DWORD cExecutePackagesTotal; | ||
63 | DWORD* pcOverallProgressTicks; | ||
64 | } BURN_EXECUTE_CONTEXT; | ||
65 | |||
66 | |||
67 | // internal function declarations | ||
68 | static HRESULT WINAPI AuthenticationRequired( | ||
69 | __in LPVOID pData, | ||
70 | __in HINTERNET hUrl, | ||
71 | __in long lHttpCode, | ||
72 | __out BOOL* pfRetrySend, | ||
73 | __out BOOL* pfRetry | ||
74 | ); | ||
75 | |||
76 | static void CalculateKeepRegistration( | ||
77 | __in BURN_ENGINE_STATE* pEngineState, | ||
78 | __inout BOOL* pfKeepRegistration | ||
79 | ); | ||
80 | static HRESULT ExecuteDependentRegistrationActions( | ||
81 | __in HANDLE hPipe, | ||
82 | __in const BURN_REGISTRATION* pRegistration, | ||
83 | __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions, | ||
84 | __in DWORD cActions | ||
85 | ); | ||
86 | static HRESULT ApplyCachePackage( | ||
87 | __in BURN_CACHE_CONTEXT* pContext, | ||
88 | __in BURN_PACKAGE* pPackage | ||
89 | ); | ||
90 | static HRESULT ApplyExtractContainer( | ||
91 | __in BURN_CACHE_CONTEXT* pContext, | ||
92 | __in BURN_CONTAINER* pContainer | ||
93 | ); | ||
94 | static HRESULT ApplyLayoutBundle( | ||
95 | __in BURN_CACHE_CONTEXT* pContext, | ||
96 | __in BURN_PAYLOAD_GROUP* pPayloads, | ||
97 | __in_z LPCWSTR wzExecutableName, | ||
98 | __in_z LPCWSTR wzUnverifiedPath, | ||
99 | __in DWORD64 qwBundleSize | ||
100 | ); | ||
101 | static HRESULT ApplyLayoutContainer( | ||
102 | __in BURN_CACHE_CONTEXT* pContext, | ||
103 | __in BURN_CONTAINER* pContainer | ||
104 | ); | ||
105 | static HRESULT ApplyProcessPayload( | ||
106 | __in BURN_CACHE_CONTEXT* pContext, | ||
107 | __in_opt BURN_PACKAGE* pPackage, | ||
108 | __in BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem | ||
109 | ); | ||
110 | static HRESULT ApplyCacheVerifyContainerOrPayload( | ||
111 | __in BURN_CACHE_CONTEXT* pContext, | ||
112 | __in_opt BURN_CONTAINER* pContainer, | ||
113 | __in_opt BURN_PACKAGE* pPackage, | ||
114 | __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem | ||
115 | ); | ||
116 | static HRESULT ExtractContainer( | ||
117 | __in BURN_CACHE_CONTEXT* pContext, | ||
118 | __in BURN_CONTAINER* pContainer | ||
119 | ); | ||
120 | static HRESULT LayoutBundle( | ||
121 | __in BURN_CACHE_CONTEXT* pContext, | ||
122 | __in_z LPCWSTR wzExecutableName, | ||
123 | __in_z LPCWSTR wzUnverifiedPath, | ||
124 | __in DWORD64 qwBundleSize | ||
125 | ); | ||
126 | static HRESULT ApplyAcquireContainerOrPayload( | ||
127 | __in BURN_CACHE_CONTEXT* pContext, | ||
128 | __in_opt BURN_CONTAINER* pContainer, | ||
129 | __in_opt BURN_PACKAGE* pPackage, | ||
130 | __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem | ||
131 | ); | ||
132 | static HRESULT AcquireContainerOrPayload( | ||
133 | __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, | ||
134 | __out BOOL* pfRetry | ||
135 | ); | ||
136 | static BOOL IsValidLocalFile( | ||
137 | __in_z LPCWSTR wzFilePath, | ||
138 | __in DWORD64 qwFileSize, | ||
139 | __in BOOL fMinimumFileSize | ||
140 | ); | ||
141 | static HRESULT LayoutOrCacheContainerOrPayload( | ||
142 | __in BURN_CACHE_CONTEXT* pContext, | ||
143 | __in_opt BURN_CONTAINER* pContainer, | ||
144 | __in_opt BURN_PACKAGE* pPackage, | ||
145 | __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem, | ||
146 | __in DWORD cTryAgainAttempts, | ||
147 | __out BOOL* pfRetry | ||
148 | ); | ||
149 | static HRESULT PreparePayloadDestinationPath( | ||
150 | __in_z LPCWSTR wzDestinationPath | ||
151 | ); | ||
152 | static HRESULT CopyPayload( | ||
153 | __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, | ||
154 | __in HANDLE hSourceFile, | ||
155 | __in_z LPCWSTR wzSourcePath, | ||
156 | __in_z LPCWSTR wzDestinationPath | ||
157 | ); | ||
158 | static HRESULT DownloadPayload( | ||
159 | __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, | ||
160 | __in_z LPCWSTR wzDestinationPath | ||
161 | ); | ||
162 | static HRESULT CALLBACK CacheMessageHandler( | ||
163 | __in BURN_CACHE_MESSAGE* pMessage, | ||
164 | __in LPVOID pvContext | ||
165 | ); | ||
166 | static HRESULT CompleteCacheProgress( | ||
167 | __in BURN_CACHE_PROGRESS_CONTEXT* pContext, | ||
168 | __in DWORD64 qwFileSize | ||
169 | ); | ||
170 | static DWORD CALLBACK CacheProgressRoutine( | ||
171 | __in LARGE_INTEGER TotalFileSize, | ||
172 | __in LARGE_INTEGER TotalBytesTransferred, | ||
173 | __in LARGE_INTEGER StreamSize, | ||
174 | __in LARGE_INTEGER StreamBytesTransferred, | ||
175 | __in DWORD dwStreamNumber, | ||
176 | __in DWORD dwCallbackReason, | ||
177 | __in HANDLE hSourceFile, | ||
178 | __in HANDLE hDestinationFile, | ||
179 | __in_opt LPVOID lpData | ||
180 | ); | ||
181 | static void DoRollbackCache( | ||
182 | __in BURN_USER_EXPERIENCE* pUX, | ||
183 | __in BURN_PLAN* pPlan, | ||
184 | __in HANDLE hPipe, | ||
185 | __in DWORD dwCheckpoint | ||
186 | ); | ||
187 | static HRESULT DoExecuteAction( | ||
188 | __in BURN_ENGINE_STATE* pEngineState, | ||
189 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
190 | __in_opt HANDLE hCacheThread, | ||
191 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
192 | __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, | ||
193 | __inout BURN_EXECUTE_ACTION_CHECKPOINT** ppCheckpoint, | ||
194 | __out BOOL* pfSuspend, | ||
195 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
196 | ); | ||
197 | static HRESULT DoRollbackActions( | ||
198 | __in BURN_ENGINE_STATE* pEngineState, | ||
199 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
200 | __in DWORD dwCheckpoint, | ||
201 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
202 | ); | ||
203 | static HRESULT ExecuteExePackage( | ||
204 | __in BURN_ENGINE_STATE* pEngineState, | ||
205 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
206 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
207 | __in BOOL fRollback, | ||
208 | __out BOOL* pfRetry, | ||
209 | __out BOOL* pfSuspend, | ||
210 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
211 | ); | ||
212 | static HRESULT ExecuteMsiPackage( | ||
213 | __in BURN_ENGINE_STATE* pEngineState, | ||
214 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
215 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
216 | __in BOOL fInsideMsiTransaction, | ||
217 | __in BOOL fRollback, | ||
218 | __out BOOL* pfRetry, | ||
219 | __out BOOL* pfSuspend, | ||
220 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
221 | ); | ||
222 | static HRESULT ExecuteMspPackage( | ||
223 | __in BURN_ENGINE_STATE* pEngineState, | ||
224 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
225 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
226 | __in BOOL fInsideMsiTransaction, | ||
227 | __in BOOL fRollback, | ||
228 | __out BOOL* pfRetry, | ||
229 | __out BOOL* pfSuspend, | ||
230 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
231 | ); | ||
232 | static HRESULT ExecuteMsuPackage( | ||
233 | __in BURN_ENGINE_STATE* pEngineState, | ||
234 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
235 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
236 | __in BOOL fRollback, | ||
237 | __in BOOL fStopWusaService, | ||
238 | __out BOOL* pfRetry, | ||
239 | __out BOOL* pfSuspend, | ||
240 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
241 | ); | ||
242 | static HRESULT ExecutePackageProviderAction( | ||
243 | __in BURN_ENGINE_STATE* pEngineState, | ||
244 | __in BURN_EXECUTE_ACTION* pAction, | ||
245 | __in BURN_EXECUTE_CONTEXT* pContext | ||
246 | ); | ||
247 | static HRESULT ExecuteDependencyAction( | ||
248 | __in BURN_ENGINE_STATE* pEngineState, | ||
249 | __in BURN_EXECUTE_ACTION* pAction, | ||
250 | __in BURN_EXECUTE_CONTEXT* pContext | ||
251 | ); | ||
252 | static HRESULT ExecuteMsiBeginTransaction( | ||
253 | __in BURN_ENGINE_STATE* pEngineState, | ||
254 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, | ||
255 | __in BURN_EXECUTE_CONTEXT* pContext | ||
256 | ); | ||
257 | static HRESULT ExecuteMsiCommitTransaction( | ||
258 | __in BURN_ENGINE_STATE* pEngineState, | ||
259 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, | ||
260 | __in BURN_EXECUTE_CONTEXT* pContext | ||
261 | ); | ||
262 | static HRESULT ExecuteMsiRollbackTransaction( | ||
263 | __in BURN_ENGINE_STATE* pEngineState, | ||
264 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, | ||
265 | __in BURN_EXECUTE_CONTEXT* pContext | ||
266 | ); | ||
267 | static void ResetTransactionRegistrationState( | ||
268 | __in BURN_ENGINE_STATE* pEngineState, | ||
269 | __in BOOL fCommit | ||
270 | ); | ||
271 | static HRESULT CleanPackage( | ||
272 | __in HANDLE hElevatedPipe, | ||
273 | __in BURN_PACKAGE* pPackage | ||
274 | ); | ||
275 | static int GenericExecuteMessageHandler( | ||
276 | __in GENERIC_EXECUTE_MESSAGE* pMessage, | ||
277 | __in LPVOID pvContext | ||
278 | ); | ||
279 | static int MsiExecuteMessageHandler( | ||
280 | __in WIU_MSI_EXECUTE_MESSAGE* pMessage, | ||
281 | __in_opt LPVOID pvContext | ||
282 | ); | ||
283 | static HRESULT ReportOverallProgressTicks( | ||
284 | __in BURN_USER_EXPERIENCE* pUX, | ||
285 | __in BOOL fRollback, | ||
286 | __in DWORD cOverallProgressTicksTotal, | ||
287 | __in DWORD cOverallProgressTicks | ||
288 | ); | ||
289 | static HRESULT ExecutePackageComplete( | ||
290 | __in BURN_USER_EXPERIENCE* pUX, | ||
291 | __in BURN_VARIABLES* pVariables, | ||
292 | __in BURN_PACKAGE* pPackage, | ||
293 | __in HRESULT hrOverall, | ||
294 | __in HRESULT hrExecute, | ||
295 | __in BOOL fRollback, | ||
296 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart, | ||
297 | __out BOOL* pfRetry, | ||
298 | __out BOOL* pfSuspend | ||
299 | ); | ||
300 | |||
301 | |||
302 | // function definitions | ||
303 | |||
304 | extern "C" void ApplyInitialize() | ||
305 | { | ||
306 | // Prevent the system from sleeping. | ||
307 | ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); | ||
308 | } | ||
309 | |||
310 | extern "C" void ApplyUninitialize() | ||
311 | { | ||
312 | ::SetThreadExecutionState(ES_CONTINUOUS); | ||
313 | } | ||
314 | |||
315 | extern "C" HRESULT ApplySetVariables( | ||
316 | __in BURN_VARIABLES* pVariables | ||
317 | ) | ||
318 | { | ||
319 | HRESULT hr = S_OK; | ||
320 | |||
321 | hr = VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, NULL, TRUE, FALSE); | ||
322 | ExitOnFailure(hr, "Failed to set the bundle forced restart package built-in variable."); | ||
323 | |||
324 | LExit: | ||
325 | return hr; | ||
326 | } | ||
327 | |||
328 | extern "C" void ApplyReset( | ||
329 | __in BURN_USER_EXPERIENCE* pUX, | ||
330 | __in BURN_PACKAGES* pPackages | ||
331 | ) | ||
332 | { | ||
333 | UserExperienceExecuteReset(pUX); | ||
334 | |||
335 | for (DWORD i = 0; i < pPackages->cPackages; ++i) | ||
336 | { | ||
337 | BURN_PACKAGE* pPackage = pPackages->rgPackages + i; | ||
338 | pPackage->hrCacheResult = S_OK; | ||
339 | pPackage->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; | ||
340 | } | ||
341 | } | ||
342 | |||
343 | extern "C" HRESULT ApplyLock( | ||
344 | __in BOOL /*fPerMachine*/, | ||
345 | __out HANDLE* phLock | ||
346 | ) | ||
347 | { | ||
348 | HRESULT hr = S_OK; | ||
349 | *phLock = NULL; | ||
350 | |||
351 | #if 0 // eventually figure out the correct way to support this. In its current form, embedded bundles (including related bundles) are hosed. | ||
352 | DWORD er = ERROR_SUCCESS; | ||
353 | HANDLE hLock = NULL; | ||
354 | |||
355 | hLock = ::CreateMutexW(NULL, TRUE, fPerMachine ? L"Global\\WixBurnExecutionLock" : L"Local\\WixBurnExecutionLock"); | ||
356 | ExitOnNullWithLastError(hLock, hr, "Failed to create lock."); | ||
357 | |||
358 | er = ::GetLastError(); | ||
359 | if (ERROR_ALREADY_EXISTS == er) | ||
360 | { | ||
361 | ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSTALL_ALREADY_RUNNING)); | ||
362 | } | ||
363 | |||
364 | *phLock = hLock; | ||
365 | hLock = NULL; | ||
366 | |||
367 | LExit: | ||
368 | ReleaseHandle(hLock); | ||
369 | #endif | ||
370 | return hr; | ||
371 | } | ||
372 | |||
373 | extern "C" HRESULT ApplyRegister( | ||
374 | __in BURN_ENGINE_STATE* pEngineState | ||
375 | ) | ||
376 | { | ||
377 | HRESULT hr = S_OK; | ||
378 | LPWSTR sczEngineWorkingPath = NULL; | ||
379 | |||
380 | hr = UserExperienceOnRegisterBegin(&pEngineState->userExperience); | ||
381 | ExitOnRootFailure(hr, "BA aborted register begin."); | ||
382 | |||
383 | // If we have a resume mode that suggests the bundle is on the machine. | ||
384 | if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING < pEngineState->command.resumeType) | ||
385 | { | ||
386 | // resume previous session | ||
387 | if (pEngineState->registration.fPerMachine) | ||
388 | { | ||
389 | hr = ElevationSessionResume(pEngineState->companionConnection.hPipe, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables); | ||
390 | ExitOnFailure(hr, "Failed to resume registration session in per-machine process."); | ||
391 | } | ||
392 | else | ||
393 | { | ||
394 | hr = RegistrationSessionResume(&pEngineState->registration, &pEngineState->variables); | ||
395 | ExitOnFailure(hr, "Failed to resume registration session."); | ||
396 | } | ||
397 | } | ||
398 | else // need to complete registration on the machine. | ||
399 | { | ||
400 | hr = CacheCalculateBundleWorkingPath(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &sczEngineWorkingPath); | ||
401 | ExitOnFailure(hr, "Failed to calculate working path for engine."); | ||
402 | |||
403 | // begin new session | ||
404 | if (pEngineState->registration.fPerMachine) | ||
405 | { | ||
406 | hr = ElevationSessionBegin(pEngineState->companionConnection.hPipe, sczEngineWorkingPath, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize); | ||
407 | ExitOnFailure(hr, "Failed to begin registration session in per-machine process."); | ||
408 | } | ||
409 | else | ||
410 | { | ||
411 | hr = RegistrationSessionBegin(sczEngineWorkingPath, &pEngineState->registration, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize); | ||
412 | ExitOnFailure(hr, "Failed to begin registration session."); | ||
413 | } | ||
414 | } | ||
415 | |||
416 | // Apply any registration actions. | ||
417 | HRESULT hrExecuteRegistration = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRegistrationActions, pEngineState->plan.cRegistrationActions); | ||
418 | UNREFERENCED_PARAMETER(hrExecuteRegistration); | ||
419 | |||
420 | // Try to save engine state. | ||
421 | hr = CoreSaveEngineState(pEngineState); | ||
422 | if (FAILED(hr)) | ||
423 | { | ||
424 | LogErrorId(hr, MSG_STATE_NOT_SAVED); | ||
425 | hr = S_OK; | ||
426 | } | ||
427 | |||
428 | LExit: | ||
429 | UserExperienceOnRegisterComplete(&pEngineState->userExperience, hr); | ||
430 | ReleaseStr(sczEngineWorkingPath); | ||
431 | |||
432 | return hr; | ||
433 | } | ||
434 | |||
435 | extern "C" HRESULT ApplyUnregister( | ||
436 | __in BURN_ENGINE_STATE* pEngineState, | ||
437 | __in BOOL fFailedOrRollback, | ||
438 | __in BOOL fSuspend, | ||
439 | __in BOOTSTRAPPER_APPLY_RESTART restart | ||
440 | ) | ||
441 | { | ||
442 | HRESULT hr = S_OK; | ||
443 | BURN_RESUME_MODE resumeMode = BURN_RESUME_MODE_NONE; | ||
444 | BOOL fKeepRegistration = pEngineState->plan.fDisallowRemoval; | ||
445 | |||
446 | CalculateKeepRegistration(pEngineState, &fKeepRegistration); | ||
447 | |||
448 | hr = UserExperienceOnUnregisterBegin(&pEngineState->userExperience, &fKeepRegistration); | ||
449 | ExitOnRootFailure(hr, "BA aborted unregister begin."); | ||
450 | |||
451 | // Calculate the correct resume mode. If a restart has been initiated, that trumps all other | ||
452 | // modes. If the user chose to suspend the install then we'll use that as the resume mode. | ||
453 | // Barring those special cases, if it was determined that we should keep the registration then | ||
454 | // do that, otherwise the resume mode was initialized to none and registration will be removed. | ||
455 | if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) | ||
456 | { | ||
457 | resumeMode = BURN_RESUME_MODE_REBOOT_PENDING; | ||
458 | } | ||
459 | else if (fSuspend) | ||
460 | { | ||
461 | resumeMode = BURN_RESUME_MODE_SUSPEND; | ||
462 | } | ||
463 | else if (fKeepRegistration) | ||
464 | { | ||
465 | resumeMode = BURN_RESUME_MODE_ARP; | ||
466 | } | ||
467 | |||
468 | // If apply failed in any way and we're going to be keeping the bundle registered then | ||
469 | // execute any rollback dependency registration actions. | ||
470 | if (fFailedOrRollback && fKeepRegistration) | ||
471 | { | ||
472 | // Execute any rollback registration actions. | ||
473 | HRESULT hrRegistrationRollback = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRollbackRegistrationActions, pEngineState->plan.cRollbackRegistrationActions); | ||
474 | UNREFERENCED_PARAMETER(hrRegistrationRollback); | ||
475 | } | ||
476 | |||
477 | if (pEngineState->registration.fPerMachine) | ||
478 | { | ||
479 | hr = ElevationSessionEnd(pEngineState->companionConnection.hPipe, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction); | ||
480 | ExitOnFailure(hr, "Failed to end session in per-machine process."); | ||
481 | } | ||
482 | else | ||
483 | { | ||
484 | hr = RegistrationSessionEnd(&pEngineState->registration, &pEngineState->variables, &pEngineState->packages, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction); | ||
485 | ExitOnFailure(hr, "Failed to end session in per-user process."); | ||
486 | } | ||
487 | |||
488 | pEngineState->resumeMode = resumeMode; | ||
489 | |||
490 | LExit: | ||
491 | UserExperienceOnUnregisterComplete(&pEngineState->userExperience, hr); | ||
492 | |||
493 | return hr; | ||
494 | } | ||
495 | |||
496 | extern "C" HRESULT ApplyCache( | ||
497 | __in HANDLE hSourceEngineFile, | ||
498 | __in BURN_USER_EXPERIENCE* pUX, | ||
499 | __in BURN_VARIABLES* pVariables, | ||
500 | __in BURN_PLAN* pPlan, | ||
501 | __in HANDLE hPipe, | ||
502 | __inout DWORD* pcOverallProgressTicks, | ||
503 | __inout BOOL* pfRollback | ||
504 | ) | ||
505 | { | ||
506 | HRESULT hr = S_OK; | ||
507 | DWORD dwCheckpoint = 0; | ||
508 | BURN_CACHE_CONTEXT cacheContext = { }; | ||
509 | BURN_PACKAGE* pPackage = NULL; | ||
510 | |||
511 | *pfRollback = FALSE; | ||
512 | |||
513 | hr = UserExperienceOnCacheBegin(pUX); | ||
514 | ExitOnRootFailure(hr, "BA aborted cache."); | ||
515 | |||
516 | cacheContext.hSourceEngineFile = hSourceEngineFile; | ||
517 | cacheContext.pPayloads = pPlan->pPayloads; | ||
518 | cacheContext.pUX = pUX; | ||
519 | cacheContext.pVariables = pVariables; | ||
520 | cacheContext.qwTotalCacheSize = pPlan->qwCacheSizeTotal; | ||
521 | cacheContext.wzLayoutDirectory = pPlan->sczLayoutDirectory; | ||
522 | |||
523 | hr = MemAllocArray(reinterpret_cast<LPVOID*>(&cacheContext.rgSearchPaths), sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); | ||
524 | ExitOnNull(cacheContext.rgSearchPaths, hr, E_OUTOFMEMORY, "Failed to allocate cache search paths array."); | ||
525 | |||
526 | for (DWORD i = 0; i < pPlan->cCacheActions; ++i) | ||
527 | { | ||
528 | BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i; | ||
529 | cacheContext.hPipe = hPipe; | ||
530 | pPackage = NULL; | ||
531 | |||
532 | switch (pCacheAction->type) | ||
533 | { | ||
534 | case BURN_CACHE_ACTION_TYPE_CHECKPOINT: | ||
535 | dwCheckpoint = pCacheAction->checkpoint.dwId; | ||
536 | break; | ||
537 | |||
538 | case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: | ||
539 | hr = ApplyLayoutBundle(&cacheContext, pCacheAction->bundleLayout.pPayloadGroup, pCacheAction->bundleLayout.sczExecutableName, pCacheAction->bundleLayout.sczUnverifiedPath, pCacheAction->bundleLayout.qwBundleSize); | ||
540 | ExitOnFailure(hr, "Failed cache action: %ls", L"layout bundle"); | ||
541 | |||
542 | ++(*pcOverallProgressTicks); | ||
543 | |||
544 | hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks); | ||
545 | LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls", L"layout bundle"); | ||
546 | |||
547 | break; | ||
548 | |||
549 | case BURN_CACHE_ACTION_TYPE_PACKAGE: | ||
550 | pPackage = pCacheAction->package.pPackage; | ||
551 | |||
552 | if (!pPackage->fPerMachine && !cacheContext.wzLayoutDirectory) | ||
553 | { | ||
554 | hr = CacheGetCompletedPath(FALSE, pPackage->sczCacheId, &pPackage->sczCacheFolder); | ||
555 | ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", pPackage->sczCacheId); | ||
556 | |||
557 | cacheContext.hPipe = INVALID_HANDLE_VALUE; | ||
558 | } | ||
559 | |||
560 | hr = ApplyCachePackage(&cacheContext, pPackage); | ||
561 | ExitOnFailure(hr, "Failed cache action: %ls", L"cache package"); | ||
562 | |||
563 | ++(*pcOverallProgressTicks); | ||
564 | |||
565 | hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks); | ||
566 | LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls", L"cache package"); | ||
567 | |||
568 | break; | ||
569 | |||
570 | case BURN_CACHE_ACTION_TYPE_CONTAINER: | ||
571 | Assert(pPlan->sczLayoutDirectory); | ||
572 | hr = ApplyLayoutContainer(&cacheContext, pCacheAction->container.pContainer); | ||
573 | ExitOnFailure(hr, "Failed cache action: %ls", L"layout container"); | ||
574 | |||
575 | break; | ||
576 | |||
577 | case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: | ||
578 | if (!::SetEvent(pCacheAction->syncpoint.hEvent)) | ||
579 | { | ||
580 | ExitWithLastError(hr, "Failed to set syncpoint event."); | ||
581 | } | ||
582 | break; | ||
583 | |||
584 | default: | ||
585 | AssertSz(FALSE, "Unknown cache action."); | ||
586 | break; | ||
587 | } | ||
588 | } | ||
589 | |||
590 | LExit: | ||
591 | if (FAILED(hr)) | ||
592 | { | ||
593 | DoRollbackCache(pUX, pPlan, hPipe, dwCheckpoint); | ||
594 | *pfRollback = TRUE; | ||
595 | } | ||
596 | |||
597 | // Clean up any remanents in the cache. | ||
598 | if (INVALID_HANDLE_VALUE != hPipe) | ||
599 | { | ||
600 | ElevationCacheCleanup(hPipe); | ||
601 | } | ||
602 | |||
603 | CacheCleanup(FALSE, pPlan->wzBundleId); | ||
604 | |||
605 | for (DWORD i = 0; i < cacheContext.cSearchPathsMax; ++i) | ||
606 | { | ||
607 | ReleaseNullStr(cacheContext.rgSearchPaths[i]); | ||
608 | } | ||
609 | ReleaseMem(cacheContext.rgSearchPaths); | ||
610 | ReleaseStr(cacheContext.sczLastUsedFolderCandidate); | ||
611 | |||
612 | UserExperienceOnCacheComplete(pUX, hr); | ||
613 | return hr; | ||
614 | } | ||
615 | |||
616 | extern "C" HRESULT ApplyExecute( | ||
617 | __in BURN_ENGINE_STATE* pEngineState, | ||
618 | __in_opt HANDLE hCacheThread, | ||
619 | __inout DWORD* pcOverallProgressTicks, | ||
620 | __out BOOL* pfRollback, | ||
621 | __out BOOL* pfSuspend, | ||
622 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
623 | ) | ||
624 | { | ||
625 | HRESULT hr = S_OK; | ||
626 | HRESULT hrRollback = S_OK; | ||
627 | BURN_EXECUTE_ACTION_CHECKPOINT* pCheckpoint = NULL; | ||
628 | BURN_EXECUTE_CONTEXT context = { }; | ||
629 | BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; | ||
630 | BOOL fSeekNextRollbackBoundary = FALSE; | ||
631 | |||
632 | context.pUX = &pEngineState->userExperience; | ||
633 | context.cExecutePackagesTotal = pEngineState->plan.cExecutePackagesTotal; | ||
634 | context.pcOverallProgressTicks = pcOverallProgressTicks; | ||
635 | |||
636 | *pfRollback = FALSE; | ||
637 | *pfSuspend = FALSE; | ||
638 | |||
639 | // Send execute begin to BA. | ||
640 | hr = UserExperienceOnExecuteBegin(&pEngineState->userExperience, pEngineState->plan.cExecutePackagesTotal); | ||
641 | ExitOnRootFailure(hr, "BA aborted execute begin."); | ||
642 | |||
643 | // Do execute actions. | ||
644 | for (DWORD i = 0; i < pEngineState->plan.cExecuteActions; ++i) | ||
645 | { | ||
646 | BURN_EXECUTE_ACTION* pExecuteAction = &pEngineState->plan.rgExecuteActions[i]; | ||
647 | if (pExecuteAction->fDeleted) | ||
648 | { | ||
649 | continue; | ||
650 | } | ||
651 | |||
652 | // If we are seeking the next rollback boundary, skip if this action wasn't it. | ||
653 | if (fSeekNextRollbackBoundary) | ||
654 | { | ||
655 | if (BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY == pExecuteAction->type) | ||
656 | { | ||
657 | continue; | ||
658 | } | ||
659 | else | ||
660 | { | ||
661 | fSeekNextRollbackBoundary = FALSE; | ||
662 | } | ||
663 | } | ||
664 | |||
665 | // Execute the action. | ||
666 | hr = DoExecuteAction(pEngineState, pExecuteAction, hCacheThread, &context, &pRollbackBoundary, &pCheckpoint, pfSuspend, pRestart); | ||
667 | |||
668 | if (*pfSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart) | ||
669 | { | ||
670 | if (pCheckpoint && pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction) | ||
671 | { | ||
672 | hr = E_INVALIDSTATE; | ||
673 | LogId(REPORT_ERROR, MSG_RESTART_REQUEST_DURING_MSI_TRANSACTION, pCheckpoint->pActiveRollbackBoundary->sczId); | ||
674 | } | ||
675 | else | ||
676 | { | ||
677 | ExitFunction(); | ||
678 | } | ||
679 | } | ||
680 | |||
681 | if (FAILED(hr)) | ||
682 | { | ||
683 | // If rollback is disabled, keep what we have and always end execution here. | ||
684 | if (pEngineState->plan.fDisableRollback) | ||
685 | { | ||
686 | LogId(REPORT_WARNING, MSG_PLAN_ROLLBACK_DISABLED); | ||
687 | |||
688 | if (pCheckpoint && pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction) | ||
689 | { | ||
690 | hrRollback = ExecuteMsiCommitTransaction(pEngineState, pCheckpoint->pActiveRollbackBoundary, &context); | ||
691 | IgnoreRollbackError(hrRollback, "Failed commit transaction from disable rollback"); | ||
692 | } | ||
693 | |||
694 | *pfRollback = TRUE; | ||
695 | break; | ||
696 | } | ||
697 | |||
698 | if (pCheckpoint) | ||
699 | { | ||
700 | // If inside a MSI transaction, roll it back. | ||
701 | if (pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction) | ||
702 | { | ||
703 | hrRollback = ExecuteMsiRollbackTransaction(pEngineState, pCheckpoint->pActiveRollbackBoundary, &context); | ||
704 | IgnoreRollbackError(hrRollback, "Failed rolling back transaction"); | ||
705 | } | ||
706 | |||
707 | // The action failed, roll back to previous rollback boundary. | ||
708 | hrRollback = DoRollbackActions(pEngineState, &context, pCheckpoint->dwId, pRestart); | ||
709 | IgnoreRollbackError(hrRollback, "Failed rollback actions"); | ||
710 | } | ||
711 | |||
712 | // If the rollback boundary is vital, end execution here. | ||
713 | if (pRollbackBoundary && pRollbackBoundary->fVital) | ||
714 | { | ||
715 | *pfRollback = TRUE; | ||
716 | break; | ||
717 | } | ||
718 | |||
719 | // Move forward to next rollback boundary. | ||
720 | fSeekNextRollbackBoundary = TRUE; | ||
721 | } | ||
722 | } | ||
723 | |||
724 | LExit: | ||
725 | // Send execute complete to BA. | ||
726 | UserExperienceOnExecuteComplete(&pEngineState->userExperience, hr); | ||
727 | |||
728 | return hr; | ||
729 | } | ||
730 | |||
731 | extern "C" void ApplyClean( | ||
732 | __in BURN_USER_EXPERIENCE* /*pUX*/, | ||
733 | __in BURN_PLAN* pPlan, | ||
734 | __in HANDLE hPipe | ||
735 | ) | ||
736 | { | ||
737 | HRESULT hr = S_OK; | ||
738 | |||
739 | for (DWORD i = 0; i < pPlan->cCleanActions; ++i) | ||
740 | { | ||
741 | BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + i; | ||
742 | BURN_PACKAGE* pPackage = pCleanAction->pPackage; | ||
743 | |||
744 | hr = CleanPackage(hPipe, pPackage); | ||
745 | } | ||
746 | } | ||
747 | |||
748 | |||
749 | // internal helper functions | ||
750 | |||
751 | static void CalculateKeepRegistration( | ||
752 | __in BURN_ENGINE_STATE* pEngineState, | ||
753 | __inout BOOL* pfKeepRegistration | ||
754 | ) | ||
755 | { | ||
756 | LogId(REPORT_STANDARD, MSG_POST_APPLY_CALCULATE_REGISTRATION); | ||
757 | |||
758 | for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) | ||
759 | { | ||
760 | BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; | ||
761 | |||
762 | if (BURN_PACKAGE_TYPE_MSP == pPackage->type) | ||
763 | { | ||
764 | MspEngineFinalizeInstallRegistrationState(pPackage); | ||
765 | } | ||
766 | |||
767 | LogId(REPORT_STANDARD, MSG_POST_APPLY_PACKAGE, pPackage->sczId, LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState)); | ||
768 | |||
769 | if (!pPackage->fCanAffectRegistration) | ||
770 | { | ||
771 | continue; | ||
772 | } | ||
773 | |||
774 | if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState || | ||
775 | BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState) | ||
776 | { | ||
777 | *pfKeepRegistration = TRUE; | ||
778 | } | ||
779 | } | ||
780 | } | ||
781 | |||
782 | static HRESULT ExecuteDependentRegistrationActions( | ||
783 | __in HANDLE hPipe, | ||
784 | __in const BURN_REGISTRATION* pRegistration, | ||
785 | __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions, | ||
786 | __in DWORD cActions | ||
787 | ) | ||
788 | { | ||
789 | HRESULT hr = S_OK; | ||
790 | |||
791 | for (DWORD iAction = 0; iAction < cActions; ++iAction) | ||
792 | { | ||
793 | const BURN_DEPENDENT_REGISTRATION_ACTION* pAction = rgActions + iAction; | ||
794 | |||
795 | if (pRegistration->fPerMachine) | ||
796 | { | ||
797 | hr = ElevationProcessDependentRegistration(hPipe, pAction); | ||
798 | ExitOnFailure(hr, "Failed to execute dependent registration action."); | ||
799 | } | ||
800 | else | ||
801 | { | ||
802 | hr = DependencyProcessDependentRegistration(pRegistration, pAction); | ||
803 | ExitOnFailure(hr, "Failed to process dependency registration action."); | ||
804 | } | ||
805 | } | ||
806 | |||
807 | LExit: | ||
808 | return hr; | ||
809 | } | ||
810 | |||
811 | static HRESULT ApplyCachePackage( | ||
812 | __in BURN_CACHE_CONTEXT* pContext, | ||
813 | __in BURN_PACKAGE* pPackage | ||
814 | ) | ||
815 | { | ||
816 | HRESULT hr = S_OK; | ||
817 | BOOL fCanceledBegin = FALSE; | ||
818 | BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE; | ||
819 | |||
820 | for (;;) | ||
821 | { | ||
822 | fCanceledBegin = FALSE; | ||
823 | |||
824 | hr = UserExperienceOnCachePackageBegin(pContext->pUX, pPackage->sczId, pPackage->payloads.cItems, pPackage->payloads.qwTotalSize); | ||
825 | if (FAILED(hr)) | ||
826 | { | ||
827 | fCanceledBegin = TRUE; | ||
828 | } | ||
829 | else | ||
830 | { | ||
831 | for (DWORD i = 0; i < pPackage->payloads.cItems; ++i) | ||
832 | { | ||
833 | BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem = pPackage->payloads.rgItems + i; | ||
834 | |||
835 | hr = ApplyProcessPayload(pContext, pPackage, pPayloadGroupItem); | ||
836 | if (FAILED(hr)) | ||
837 | { | ||
838 | break; | ||
839 | } | ||
840 | } | ||
841 | } | ||
842 | |||
843 | pPackage->hrCacheResult = hr; | ||
844 | cachePackageCompleteAction = SUCCEEDED(hr) || pPackage->fVital || fCanceledBegin ? BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE; | ||
845 | UserExperienceOnCachePackageComplete(pContext->pUX, pPackage->sczId, hr, &cachePackageCompleteAction); | ||
846 | |||
847 | if (SUCCEEDED(hr)) | ||
848 | { | ||
849 | break; | ||
850 | } | ||
851 | |||
852 | if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_RETRY == cachePackageCompleteAction) | ||
853 | { | ||
854 | for (DWORD i = 0; i < pPackage->payloads.cItems; ++i) | ||
855 | { | ||
856 | BURN_PAYLOAD_GROUP_ITEM* pItem = pPackage->payloads.rgItems + i; | ||
857 | if (pItem->fCached) | ||
858 | { | ||
859 | pItem->pPayload->cRemainingInstances += 1; | ||
860 | pItem->fCached = FALSE; | ||
861 | } | ||
862 | |||
863 | if (pItem->qwCommittedCacheProgress) | ||
864 | { | ||
865 | pContext->qwSuccessfulCacheProgress -= pItem->qwCommittedCacheProgress; | ||
866 | pItem->qwCommittedCacheProgress = 0; | ||
867 | } | ||
868 | } | ||
869 | |||
870 | LogErrorId(hr, MSG_CACHE_RETRYING_PACKAGE, pPackage->sczId, NULL, NULL); | ||
871 | |||
872 | continue; | ||
873 | } | ||
874 | else if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE == cachePackageCompleteAction && !pPackage->fVital) // ignore non-vital download failures. | ||
875 | { | ||
876 | LogId(REPORT_STANDARD, MSG_CACHE_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hr); | ||
877 | hr = S_OK; | ||
878 | } | ||
879 | else if (fCanceledBegin) | ||
880 | { | ||
881 | LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls: %ls", L"begin cache package", pPackage->sczId); | ||
882 | } | ||
883 | |||
884 | break; | ||
885 | } | ||
886 | |||
887 | LExit: | ||
888 | return hr; | ||
889 | } | ||
890 | |||
891 | static HRESULT ApplyExtractContainer( | ||
892 | __in BURN_CACHE_CONTEXT* pContext, | ||
893 | __in BURN_CONTAINER* pContainer | ||
894 | ) | ||
895 | { | ||
896 | HRESULT hr = S_OK; | ||
897 | |||
898 | if (pContainer->qwCommittedCacheProgress) | ||
899 | { | ||
900 | pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedCacheProgress; | ||
901 | pContainer->qwCommittedCacheProgress = 0; | ||
902 | } | ||
903 | |||
904 | if (pContainer->qwCommittedExtractProgress) | ||
905 | { | ||
906 | pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedExtractProgress; | ||
907 | pContainer->qwCommittedExtractProgress = 0; | ||
908 | } | ||
909 | |||
910 | if (!pContainer->fActuallyAttached) | ||
911 | { | ||
912 | hr = ApplyAcquireContainerOrPayload(pContext, pContainer, NULL, NULL); | ||
913 | LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_CONTAINER, "Failed to acquire container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); | ||
914 | } | ||
915 | |||
916 | hr = ExtractContainer(pContext, pContainer); | ||
917 | LogExitOnFailure(hr, MSG_FAILED_EXTRACT_CONTAINER, "Failed to extract payloads from container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); | ||
918 | |||
919 | if (pContext->sczLastUsedFolderCandidate) | ||
920 | { | ||
921 | // We successfully copied from a source location, set that as the last used source. | ||
922 | CacheSetLastUsedSource(pContext->pVariables, pContext->sczLastUsedFolderCandidate, pContainer->sczFilePath); | ||
923 | } | ||
924 | |||
925 | if (pContainer->qwExtractSizeTotal < pContainer->qwCommittedExtractProgress) | ||
926 | { | ||
927 | AssertSz(FALSE, "Container extracted more than planned."); | ||
928 | pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedExtractProgress; | ||
929 | pContext->qwSuccessfulCacheProgress += pContainer->qwExtractSizeTotal; | ||
930 | } | ||
931 | else | ||
932 | { | ||
933 | pContext->qwSuccessfulCacheProgress += pContainer->qwExtractSizeTotal - pContainer->qwCommittedExtractProgress; | ||
934 | } | ||
935 | |||
936 | pContainer->qwCommittedExtractProgress = pContainer->qwExtractSizeTotal; | ||
937 | |||
938 | LExit: | ||
939 | ReleaseNullStr(pContext->sczLastUsedFolderCandidate); | ||
940 | |||
941 | return hr; | ||
942 | } | ||
943 | |||
944 | static HRESULT ApplyLayoutBundle( | ||
945 | __in BURN_CACHE_CONTEXT* pContext, | ||
946 | __in BURN_PAYLOAD_GROUP* pPayloads, | ||
947 | __in_z LPCWSTR wzExecutableName, | ||
948 | __in_z LPCWSTR wzUnverifiedPath, | ||
949 | __in DWORD64 qwBundleSize | ||
950 | ) | ||
951 | { | ||
952 | HRESULT hr = S_OK; | ||
953 | |||
954 | hr = LayoutBundle(pContext, wzExecutableName, wzUnverifiedPath, qwBundleSize); | ||
955 | ExitOnFailure(hr, "Failed to layout bundle."); | ||
956 | |||
957 | for (DWORD i = 0; i < pPayloads->cItems; ++i) | ||
958 | { | ||
959 | BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem = pPayloads->rgItems + i; | ||
960 | |||
961 | hr = ApplyProcessPayload(pContext, NULL, pPayloadGroupItem); | ||
962 | ExitOnFailure(hr, "Failed to layout bundle payload: %ls", pPayloadGroupItem->pPayload->sczKey); | ||
963 | } | ||
964 | |||
965 | LExit: | ||
966 | return hr; | ||
967 | } | ||
968 | |||
969 | static HRESULT ApplyLayoutContainer( | ||
970 | __in BURN_CACHE_CONTEXT* pContext, | ||
971 | __in BURN_CONTAINER* pContainer | ||
972 | ) | ||
973 | { | ||
974 | HRESULT hr = S_OK; | ||
975 | DWORD cTryAgainAttempts = 0; | ||
976 | BOOL fRetry = FALSE; | ||
977 | |||
978 | Assert(!pContainer->fAttached); | ||
979 | |||
980 | hr = ApplyCacheVerifyContainerOrPayload(pContext, pContainer, NULL, NULL); | ||
981 | if (SUCCEEDED(hr)) | ||
982 | { | ||
983 | ExitFunction(); | ||
984 | } | ||
985 | |||
986 | for (;;) | ||
987 | { | ||
988 | fRetry = FALSE; | ||
989 | |||
990 | hr = ApplyAcquireContainerOrPayload(pContext, pContainer, NULL, NULL); | ||
991 | LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_CONTAINER, "Failed to acquire container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); | ||
992 | |||
993 | hr = LayoutOrCacheContainerOrPayload(pContext, pContainer, NULL, NULL, cTryAgainAttempts, &fRetry); | ||
994 | if (SUCCEEDED(hr)) | ||
995 | { | ||
996 | break; | ||
997 | } | ||
998 | else | ||
999 | { | ||
1000 | LogErrorId(hr, MSG_FAILED_LAYOUT_CONTAINER, pContainer->sczId, pContext->wzLayoutDirectory, pContainer->sczUnverifiedPath); | ||
1001 | |||
1002 | if (!fRetry) | ||
1003 | { | ||
1004 | ExitFunction(); | ||
1005 | } | ||
1006 | |||
1007 | ++cTryAgainAttempts; | ||
1008 | pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedCacheProgress; | ||
1009 | pContainer->qwCommittedCacheProgress = 0; | ||
1010 | ReleaseNullStr(pContext->sczLastUsedFolderCandidate); | ||
1011 | LogErrorId(hr, MSG_CACHE_RETRYING_CONTAINER, pContainer->sczId, NULL, NULL); | ||
1012 | } | ||
1013 | } | ||
1014 | |||
1015 | LExit: | ||
1016 | ReleaseNullStr(pContext->sczLastUsedFolderCandidate); | ||
1017 | |||
1018 | return hr; | ||
1019 | } | ||
1020 | |||
1021 | static HRESULT ApplyProcessPayload( | ||
1022 | __in BURN_CACHE_CONTEXT* pContext, | ||
1023 | __in_opt BURN_PACKAGE* pPackage, | ||
1024 | __in BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem | ||
1025 | ) | ||
1026 | { | ||
1027 | HRESULT hr = S_OK; | ||
1028 | DWORD cTryAgainAttempts = 0; | ||
1029 | BOOL fRetry = FALSE; | ||
1030 | BURN_PAYLOAD* pPayload = pPayloadGroupItem->pPayload; | ||
1031 | |||
1032 | Assert(pContext->pPayloads && pPackage || pContext->wzLayoutDirectory); | ||
1033 | |||
1034 | if (pPayload->pContainer && pContext->wzLayoutDirectory) | ||
1035 | { | ||
1036 | ExitFunction(); | ||
1037 | } | ||
1038 | |||
1039 | hr = ApplyCacheVerifyContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem); | ||
1040 | if (SUCCEEDED(hr)) | ||
1041 | { | ||
1042 | ExitFunction(); | ||
1043 | } | ||
1044 | |||
1045 | for (;;) | ||
1046 | { | ||
1047 | fRetry = FALSE; | ||
1048 | |||
1049 | hr = ApplyAcquireContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem); | ||
1050 | LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_PAYLOAD, "Failed to acquire payload: %ls to working path: %ls", pPayload->sczKey, pPayload->sczUnverifiedPath); | ||
1051 | |||
1052 | hr = LayoutOrCacheContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem, cTryAgainAttempts, &fRetry); | ||
1053 | if (SUCCEEDED(hr)) | ||
1054 | { | ||
1055 | break; | ||
1056 | } | ||
1057 | else | ||
1058 | { | ||
1059 | LogErrorId(hr, pContext->wzLayoutDirectory ? MSG_FAILED_LAYOUT_PAYLOAD : MSG_FAILED_CACHE_PAYLOAD, pPayload->sczKey, pContext->wzLayoutDirectory, pPayload->sczUnverifiedPath); | ||
1060 | |||
1061 | if (!fRetry) | ||
1062 | { | ||
1063 | ExitFunction(); | ||
1064 | } | ||
1065 | |||
1066 | ++cTryAgainAttempts; | ||
1067 | pContext->qwSuccessfulCacheProgress -= pPayloadGroupItem->qwCommittedCacheProgress; | ||
1068 | pPayloadGroupItem->qwCommittedCacheProgress = 0; | ||
1069 | ReleaseNullStr(pContext->sczLastUsedFolderCandidate); | ||
1070 | LogErrorId(hr, MSG_CACHE_RETRYING_PAYLOAD, pPayload->sczKey, NULL, NULL); | ||
1071 | } | ||
1072 | } | ||
1073 | |||
1074 | LExit: | ||
1075 | ReleaseNullStr(pContext->sczLastUsedFolderCandidate); | ||
1076 | |||
1077 | return hr; | ||
1078 | } | ||
1079 | |||
1080 | static HRESULT ApplyCacheVerifyContainerOrPayload( | ||
1081 | __in BURN_CACHE_CONTEXT* pContext, | ||
1082 | __in_opt BURN_CONTAINER* pContainer, | ||
1083 | __in_opt BURN_PACKAGE* pPackage, | ||
1084 | __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem | ||
1085 | ) | ||
1086 | { | ||
1087 | AssertSz(pContainer || pPayloadGroupItem, "Must provide a container or a payload."); | ||
1088 | |||
1089 | HRESULT hr = S_OK; | ||
1090 | BURN_CACHE_PROGRESS_CONTEXT progress = { }; | ||
1091 | |||
1092 | progress.pCacheContext = pContext; | ||
1093 | progress.pContainer = pContainer; | ||
1094 | progress.pPackage = pPackage; | ||
1095 | progress.pPayloadGroupItem = pPayloadGroupItem; | ||
1096 | |||
1097 | if (pContainer) | ||
1098 | { | ||
1099 | hr = CacheVerifyContainer(pContainer, pContext->wzLayoutDirectory, CacheMessageHandler, CacheProgressRoutine, &progress); | ||
1100 | } | ||
1101 | else if (!pContext->wzLayoutDirectory && INVALID_HANDLE_VALUE != pContext->hPipe) | ||
1102 | { | ||
1103 | hr = ElevationCacheVerifyPayload(pContext->hPipe, pPackage, pPayloadGroupItem->pPayload, CacheMessageHandler, CacheProgressRoutine, &progress); | ||
1104 | } | ||
1105 | else | ||
1106 | { | ||
1107 | hr = CacheVerifyPayload(pPayloadGroupItem->pPayload, pContext->wzLayoutDirectory ? pContext->wzLayoutDirectory : pPackage->sczCacheFolder, CacheMessageHandler, CacheProgressRoutine, &progress); | ||
1108 | } | ||
1109 | |||
1110 | return hr; | ||
1111 | } | ||
1112 | |||
1113 | static HRESULT ExtractContainer( | ||
1114 | __in BURN_CACHE_CONTEXT* pContext, | ||
1115 | __in BURN_CONTAINER* pContainer | ||
1116 | ) | ||
1117 | { | ||
1118 | HRESULT hr = S_OK; | ||
1119 | BURN_CONTAINER_CONTEXT context = { }; | ||
1120 | HANDLE hContainerHandle = INVALID_HANDLE_VALUE; | ||
1121 | LPWSTR sczStreamName = NULL; | ||
1122 | BURN_PAYLOAD* pExtract = NULL; | ||
1123 | BURN_CACHE_PROGRESS_CONTEXT progress = { }; | ||
1124 | |||
1125 | progress.pCacheContext = pContext; | ||
1126 | progress.pContainer = pContainer; | ||
1127 | progress.type = BURN_CACHE_PROGRESS_TYPE_EXTRACT; | ||
1128 | |||
1129 | // If the container is actually attached, then it was planned to be acquired through hSourceEngineFile. | ||
1130 | if (pContainer->fActuallyAttached) | ||
1131 | { | ||
1132 | hContainerHandle = pContext->hSourceEngineFile; | ||
1133 | } | ||
1134 | |||
1135 | hr = ContainerOpen(&context, pContainer, hContainerHandle, pContainer->sczUnverifiedPath); | ||
1136 | ExitOnFailure(hr, "Failed to open container: %ls.", pContainer->sczId); | ||
1137 | |||
1138 | while (S_OK == (hr = ContainerNextStream(&context, &sczStreamName))) | ||
1139 | { | ||
1140 | BOOL fExtracted = FALSE; | ||
1141 | |||
1142 | hr = PayloadFindEmbeddedBySourcePath(pContext->pPayloads, sczStreamName, &pExtract); | ||
1143 | if (E_NOTFOUND != hr) | ||
1144 | { | ||
1145 | ExitOnFailure(hr, "Failed to find embedded payload by source path: %ls container: %ls", sczStreamName, pContainer->sczId); | ||
1146 | |||
1147 | // Skip payloads that weren't planned or have already been cached. | ||
1148 | if (pExtract->sczUnverifiedPath && pExtract->cRemainingInstances) | ||
1149 | { | ||
1150 | progress.pPayload = pExtract; | ||
1151 | |||
1152 | hr = PreparePayloadDestinationPath(pExtract->sczUnverifiedPath); | ||
1153 | ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", pExtract->sczUnverifiedPath); | ||
1154 | |||
1155 | hr = UserExperienceOnCachePayloadExtractBegin(pContext->pUX, pContainer->sczId, pExtract->sczKey); | ||
1156 | if (FAILED(hr)) | ||
1157 | { | ||
1158 | UserExperienceOnCachePayloadExtractComplete(pContext->pUX, pContainer->sczId, pExtract->sczKey, hr); | ||
1159 | ExitOnRootFailure(hr, "BA aborted cache payload extract begin."); | ||
1160 | } | ||
1161 | |||
1162 | // TODO: Send progress when extracting stream to file. | ||
1163 | hr = ContainerStreamToFile(&context, pExtract->sczUnverifiedPath); | ||
1164 | // Error handling happens after sending complete message to BA. | ||
1165 | |||
1166 | // If succeeded, send 100% complete here to make sure progress was sent to the BA. | ||
1167 | if (SUCCEEDED(hr)) | ||
1168 | { | ||
1169 | hr = CompleteCacheProgress(&progress, pExtract->qwFileSize); | ||
1170 | } | ||
1171 | |||
1172 | UserExperienceOnCachePayloadExtractComplete(pContext->pUX, pContainer->sczId, pExtract->sczKey, hr); | ||
1173 | ExitOnFailure(hr, "Failed to extract payload: %ls from container: %ls", sczStreamName, pContainer->sczId); | ||
1174 | |||
1175 | fExtracted = TRUE; | ||
1176 | } | ||
1177 | } | ||
1178 | |||
1179 | if (!fExtracted) | ||
1180 | { | ||
1181 | hr = ContainerSkipStream(&context); | ||
1182 | ExitOnFailure(hr, "Failed to skip the extraction of payload: %ls from container: %ls", sczStreamName, pContainer->sczId); | ||
1183 | } | ||
1184 | } | ||
1185 | |||
1186 | if (E_NOMOREITEMS == hr) | ||
1187 | { | ||
1188 | hr = S_OK; | ||
1189 | } | ||
1190 | ExitOnFailure(hr, "Failed to extract all payloads from container: %ls", pContainer->sczId); | ||
1191 | |||
1192 | LExit: | ||
1193 | ReleaseStr(sczStreamName); | ||
1194 | ContainerClose(&context); | ||
1195 | |||
1196 | return hr; | ||
1197 | } | ||
1198 | |||
1199 | static HRESULT LayoutBundle( | ||
1200 | __in BURN_CACHE_CONTEXT* pContext, | ||
1201 | __in_z LPCWSTR wzExecutableName, | ||
1202 | __in_z LPCWSTR wzUnverifiedPath, | ||
1203 | __in DWORD64 qwBundleSize | ||
1204 | ) | ||
1205 | { | ||
1206 | HRESULT hr = S_OK; | ||
1207 | LPWSTR sczBundlePath = NULL; | ||
1208 | LPWSTR sczBundleDownloadUrl = NULL; | ||
1209 | LPWSTR sczDestinationPath = NULL; | ||
1210 | int nEquivalentPaths = 0; | ||
1211 | BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; | ||
1212 | BURN_CACHE_PROGRESS_CONTEXT progress = { }; | ||
1213 | BOOL fRetry = FALSE; | ||
1214 | BOOL fRetryAcquire = FALSE; | ||
1215 | BOOL fCanceledBegin = FALSE; | ||
1216 | |||
1217 | progress.pCacheContext = pContext; | ||
1218 | |||
1219 | hr = VariableGetString(pContext->pVariables, BURN_BUNDLE_SOURCE_PROCESS_PATH, &sczBundlePath); | ||
1220 | if (FAILED(hr)) | ||
1221 | { | ||
1222 | if (E_NOTFOUND != hr) | ||
1223 | { | ||
1224 | ExitOnFailure(hr, "Failed to get path to bundle source process path to layout."); | ||
1225 | } | ||
1226 | |||
1227 | hr = PathForCurrentProcess(&sczBundlePath, NULL); | ||
1228 | ExitOnFailure(hr, "Failed to get path to bundle to layout."); | ||
1229 | } | ||
1230 | |||
1231 | hr = PathConcat(pContext->wzLayoutDirectory, wzExecutableName, &sczDestinationPath); | ||
1232 | ExitOnFailure(hr, "Failed to concat layout path for bundle."); | ||
1233 | |||
1234 | // If the destination path is the currently running bundle, bail. | ||
1235 | hr = PathCompare(sczBundlePath, sczDestinationPath, &nEquivalentPaths); | ||
1236 | ExitOnFailure(hr, "Failed to determine if layout bundle path was equivalent with current process path."); | ||
1237 | |||
1238 | if (CSTR_EQUAL == nEquivalentPaths && FileExistsEx(sczDestinationPath, NULL)) | ||
1239 | { | ||
1240 | hr = UserExperienceOnCacheContainerOrPayloadVerifyBegin(pContext->pUX, NULL, NULL); | ||
1241 | if (FAILED(hr)) | ||
1242 | { | ||
1243 | UserExperienceOnCacheContainerOrPayloadVerifyComplete(pContext->pUX, NULL, NULL, hr); | ||
1244 | ExitOnRootFailure(hr, "BA aborted cache payload verify begin."); | ||
1245 | } | ||
1246 | |||
1247 | progress.type = BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY; | ||
1248 | hr = CompleteCacheProgress(&progress, qwBundleSize); | ||
1249 | |||
1250 | UserExperienceOnCacheContainerOrPayloadVerifyComplete(pContext->pUX, NULL, NULL, hr); | ||
1251 | |||
1252 | ExitFunction(); | ||
1253 | } | ||
1254 | |||
1255 | do | ||
1256 | { | ||
1257 | hr = S_OK; | ||
1258 | fRetry = FALSE; | ||
1259 | progress.type = BURN_CACHE_PROGRESS_TYPE_ACQUIRE; | ||
1260 | |||
1261 | for (;;) | ||
1262 | { | ||
1263 | fRetryAcquire = FALSE; | ||
1264 | progress.fCancel = FALSE; | ||
1265 | fCanceledBegin = FALSE; | ||
1266 | |||
1267 | hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, NULL, NULL, &sczBundlePath, &sczBundleDownloadUrl, NULL, &cacheOperation); | ||
1268 | |||
1269 | if (FAILED(hr)) | ||
1270 | { | ||
1271 | fCanceledBegin = TRUE; | ||
1272 | } | ||
1273 | else | ||
1274 | { | ||
1275 | hr = CopyPayload(&progress, pContext->hSourceEngineFile, sczBundlePath, wzUnverifiedPath); | ||
1276 | // Error handling happens after sending complete message to BA. | ||
1277 | |||
1278 | // If succeeded, send 100% complete here to make sure progress was sent to the BA. | ||
1279 | if (SUCCEEDED(hr)) | ||
1280 | { | ||
1281 | hr = CompleteCacheProgress(&progress, qwBundleSize); | ||
1282 | } | ||
1283 | } | ||
1284 | |||
1285 | UserExperienceOnCacheAcquireComplete(pContext->pUX, NULL, NULL, hr, &fRetryAcquire); | ||
1286 | if (fRetryAcquire) | ||
1287 | { | ||
1288 | continue; | ||
1289 | } | ||
1290 | else if (fCanceledBegin) | ||
1291 | { | ||
1292 | ExitOnRootFailure(hr, "BA aborted cache acquire begin."); | ||
1293 | } | ||
1294 | |||
1295 | ExitOnFailure(hr, "Failed to copy bundle from: '%ls' to: '%ls'", sczBundlePath, wzUnverifiedPath); | ||
1296 | break; | ||
1297 | } | ||
1298 | |||
1299 | do | ||
1300 | { | ||
1301 | fCanceledBegin = FALSE; | ||
1302 | |||
1303 | hr = UserExperienceOnCacheVerifyBegin(pContext->pUX, NULL, NULL); | ||
1304 | |||
1305 | if (FAILED(hr)) | ||
1306 | { | ||
1307 | fCanceledBegin = TRUE; | ||
1308 | } | ||
1309 | else | ||
1310 | { | ||
1311 | hr = CacheLayoutBundle(wzExecutableName, pContext->wzLayoutDirectory, wzUnverifiedPath, qwBundleSize, CacheMessageHandler, CacheProgressRoutine, &progress); | ||
1312 | } | ||
1313 | |||
1314 | BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; | ||
1315 | UserExperienceOnCacheVerifyComplete(pContext->pUX, NULL, NULL, hr, &action); | ||
1316 | if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action) | ||
1317 | { | ||
1318 | hr = S_FALSE; // retry verify. | ||
1319 | } | ||
1320 | else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action) | ||
1321 | { | ||
1322 | fRetry = TRUE; // go back and retry acquire. | ||
1323 | } | ||
1324 | else if (fCanceledBegin) | ||
1325 | { | ||
1326 | ExitOnRootFailure(hr, "BA aborted cache verify begin."); | ||
1327 | } | ||
1328 | } while (S_FALSE == hr); | ||
1329 | |||
1330 | if (fRetry) | ||
1331 | { | ||
1332 | pContext->qwSuccessfulCacheProgress -= qwBundleSize; // Acquire | ||
1333 | } | ||
1334 | } while (fRetry); | ||
1335 | LogExitOnFailure(hr, MSG_FAILED_LAYOUT_BUNDLE, "Failed to layout bundle: %ls to layout directory: %ls", sczBundlePath, pContext->wzLayoutDirectory); | ||
1336 | |||
1337 | LExit: | ||
1338 | ReleaseStr(sczDestinationPath); | ||
1339 | ReleaseStr(sczBundleDownloadUrl); | ||
1340 | ReleaseStr(sczBundlePath); | ||
1341 | |||
1342 | return hr; | ||
1343 | } | ||
1344 | |||
1345 | static HRESULT ApplyAcquireContainerOrPayload( | ||
1346 | __in BURN_CACHE_CONTEXT* pContext, | ||
1347 | __in_opt BURN_CONTAINER* pContainer, | ||
1348 | __in_opt BURN_PACKAGE* pPackage, | ||
1349 | __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem | ||
1350 | ) | ||
1351 | { | ||
1352 | AssertSz(pContainer || pPayloadGroupItem, "Must provide a container or a payload."); | ||
1353 | |||
1354 | HRESULT hr = S_OK; | ||
1355 | BURN_CACHE_PROGRESS_CONTEXT progress = { }; | ||
1356 | BOOL fRetry = FALSE; | ||
1357 | |||
1358 | progress.pCacheContext = pContext; | ||
1359 | progress.type = BURN_CACHE_PROGRESS_TYPE_ACQUIRE; | ||
1360 | progress.pContainer = pContainer; | ||
1361 | progress.pPackage = pPackage; | ||
1362 | progress.pPayloadGroupItem = pPayloadGroupItem; | ||
1363 | |||
1364 | do | ||
1365 | { | ||
1366 | hr = AcquireContainerOrPayload(&progress, &fRetry); | ||
1367 | |||
1368 | if (fRetry) | ||
1369 | { | ||
1370 | LogErrorId(hr, pContainer ? MSG_APPLY_RETRYING_ACQUIRE_CONTAINER : MSG_APPLY_RETRYING_ACQUIRE_PAYLOAD, pContainer ? pContainer->sczId : pPayloadGroupItem->pPayload->sczKey, NULL, NULL); | ||
1371 | hr = S_OK; | ||
1372 | } | ||
1373 | |||
1374 | ExitOnFailure(hr, "Failed to acquire %hs: %ls", pContainer ? "container" : "payload", pContainer ? pContainer->sczId : pPayloadGroupItem->pPayload->sczKey); | ||
1375 | } while (fRetry); | ||
1376 | |||
1377 | LExit: | ||
1378 | return hr; | ||
1379 | } | ||
1380 | |||
1381 | static HRESULT AcquireContainerOrPayload( | ||
1382 | __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, | ||
1383 | __out BOOL* pfRetry | ||
1384 | ) | ||
1385 | { | ||
1386 | BURN_CACHE_CONTEXT* pContext = pProgress->pCacheContext; | ||
1387 | BURN_CONTAINER* pContainer = pProgress->pContainer; | ||
1388 | BURN_PACKAGE* pPackage = pProgress->pPackage; | ||
1389 | BURN_PAYLOAD* pPayload = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload : NULL; | ||
1390 | AssertSz(pContainer || pPayload, "Must provide a container or a payload."); | ||
1391 | |||
1392 | HRESULT hr = S_OK; | ||
1393 | int nEquivalentPaths = 0; | ||
1394 | LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL; | ||
1395 | LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL; | ||
1396 | LPCWSTR wzPayloadContainerId = pPayload && pPayload->pContainer ? pPayload->pContainer->sczId : NULL; | ||
1397 | LPCWSTR wzDestinationPath = pContainer ? pContainer->sczUnverifiedPath: pPayload->sczUnverifiedPath; | ||
1398 | LPCWSTR wzRelativePath = pContainer ? pContainer->sczFilePath : pPayload->sczFilePath; | ||
1399 | DWORD dwChosenSearchPath = 0; | ||
1400 | DWORD dwDestinationSearchPath = 0; | ||
1401 | BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; | ||
1402 | BOOTSTRAPPER_CACHE_RESOLVE_OPERATION resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_NONE; | ||
1403 | LPWSTR* pwzDownloadUrl = pContainer ? &pContainer->downloadSource.sczUrl : &pPayload->downloadSource.sczUrl; | ||
1404 | LPWSTR* pwzSourcePath = pContainer ? &pContainer->sczSourcePath : &pPayload->sczSourcePath; | ||
1405 | BOOL fFoundLocal = FALSE; | ||
1406 | BOOL fPreferExtract = FALSE; | ||
1407 | DWORD64 qwFileSize = 0; | ||
1408 | BOOL fMinimumFileSize = FALSE; | ||
1409 | |||
1410 | if (pContainer) | ||
1411 | { | ||
1412 | if (pContainer->fAttached) | ||
1413 | { | ||
1414 | fMinimumFileSize = TRUE; | ||
1415 | qwFileSize = pContainer->qwAttachedOffset + pContainer->qwFileSize; | ||
1416 | } | ||
1417 | else if (pContainer->pbHash && pContext->wzLayoutDirectory) | ||
1418 | { | ||
1419 | qwFileSize = pContainer->qwFileSize; | ||
1420 | } | ||
1421 | } | ||
1422 | else if (pPayload->pbHash) | ||
1423 | { | ||
1424 | qwFileSize = pPayload->qwFileSize; | ||
1425 | } | ||
1426 | |||
1427 | pContext->cSearchPaths = 0; | ||
1428 | *pfRetry = FALSE; | ||
1429 | pProgress->fCancel = FALSE; | ||
1430 | |||
1431 | hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pwzSourcePath, pwzDownloadUrl, wzPayloadContainerId, &cacheOperation); | ||
1432 | ExitOnRootFailure(hr, "BA aborted cache acquire begin."); | ||
1433 | |||
1434 | // Skip the Resolving event and probing local paths if the BA already knew it wanted to download or extract. | ||
1435 | if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD != cacheOperation && | ||
1436 | BOOTSTRAPPER_CACHE_OPERATION_EXTRACT != cacheOperation) | ||
1437 | { | ||
1438 | do | ||
1439 | { | ||
1440 | fFoundLocal = FALSE; | ||
1441 | fPreferExtract = FALSE; | ||
1442 | resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_NONE; | ||
1443 | dwChosenSearchPath = 0; | ||
1444 | dwDestinationSearchPath = 0; | ||
1445 | |||
1446 | hr = CacheGetLocalSourcePaths(wzRelativePath, *pwzSourcePath, wzDestinationPath, pContext->wzLayoutDirectory, pContext->pVariables, &pContext->rgSearchPaths, &pContext->cSearchPaths, &dwChosenSearchPath, &dwDestinationSearchPath); | ||
1447 | ExitOnFailure(hr, "Failed to search local source."); | ||
1448 | |||
1449 | if (wzPayloadContainerId) | ||
1450 | { | ||
1451 | // When a payload comes from a container, the container has the highest chance of being correct. | ||
1452 | // But we want to avoid extracting the container multiple times. | ||
1453 | // So only consider the destination path, which means the container was already extracted. | ||
1454 | if (IsValidLocalFile(pContext->rgSearchPaths[dwDestinationSearchPath], qwFileSize, fMinimumFileSize)) | ||
1455 | { | ||
1456 | fFoundLocal = TRUE; | ||
1457 | dwChosenSearchPath = dwDestinationSearchPath; | ||
1458 | } | ||
1459 | else // don't prefer the container if extracting it already failed. | ||
1460 | { | ||
1461 | fPreferExtract = SUCCEEDED(pPayload->pContainer->hrExtract); | ||
1462 | } | ||
1463 | } | ||
1464 | |||
1465 | if (!fFoundLocal) | ||
1466 | { | ||
1467 | for (DWORD i = 0; i < pContext->cSearchPaths; ++i) | ||
1468 | { | ||
1469 | // If the file exists locally with the correct size, choose it. | ||
1470 | if (IsValidLocalFile(pContext->rgSearchPaths[i], qwFileSize, fMinimumFileSize)) | ||
1471 | { | ||
1472 | dwChosenSearchPath = i; | ||
1473 | |||
1474 | fFoundLocal = TRUE; | ||
1475 | break; | ||
1476 | } | ||
1477 | } | ||
1478 | } | ||
1479 | |||
1480 | if (BOOTSTRAPPER_CACHE_OPERATION_COPY == cacheOperation) | ||
1481 | { | ||
1482 | if (fFoundLocal) | ||
1483 | { | ||
1484 | resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_LOCAL; | ||
1485 | } | ||
1486 | } | ||
1487 | else | ||
1488 | { | ||
1489 | if (fPreferExtract) // the file comes from a container which hasn't been extracted yet, so extract it. | ||
1490 | { | ||
1491 | resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER; | ||
1492 | } | ||
1493 | else if (fFoundLocal) // the file exists locally, so copy it. | ||
1494 | { | ||
1495 | resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_LOCAL; | ||
1496 | } | ||
1497 | else if (*pwzDownloadUrl && **pwzDownloadUrl) | ||
1498 | { | ||
1499 | resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD; | ||
1500 | } | ||
1501 | else if (wzPayloadContainerId) | ||
1502 | { | ||
1503 | resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER; | ||
1504 | } | ||
1505 | } | ||
1506 | |||
1507 | // Let the BA have a chance to override the source. | ||
1508 | hr = UserExperienceOnCacheAcquireResolving(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pContext->rgSearchPaths, pContext->cSearchPaths, fFoundLocal, &dwChosenSearchPath, pwzDownloadUrl, wzPayloadContainerId, &resolveOperation); | ||
1509 | ExitOnRootFailure(hr, "BA aborted cache acquire resolving."); | ||
1510 | |||
1511 | switch (resolveOperation) | ||
1512 | { | ||
1513 | case BOOTSTRAPPER_CACHE_RESOLVE_LOCAL: | ||
1514 | cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_COPY; | ||
1515 | break; | ||
1516 | case BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD: | ||
1517 | cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD; | ||
1518 | break; | ||
1519 | case BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER: | ||
1520 | cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_EXTRACT; | ||
1521 | break; | ||
1522 | case BOOTSTRAPPER_CACHE_RESOLVE_RETRY: | ||
1523 | pContext->cSearchPathsMax = max(pContext->cSearchPaths, pContext->cSearchPathsMax); | ||
1524 | break; | ||
1525 | } | ||
1526 | } while (BOOTSTRAPPER_CACHE_RESOLVE_RETRY == resolveOperation); | ||
1527 | } | ||
1528 | |||
1529 | switch (cacheOperation) | ||
1530 | { | ||
1531 | case BOOTSTRAPPER_CACHE_OPERATION_COPY: | ||
1532 | // If the source path and destination path are different, do the copy (otherwise there's no point). | ||
1533 | hr = PathCompare(pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath, &nEquivalentPaths); | ||
1534 | ExitOnFailure(hr, "Failed to determine if payload paths were equivalent, source: %ls, destination: %ls.", pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath); | ||
1535 | |||
1536 | if (CSTR_EQUAL != nEquivalentPaths) | ||
1537 | { | ||
1538 | hr = CopyPayload(pProgress, INVALID_HANDLE_VALUE, pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath); | ||
1539 | ExitOnFailure(hr, "Failed to copy payload: %ls", wzPayloadId); | ||
1540 | |||
1541 | // Store the source path so it can be used as the LastUsedFolder if it passes verification. | ||
1542 | pContext->sczLastUsedFolderCandidate = pContext->rgSearchPaths[dwChosenSearchPath]; | ||
1543 | pContext->rgSearchPaths[dwChosenSearchPath] = NULL; | ||
1544 | } | ||
1545 | |||
1546 | break; | ||
1547 | case BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD: | ||
1548 | hr = DownloadPayload(pProgress, wzDestinationPath); | ||
1549 | ExitOnFailure(hr, "Failed to download payload: %ls", wzPayloadId); | ||
1550 | |||
1551 | break; | ||
1552 | case BOOTSTRAPPER_CACHE_OPERATION_EXTRACT: | ||
1553 | Assert(pPayload && pPayload->pContainer); | ||
1554 | |||
1555 | hr = ApplyExtractContainer(pContext, pPayload->pContainer); | ||
1556 | ExitOnFailure(hr, "Failed to extract container for payload: %ls", wzPayloadId); | ||
1557 | |||
1558 | break; | ||
1559 | default: | ||
1560 | hr = E_FILENOTFOUND; | ||
1561 | LogExitOnFailure(hr, MSG_RESOLVE_SOURCE_FAILED, "Failed to resolve source, payload: %ls, package: %ls, container: %ls", wzPayloadId, pPackage ? pPackage->sczId : NULL, pContainer ? pContainer->sczId : NULL); | ||
1562 | } | ||
1563 | |||
1564 | // Send 100% complete here. This is sometimes the only progress sent to the BA. | ||
1565 | hr = CompleteCacheProgress(pProgress, pContainer ? pContainer->qwFileSize : pPayload->qwFileSize); | ||
1566 | |||
1567 | LExit: | ||
1568 | if (BOOTSTRAPPER_CACHE_OPERATION_EXTRACT == cacheOperation) | ||
1569 | { | ||
1570 | if (FAILED(hr) && SUCCEEDED(pPayload->pContainer->hrExtract) && | ||
1571 | (fFoundLocal || pPayload->downloadSource.sczUrl && *pPayload->downloadSource.sczUrl)) | ||
1572 | { | ||
1573 | *pfRetry = TRUE; | ||
1574 | } | ||
1575 | pPayload->pContainer->hrExtract = hr; | ||
1576 | } | ||
1577 | UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, pfRetry); | ||
1578 | |||
1579 | pContext->cSearchPathsMax = max(pContext->cSearchPaths, pContext->cSearchPathsMax); | ||
1580 | |||
1581 | return hr; | ||
1582 | } | ||
1583 | |||
1584 | static BOOL IsValidLocalFile( | ||
1585 | __in_z LPCWSTR wzFilePath, | ||
1586 | __in DWORD64 qwFileSize, | ||
1587 | __in BOOL fMinimumFileSize | ||
1588 | ) | ||
1589 | { | ||
1590 | LONGLONG llFileSize = 0; | ||
1591 | |||
1592 | if (!qwFileSize) | ||
1593 | { | ||
1594 | return FileExistsEx(wzFilePath, NULL); | ||
1595 | } | ||
1596 | else | ||
1597 | { | ||
1598 | return SUCCEEDED(FileSize(wzFilePath, &llFileSize)) && | ||
1599 | (static_cast<DWORD64>(llFileSize) == qwFileSize || | ||
1600 | fMinimumFileSize && static_cast<DWORD64>(llFileSize) > qwFileSize); | ||
1601 | } | ||
1602 | } | ||
1603 | |||
1604 | static HRESULT LayoutOrCacheContainerOrPayload( | ||
1605 | __in BURN_CACHE_CONTEXT* pContext, | ||
1606 | __in_opt BURN_CONTAINER* pContainer, | ||
1607 | __in_opt BURN_PACKAGE* pPackage, | ||
1608 | __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem, | ||
1609 | __in DWORD cTryAgainAttempts, | ||
1610 | __out BOOL* pfRetry | ||
1611 | ) | ||
1612 | { | ||
1613 | HRESULT hr = S_OK; | ||
1614 | BURN_PAYLOAD* pPayload = pPayloadGroupItem ? pPayloadGroupItem->pPayload : NULL; | ||
1615 | LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : L""; | ||
1616 | LPCWSTR wzUnverifiedPath = pContainer ? pContainer->sczUnverifiedPath : pPayload->sczUnverifiedPath; | ||
1617 | LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : L""; | ||
1618 | BOOL fCanAffectRegistration = FALSE; | ||
1619 | BURN_CACHE_PROGRESS_CONTEXT progress = { }; | ||
1620 | BOOL fMove = !pPayload || 1 == pPayload->cRemainingInstances; | ||
1621 | BOOL fCanceledBegin = FALSE; | ||
1622 | |||
1623 | if (pContainer) | ||
1624 | { | ||
1625 | Assert(!pPayloadGroupItem); | ||
1626 | } | ||
1627 | else | ||
1628 | { | ||
1629 | Assert(pPayload); | ||
1630 | AssertSz(0 < pPayload->cRemainingInstances, "Laying out payload more times than planned."); | ||
1631 | AssertSz(!pPayloadGroupItem->fCached, "Laying out payload group item that was already cached."); | ||
1632 | } | ||
1633 | |||
1634 | if (!pContext->wzLayoutDirectory) | ||
1635 | { | ||
1636 | Assert(!pContainer); | ||
1637 | Assert(pPackage); | ||
1638 | |||
1639 | fCanAffectRegistration = pPackage->fCanAffectRegistration; | ||
1640 | } | ||
1641 | |||
1642 | *pfRetry = FALSE; | ||
1643 | progress.pCacheContext = pContext; | ||
1644 | progress.pContainer = pContainer; | ||
1645 | progress.pPackage = pPackage; | ||
1646 | progress.pPayloadGroupItem = pPayloadGroupItem; | ||
1647 | |||
1648 | do | ||
1649 | { | ||
1650 | fCanceledBegin = FALSE; | ||
1651 | |||
1652 | hr = UserExperienceOnCacheVerifyBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId); | ||
1653 | |||
1654 | if (FAILED(hr)) | ||
1655 | { | ||
1656 | fCanceledBegin = TRUE; | ||
1657 | } | ||
1658 | else | ||
1659 | { | ||
1660 | if (pContext->wzLayoutDirectory) // layout the container or payload. | ||
1661 | { | ||
1662 | if (pContainer) | ||
1663 | { | ||
1664 | hr = CacheLayoutContainer(pContainer, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress); | ||
1665 | } | ||
1666 | else | ||
1667 | { | ||
1668 | hr = CacheLayoutPayload(pPayload, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress); | ||
1669 | } | ||
1670 | } | ||
1671 | else if (INVALID_HANDLE_VALUE != pContext->hPipe) // pass the decision off to the elevated process. | ||
1672 | { | ||
1673 | hr = ElevationCacheCompletePayload(pContext->hPipe, pPackage, pPayload, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress); | ||
1674 | } | ||
1675 | else // complete the payload. | ||
1676 | { | ||
1677 | hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress); | ||
1678 | } | ||
1679 | } | ||
1680 | |||
1681 | if (SUCCEEDED(hr) && fCanAffectRegistration) | ||
1682 | { | ||
1683 | pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; | ||
1684 | } | ||
1685 | |||
1686 | BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = FAILED(hr) && !fCanceledBegin && cTryAgainAttempts < BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS ? BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION : BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; | ||
1687 | UserExperienceOnCacheVerifyComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &action); | ||
1688 | if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action) | ||
1689 | { | ||
1690 | hr = S_FALSE; // retry verify. | ||
1691 | } | ||
1692 | else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action) | ||
1693 | { | ||
1694 | *pfRetry = TRUE; // go back and retry acquire. | ||
1695 | } | ||
1696 | else if (fCanceledBegin) | ||
1697 | { | ||
1698 | ExitOnRootFailure(hr, "BA aborted cache verify begin."); | ||
1699 | } | ||
1700 | } while (S_FALSE == hr); | ||
1701 | |||
1702 | if (SUCCEEDED(hr) && pPayloadGroupItem) | ||
1703 | { | ||
1704 | pPayload->cRemainingInstances -= 1; | ||
1705 | pPayloadGroupItem->fCached = TRUE; | ||
1706 | } | ||
1707 | |||
1708 | LExit: | ||
1709 | return hr; | ||
1710 | } | ||
1711 | |||
1712 | static HRESULT PreparePayloadDestinationPath( | ||
1713 | __in_z LPCWSTR wzDestinationPath | ||
1714 | ) | ||
1715 | { | ||
1716 | HRESULT hr = S_OK; | ||
1717 | DWORD dwFileAttributes = 0; | ||
1718 | |||
1719 | // If the destination file already exists, clear the readonly bit to avoid E_ACCESSDENIED. | ||
1720 | if (FileExistsEx(wzDestinationPath, &dwFileAttributes)) | ||
1721 | { | ||
1722 | if (FILE_ATTRIBUTE_READONLY & dwFileAttributes) | ||
1723 | { | ||
1724 | dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY; | ||
1725 | if (!::SetFileAttributes(wzDestinationPath, dwFileAttributes)) | ||
1726 | { | ||
1727 | ExitWithLastError(hr, "Failed to clear readonly bit on payload destination path: %ls", wzDestinationPath); | ||
1728 | } | ||
1729 | } | ||
1730 | } | ||
1731 | |||
1732 | LExit: | ||
1733 | if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) | ||
1734 | { | ||
1735 | hr = S_OK; | ||
1736 | } | ||
1737 | |||
1738 | return hr; | ||
1739 | } | ||
1740 | |||
1741 | static HRESULT CopyPayload( | ||
1742 | __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, | ||
1743 | __in HANDLE hSourceFile, | ||
1744 | __in_z LPCWSTR wzSourcePath, | ||
1745 | __in_z LPCWSTR wzDestinationPath | ||
1746 | ) | ||
1747 | { | ||
1748 | HRESULT hr = S_OK; | ||
1749 | LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; | ||
1750 | LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : L""; | ||
1751 | HANDLE hDestinationFile = INVALID_HANDLE_VALUE; | ||
1752 | HANDLE hSourceOpenedFile = INVALID_HANDLE_VALUE; | ||
1753 | |||
1754 | DWORD dwLogId = pProgress->pContainer ? MSG_ACQUIRE_CONTAINER : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; | ||
1755 | LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "copy", wzSourcePath); | ||
1756 | |||
1757 | hr = PreparePayloadDestinationPath(wzDestinationPath); | ||
1758 | ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", wzDestinationPath); | ||
1759 | |||
1760 | if (INVALID_HANDLE_VALUE == hSourceFile) | ||
1761 | { | ||
1762 | hSourceOpenedFile = ::CreateFileW(wzSourcePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
1763 | if (INVALID_HANDLE_VALUE == hSourceOpenedFile) | ||
1764 | { | ||
1765 | ExitWithLastError(hr, "Failed to open source file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); | ||
1766 | } | ||
1767 | |||
1768 | hSourceFile = hSourceOpenedFile; | ||
1769 | } | ||
1770 | else | ||
1771 | { | ||
1772 | hr = FileSetPointer(hSourceFile, 0, NULL, FILE_BEGIN); | ||
1773 | ExitOnRootFailure(hr, "Failed to read from start of source file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); | ||
1774 | } | ||
1775 | |||
1776 | hDestinationFile = ::CreateFileW(wzDestinationPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
1777 | if (INVALID_HANDLE_VALUE == hDestinationFile) | ||
1778 | { | ||
1779 | ExitWithLastError(hr, "Failed to open destination file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); | ||
1780 | } | ||
1781 | |||
1782 | hr = FileCopyUsingHandlesWithProgress(hSourceFile, hDestinationFile, 0, CacheProgressRoutine, pProgress); | ||
1783 | if (FAILED(hr)) | ||
1784 | { | ||
1785 | if (pProgress->fCancel) | ||
1786 | { | ||
1787 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
1788 | ExitOnRootFailure(hr, "BA aborted copy of payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); | ||
1789 | } | ||
1790 | else | ||
1791 | { | ||
1792 | ExitOnRootFailure(hr, "Failed attempt to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); | ||
1793 | } | ||
1794 | } | ||
1795 | |||
1796 | LExit: | ||
1797 | ReleaseFileHandle(hDestinationFile); | ||
1798 | ReleaseFileHandle(hSourceOpenedFile); | ||
1799 | |||
1800 | return hr; | ||
1801 | } | ||
1802 | |||
1803 | static HRESULT DownloadPayload( | ||
1804 | __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, | ||
1805 | __in_z LPCWSTR wzDestinationPath | ||
1806 | ) | ||
1807 | { | ||
1808 | HRESULT hr = S_OK; | ||
1809 | LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; | ||
1810 | LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : L""; | ||
1811 | DOWNLOAD_SOURCE* pDownloadSource = pProgress->pContainer ? &pProgress->pContainer->downloadSource : &pProgress->pPayloadGroupItem->pPayload->downloadSource; | ||
1812 | DWORD64 qwDownloadSize = pProgress->pContainer ? pProgress->pContainer->qwFileSize : pProgress->pPayloadGroupItem->pPayload->qwFileSize; | ||
1813 | DOWNLOAD_CACHE_CALLBACK cacheCallback = { }; | ||
1814 | DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { }; | ||
1815 | APPLY_AUTHENTICATION_REQUIRED_DATA authenticationData = { }; | ||
1816 | |||
1817 | DWORD dwLogId = pProgress->pContainer ? MSG_ACQUIRE_CONTAINER : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; | ||
1818 | LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "download", pDownloadSource->sczUrl); | ||
1819 | |||
1820 | hr = PreparePayloadDestinationPath(wzDestinationPath); | ||
1821 | ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", wzDestinationPath); | ||
1822 | |||
1823 | cacheCallback.pfnProgress = CacheProgressRoutine; | ||
1824 | cacheCallback.pfnCancel = NULL; // TODO: set this | ||
1825 | cacheCallback.pv = pProgress; | ||
1826 | |||
1827 | authenticationData.pUX = pProgress->pCacheContext->pUX; | ||
1828 | authenticationData.wzPackageOrContainerId = wzPackageOrContainerId; | ||
1829 | authenticationData.wzPayloadId = wzPayloadId; | ||
1830 | authenticationCallback.pv = static_cast<LPVOID>(&authenticationData); | ||
1831 | authenticationCallback.pfnAuthenticate = &AuthenticationRequired; | ||
1832 | |||
1833 | hr = DownloadUrl(pDownloadSource, qwDownloadSize, wzDestinationPath, &cacheCallback, &authenticationCallback); | ||
1834 | ExitOnFailure(hr, "Failed attempt to download URL: '%ls' to: '%ls'", pDownloadSource->sczUrl, wzDestinationPath); | ||
1835 | |||
1836 | LExit: | ||
1837 | return hr; | ||
1838 | } | ||
1839 | |||
1840 | static HRESULT WINAPI AuthenticationRequired( | ||
1841 | __in LPVOID pData, | ||
1842 | __in HINTERNET hUrl, | ||
1843 | __in long lHttpCode, | ||
1844 | __out BOOL* pfRetrySend, | ||
1845 | __out BOOL* pfRetry | ||
1846 | ) | ||
1847 | { | ||
1848 | Assert(401 == lHttpCode || 407 == lHttpCode); | ||
1849 | |||
1850 | HRESULT hr = S_OK; | ||
1851 | DWORD er = ERROR_SUCCESS; | ||
1852 | BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY; | ||
1853 | LPWSTR sczError = NULL; | ||
1854 | int nResult = IDNOACTION; | ||
1855 | |||
1856 | *pfRetrySend = FALSE; | ||
1857 | *pfRetry = FALSE; | ||
1858 | |||
1859 | hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL); | ||
1860 | ExitOnFailure(hr, "Failed to allocation error string."); | ||
1861 | |||
1862 | APPLY_AUTHENTICATION_REQUIRED_DATA* authenticationData = reinterpret_cast<APPLY_AUTHENTICATION_REQUIRED_DATA*>(pData); | ||
1863 | |||
1864 | UserExperienceOnError(authenticationData->pUX, errorType, authenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value; | ||
1865 | nResult = UserExperienceCheckExecuteResult(authenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult); | ||
1866 | if (IDTRYAGAIN == nResult && authenticationData->pUX->hwndApply) | ||
1867 | { | ||
1868 | 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); | ||
1869 | if (ERROR_SUCCESS == er || ERROR_CANCELLED == er) | ||
1870 | { | ||
1871 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
1872 | } | ||
1873 | else if (ERROR_INTERNET_FORCE_RETRY == er) | ||
1874 | { | ||
1875 | *pfRetrySend = TRUE; | ||
1876 | hr = S_OK; | ||
1877 | } | ||
1878 | else | ||
1879 | { | ||
1880 | hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); | ||
1881 | } | ||
1882 | } | ||
1883 | else if (IDRETRY == nResult) | ||
1884 | { | ||
1885 | *pfRetry = TRUE; | ||
1886 | hr = S_OK; | ||
1887 | } | ||
1888 | else | ||
1889 | { | ||
1890 | hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); | ||
1891 | } | ||
1892 | |||
1893 | LExit: | ||
1894 | ReleaseStr(sczError); | ||
1895 | |||
1896 | return hr; | ||
1897 | } | ||
1898 | |||
1899 | static HRESULT CALLBACK CacheMessageHandler( | ||
1900 | __in BURN_CACHE_MESSAGE* pMessage, | ||
1901 | __in LPVOID pvContext | ||
1902 | ) | ||
1903 | { | ||
1904 | HRESULT hr = S_OK; | ||
1905 | BURN_CACHE_PROGRESS_CONTEXT* pProgress = static_cast<BURN_CACHE_PROGRESS_CONTEXT*>(pvContext); | ||
1906 | LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL; | ||
1907 | LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : pProgress->pPayload ? pProgress->pPayload->sczKey : NULL; | ||
1908 | |||
1909 | switch (pMessage->type) | ||
1910 | { | ||
1911 | case BURN_CACHE_MESSAGE_BEGIN: | ||
1912 | switch (pMessage->begin.cacheStep) | ||
1913 | { | ||
1914 | case BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE: | ||
1915 | pProgress->type = BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY; | ||
1916 | hr = UserExperienceOnCacheContainerOrPayloadVerifyBegin(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId); | ||
1917 | break; | ||
1918 | case BURN_CACHE_STEP_HASH_TO_SKIP_VERIFY: | ||
1919 | pProgress->type = BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY; | ||
1920 | break; | ||
1921 | case BURN_CACHE_STEP_STAGE: | ||
1922 | pProgress->type = BURN_CACHE_PROGRESS_TYPE_STAGE; | ||
1923 | break; | ||
1924 | case BURN_CACHE_STEP_HASH: | ||
1925 | pProgress->type = BURN_CACHE_PROGRESS_TYPE_HASH; | ||
1926 | break; | ||
1927 | case BURN_CACHE_STEP_FINALIZE: | ||
1928 | pProgress->type = BURN_CACHE_PROGRESS_TYPE_FINALIZE; | ||
1929 | break; | ||
1930 | } | ||
1931 | break; | ||
1932 | case BURN_CACHE_MESSAGE_SUCCESS: | ||
1933 | hr = CompleteCacheProgress(pProgress, pMessage->success.qwFileSize); | ||
1934 | break; | ||
1935 | case BURN_CACHE_MESSAGE_COMPLETE: | ||
1936 | switch (pProgress->type) | ||
1937 | { | ||
1938 | case BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY: | ||
1939 | hr = UserExperienceOnCacheContainerOrPayloadVerifyComplete(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, hr); | ||
1940 | break; | ||
1941 | } | ||
1942 | } | ||
1943 | |||
1944 | return hr; | ||
1945 | } | ||
1946 | |||
1947 | static HRESULT CompleteCacheProgress( | ||
1948 | __in BURN_CACHE_PROGRESS_CONTEXT* pContext, | ||
1949 | __in DWORD64 qwFileSize | ||
1950 | ) | ||
1951 | { | ||
1952 | HRESULT hr = S_OK; | ||
1953 | LARGE_INTEGER liContainerOrPayloadSize = { }; | ||
1954 | LARGE_INTEGER liZero = { }; | ||
1955 | DWORD dwResult = 0; | ||
1956 | DWORD64 qwCommitSize = 0; | ||
1957 | |||
1958 | liContainerOrPayloadSize.QuadPart = qwFileSize; | ||
1959 | |||
1960 | // Need to commit the steps that were skipped. | ||
1961 | if (BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY == pContext->type || BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY == pContext->type) | ||
1962 | { | ||
1963 | Assert(!pContext->pPayload); | ||
1964 | |||
1965 | qwCommitSize = qwFileSize * (pContext->pCacheContext->wzLayoutDirectory ? 2 : 3); // Acquire (+ Stage) + Hash + Finalize - 1 (that's added later) | ||
1966 | |||
1967 | pContext->pCacheContext->qwSuccessfulCacheProgress += qwCommitSize; | ||
1968 | |||
1969 | if (pContext->pContainer) | ||
1970 | { | ||
1971 | pContext->pContainer->qwCommittedCacheProgress += qwCommitSize; | ||
1972 | } | ||
1973 | else if (pContext->pPayloadGroupItem) | ||
1974 | { | ||
1975 | pContext->pPayloadGroupItem->qwCommittedCacheProgress += qwCommitSize; | ||
1976 | } | ||
1977 | } | ||
1978 | |||
1979 | dwResult = CacheProgressRoutine(liContainerOrPayloadSize, liContainerOrPayloadSize, liZero, liZero, 0, 0, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, pContext); | ||
1980 | |||
1981 | if (PROGRESS_CONTINUE == dwResult) | ||
1982 | { | ||
1983 | pContext->pCacheContext->qwSuccessfulCacheProgress += qwFileSize; | ||
1984 | |||
1985 | if (pContext->pPayload) | ||
1986 | { | ||
1987 | pContext->pContainer->qwCommittedExtractProgress += qwFileSize; | ||
1988 | } | ||
1989 | else if (pContext->pContainer) | ||
1990 | { | ||
1991 | pContext->pContainer->qwCommittedCacheProgress += qwFileSize; | ||
1992 | } | ||
1993 | else if (pContext->pPayloadGroupItem) | ||
1994 | { | ||
1995 | pContext->pPayloadGroupItem->qwCommittedCacheProgress += qwFileSize; | ||
1996 | } | ||
1997 | |||
1998 | if (BURN_CACHE_PROGRESS_TYPE_FINALIZE == pContext->type && pContext->pCacheContext->sczLastUsedFolderCandidate) | ||
1999 | { | ||
2000 | // We successfully copied from a source location, set that as the last used source. | ||
2001 | CacheSetLastUsedSource(pContext->pCacheContext->pVariables, pContext->pCacheContext->sczLastUsedFolderCandidate, pContext->pContainer ? pContext->pContainer->sczFilePath : pContext->pPayloadGroupItem->pPayload->sczFilePath); | ||
2002 | } | ||
2003 | } | ||
2004 | else if (PROGRESS_CANCEL == dwResult) | ||
2005 | { | ||
2006 | if (pContext->fCancel) | ||
2007 | { | ||
2008 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
2009 | } | ||
2010 | else | ||
2011 | { | ||
2012 | hr = pContext->hrError; | ||
2013 | } | ||
2014 | |||
2015 | if (qwCommitSize) | ||
2016 | { | ||
2017 | pContext->pCacheContext->qwSuccessfulCacheProgress -= qwCommitSize; | ||
2018 | |||
2019 | if (pContext->pContainer) | ||
2020 | { | ||
2021 | pContext->pContainer->qwCommittedCacheProgress -= qwCommitSize; | ||
2022 | } | ||
2023 | else if (pContext->pPayloadGroupItem) | ||
2024 | { | ||
2025 | pContext->pPayloadGroupItem->qwCommittedCacheProgress -= qwCommitSize; | ||
2026 | } | ||
2027 | } | ||
2028 | } | ||
2029 | |||
2030 | return hr; | ||
2031 | } | ||
2032 | |||
2033 | static DWORD CALLBACK CacheProgressRoutine( | ||
2034 | __in LARGE_INTEGER TotalFileSize, | ||
2035 | __in LARGE_INTEGER TotalBytesTransferred, | ||
2036 | __in LARGE_INTEGER /*StreamSize*/, | ||
2037 | __in LARGE_INTEGER /*StreamBytesTransferred*/, | ||
2038 | __in DWORD /*dwStreamNumber*/, | ||
2039 | __in DWORD /*dwCallbackReason*/, | ||
2040 | __in HANDLE /*hSourceFile*/, | ||
2041 | __in HANDLE /*hDestinationFile*/, | ||
2042 | __in_opt LPVOID lpData | ||
2043 | ) | ||
2044 | { | ||
2045 | HRESULT hr = S_OK; | ||
2046 | DWORD dwResult = PROGRESS_CONTINUE; | ||
2047 | BURN_CACHE_PROGRESS_CONTEXT* pProgress = static_cast<BURN_CACHE_PROGRESS_CONTEXT*>(lpData); | ||
2048 | LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL; | ||
2049 | LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : pProgress->pPayload ? pProgress->pPayload->sczKey : NULL; | ||
2050 | DWORD64 qwCacheProgress = pProgress->pCacheContext->qwSuccessfulCacheProgress + TotalBytesTransferred.QuadPart; | ||
2051 | if (qwCacheProgress > pProgress->pCacheContext->qwTotalCacheSize) | ||
2052 | { | ||
2053 | //AssertSz(FALSE, "Apply has cached more than Plan envisioned."); | ||
2054 | qwCacheProgress = pProgress->pCacheContext->qwTotalCacheSize; | ||
2055 | } | ||
2056 | DWORD dwOverallPercentage = pProgress->pCacheContext->qwTotalCacheSize ? static_cast<DWORD>(qwCacheProgress * 100 / pProgress->pCacheContext->qwTotalCacheSize) : 0; | ||
2057 | |||
2058 | switch (pProgress->type) | ||
2059 | { | ||
2060 | case BURN_CACHE_PROGRESS_TYPE_ACQUIRE: | ||
2061 | hr = UserExperienceOnCacheAcquireProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); | ||
2062 | ExitOnRootFailure(hr, "BA aborted acquire of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); | ||
2063 | break; | ||
2064 | case BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY: | ||
2065 | hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_HASH); | ||
2066 | ExitOnRootFailure(hr, "BA aborted payload verify step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); | ||
2067 | break; | ||
2068 | case BURN_CACHE_PROGRESS_TYPE_STAGE: | ||
2069 | hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_STAGE); | ||
2070 | ExitOnRootFailure(hr, "BA aborted stage step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); | ||
2071 | break; | ||
2072 | case BURN_CACHE_PROGRESS_TYPE_HASH: | ||
2073 | hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_HASH); | ||
2074 | ExitOnRootFailure(hr, "BA aborted hash step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); | ||
2075 | break; | ||
2076 | case BURN_CACHE_PROGRESS_TYPE_FINALIZE: | ||
2077 | hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_FINALIZE); | ||
2078 | ExitOnRootFailure(hr, "BA aborted finalize step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); | ||
2079 | break; | ||
2080 | case BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY: | ||
2081 | hr = UserExperienceOnCacheContainerOrPayloadVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); | ||
2082 | ExitOnRootFailure(hr, "BA aborted container or payload verify: %ls", wzPayloadId); | ||
2083 | break; | ||
2084 | case BURN_CACHE_PROGRESS_TYPE_EXTRACT: | ||
2085 | hr = UserExperienceOnCachePayloadExtractProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); | ||
2086 | ExitOnRootFailure(hr, "BA aborted extract container: %ls, payload: %ls", wzPackageOrContainerId, wzPayloadId); | ||
2087 | break; | ||
2088 | } | ||
2089 | |||
2090 | LExit: | ||
2091 | if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr) | ||
2092 | { | ||
2093 | dwResult = PROGRESS_CANCEL; | ||
2094 | pProgress->fCancel = TRUE; | ||
2095 | } | ||
2096 | else if (FAILED(hr)) | ||
2097 | { | ||
2098 | dwResult = PROGRESS_CANCEL; | ||
2099 | pProgress->hrError = hr; | ||
2100 | } | ||
2101 | else | ||
2102 | { | ||
2103 | dwResult = PROGRESS_CONTINUE; | ||
2104 | } | ||
2105 | |||
2106 | return dwResult; | ||
2107 | } | ||
2108 | |||
2109 | static void DoRollbackCache( | ||
2110 | __in BURN_USER_EXPERIENCE* /*pUX*/, | ||
2111 | __in BURN_PLAN* pPlan, | ||
2112 | __in HANDLE hPipe, | ||
2113 | __in DWORD dwCheckpoint | ||
2114 | ) | ||
2115 | { | ||
2116 | HRESULT hr = S_OK; | ||
2117 | DWORD iCheckpoint = 0; | ||
2118 | |||
2119 | // Scan to last checkpoint. | ||
2120 | for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i) | ||
2121 | { | ||
2122 | BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i]; | ||
2123 | |||
2124 | if (BURN_CACHE_ACTION_TYPE_CHECKPOINT == pRollbackCacheAction->type && pRollbackCacheAction->checkpoint.dwId == dwCheckpoint) | ||
2125 | { | ||
2126 | iCheckpoint = i; | ||
2127 | break; | ||
2128 | } | ||
2129 | } | ||
2130 | |||
2131 | // Rollback cache actions. | ||
2132 | if (iCheckpoint) | ||
2133 | { | ||
2134 | // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF. | ||
2135 | for (int i = iCheckpoint - 1; i >= 0; --i) | ||
2136 | { | ||
2137 | BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i]; | ||
2138 | |||
2139 | switch (pRollbackCacheAction->type) | ||
2140 | { | ||
2141 | case BURN_CACHE_ACTION_TYPE_CHECKPOINT: | ||
2142 | break; | ||
2143 | |||
2144 | case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE: | ||
2145 | hr = CleanPackage(hPipe, pRollbackCacheAction->rollbackPackage.pPackage); | ||
2146 | break; | ||
2147 | |||
2148 | default: | ||
2149 | AssertSz(FALSE, "Invalid rollback cache action."); | ||
2150 | break; | ||
2151 | } | ||
2152 | } | ||
2153 | } | ||
2154 | } | ||
2155 | |||
2156 | static HRESULT DoExecuteAction( | ||
2157 | __in BURN_ENGINE_STATE* pEngineState, | ||
2158 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
2159 | __in_opt HANDLE hCacheThread, | ||
2160 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
2161 | __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, | ||
2162 | __inout BURN_EXECUTE_ACTION_CHECKPOINT** ppCheckpoint, | ||
2163 | __out BOOL* pfSuspend, | ||
2164 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
2165 | ) | ||
2166 | { | ||
2167 | Assert(!pExecuteAction->fDeleted); | ||
2168 | |||
2169 | HRESULT hr = S_OK; | ||
2170 | HANDLE rghWait[2] = { }; | ||
2171 | BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
2172 | BOOL fRetry = FALSE; | ||
2173 | BOOL fStopWusaService = FALSE; | ||
2174 | BOOL fInsideMsiTransaction = FALSE; | ||
2175 | |||
2176 | pContext->fRollback = FALSE; | ||
2177 | |||
2178 | do | ||
2179 | { | ||
2180 | fInsideMsiTransaction = *ppRollbackBoundary && (*ppRollbackBoundary)->fActiveTransaction; | ||
2181 | |||
2182 | switch (pExecuteAction->type) | ||
2183 | { | ||
2184 | case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: | ||
2185 | *ppCheckpoint = &pExecuteAction->checkpoint; | ||
2186 | break; | ||
2187 | |||
2188 | case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT: | ||
2189 | // wait for cache sync-point | ||
2190 | rghWait[0] = pExecuteAction->syncpoint.hEvent; | ||
2191 | rghWait[1] = hCacheThread; | ||
2192 | switch (::WaitForMultipleObjects(rghWait[1] ? 2 : 1, rghWait, FALSE, INFINITE)) | ||
2193 | { | ||
2194 | case WAIT_OBJECT_0: | ||
2195 | break; | ||
2196 | |||
2197 | case WAIT_OBJECT_0 + 1: | ||
2198 | if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr)) | ||
2199 | { | ||
2200 | ExitWithLastError(hr, "Failed to get cache thread exit code."); | ||
2201 | } | ||
2202 | |||
2203 | if (SUCCEEDED(hr)) | ||
2204 | { | ||
2205 | hr = E_UNEXPECTED; | ||
2206 | } | ||
2207 | ExitOnFailure(hr, "Cache thread exited unexpectedly."); | ||
2208 | |||
2209 | case WAIT_FAILED: __fallthrough; | ||
2210 | default: | ||
2211 | ExitWithLastError(hr, "Failed to wait for cache check-point."); | ||
2212 | } | ||
2213 | break; | ||
2214 | |||
2215 | case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: | ||
2216 | hr = ExecuteExePackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart); | ||
2217 | ExitOnFailure(hr, "Failed to execute EXE package."); | ||
2218 | break; | ||
2219 | |||
2220 | case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: | ||
2221 | hr = ExecuteMsiPackage(pEngineState, pExecuteAction, pContext, fInsideMsiTransaction, FALSE, &fRetry, pfSuspend, &restart); | ||
2222 | ExitOnFailure(hr, "Failed to execute MSI package."); | ||
2223 | break; | ||
2224 | |||
2225 | case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: | ||
2226 | hr = ExecuteMspPackage(pEngineState, pExecuteAction, pContext, fInsideMsiTransaction, FALSE, &fRetry, pfSuspend, &restart); | ||
2227 | ExitOnFailure(hr, "Failed to execute MSP package."); | ||
2228 | break; | ||
2229 | |||
2230 | case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: | ||
2231 | hr = ExecuteMsuPackage(pEngineState, pExecuteAction, pContext, FALSE, fStopWusaService, &fRetry, pfSuspend, &restart); | ||
2232 | fStopWusaService = fRetry; | ||
2233 | ExitOnFailure(hr, "Failed to execute MSU package."); | ||
2234 | break; | ||
2235 | |||
2236 | case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: | ||
2237 | hr = ExecutePackageProviderAction(pEngineState, pExecuteAction, pContext); | ||
2238 | ExitOnFailure(hr, "Failed to execute package provider registration action."); | ||
2239 | break; | ||
2240 | |||
2241 | case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: | ||
2242 | hr = ExecuteDependencyAction(pEngineState, pExecuteAction, pContext); | ||
2243 | ExitOnFailure(hr, "Failed to execute dependency action."); | ||
2244 | break; | ||
2245 | |||
2246 | break; | ||
2247 | |||
2248 | case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: | ||
2249 | *ppRollbackBoundary = pExecuteAction->rollbackBoundary.pRollbackBoundary; | ||
2250 | break; | ||
2251 | |||
2252 | case BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION: | ||
2253 | hr = ExecuteMsiBeginTransaction(pEngineState, pExecuteAction->msiTransaction.pRollbackBoundary, pContext); | ||
2254 | ExitOnFailure(hr, "Failed to execute begin MSI transaction action."); | ||
2255 | break; | ||
2256 | |||
2257 | case BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION: | ||
2258 | hr = ExecuteMsiCommitTransaction(pEngineState, pExecuteAction->msiTransaction.pRollbackBoundary, pContext); | ||
2259 | ExitOnFailure(hr, "Failed to execute commit MSI transaction action."); | ||
2260 | break; | ||
2261 | |||
2262 | default: | ||
2263 | hr = E_UNEXPECTED; | ||
2264 | ExitOnFailure(hr, "Invalid execute action."); | ||
2265 | } | ||
2266 | |||
2267 | if (*pRestart < restart) | ||
2268 | { | ||
2269 | *pRestart = restart; | ||
2270 | } | ||
2271 | } while (fRetry && *pRestart < BOOTSTRAPPER_APPLY_RESTART_INITIATED); | ||
2272 | |||
2273 | LExit: | ||
2274 | return hr; | ||
2275 | } | ||
2276 | |||
2277 | static HRESULT DoRollbackActions( | ||
2278 | __in BURN_ENGINE_STATE* pEngineState, | ||
2279 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
2280 | __in DWORD dwCheckpoint, | ||
2281 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
2282 | ) | ||
2283 | { | ||
2284 | HRESULT hr = S_OK; | ||
2285 | DWORD iCheckpoint = 0; | ||
2286 | BOOL fRetryIgnored = FALSE; | ||
2287 | BOOL fSuspendIgnored = FALSE; | ||
2288 | |||
2289 | pContext->fRollback = TRUE; | ||
2290 | |||
2291 | // scan to last checkpoint | ||
2292 | for (DWORD i = 0; i < pEngineState->plan.cRollbackActions; ++i) | ||
2293 | { | ||
2294 | BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i]; | ||
2295 | if (pRollbackAction->fDeleted) | ||
2296 | { | ||
2297 | continue; | ||
2298 | } | ||
2299 | |||
2300 | if (BURN_EXECUTE_ACTION_TYPE_CHECKPOINT == pRollbackAction->type) | ||
2301 | { | ||
2302 | if (pRollbackAction->checkpoint.dwId == dwCheckpoint) | ||
2303 | { | ||
2304 | iCheckpoint = i; | ||
2305 | break; | ||
2306 | } | ||
2307 | } | ||
2308 | } | ||
2309 | |||
2310 | // execute rollback actions | ||
2311 | if (iCheckpoint) | ||
2312 | { | ||
2313 | // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF. | ||
2314 | for (int i = iCheckpoint - 1; i >= 0; --i) | ||
2315 | { | ||
2316 | BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i]; | ||
2317 | if (pRollbackAction->fDeleted) | ||
2318 | { | ||
2319 | continue; | ||
2320 | } | ||
2321 | |||
2322 | BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
2323 | switch (pRollbackAction->type) | ||
2324 | { | ||
2325 | case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: | ||
2326 | break; | ||
2327 | |||
2328 | case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: | ||
2329 | hr = ExecuteExePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); | ||
2330 | IgnoreRollbackError(hr, "Failed to rollback EXE package."); | ||
2331 | break; | ||
2332 | |||
2333 | case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: | ||
2334 | hr = ExecuteMsiPackage(pEngineState, pRollbackAction, pContext, FALSE, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); | ||
2335 | IgnoreRollbackError(hr, "Failed to rollback MSI package."); | ||
2336 | break; | ||
2337 | |||
2338 | case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: | ||
2339 | hr = ExecuteMspPackage(pEngineState, pRollbackAction, pContext, FALSE, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); | ||
2340 | IgnoreRollbackError(hr, "Failed to rollback MSP package."); | ||
2341 | break; | ||
2342 | |||
2343 | case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: | ||
2344 | hr = ExecuteMsuPackage(pEngineState, pRollbackAction, pContext, TRUE, FALSE, &fRetryIgnored, &fSuspendIgnored, &restart); | ||
2345 | IgnoreRollbackError(hr, "Failed to rollback MSU package."); | ||
2346 | break; | ||
2347 | |||
2348 | case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: | ||
2349 | hr = ExecutePackageProviderAction(pEngineState, pRollbackAction, pContext); | ||
2350 | IgnoreRollbackError(hr, "Failed to rollback package provider action."); | ||
2351 | break; | ||
2352 | |||
2353 | case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: | ||
2354 | hr = ExecuteDependencyAction(pEngineState, pRollbackAction, pContext); | ||
2355 | IgnoreRollbackError(hr, "Failed to rollback dependency action."); | ||
2356 | break; | ||
2357 | |||
2358 | case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: | ||
2359 | ExitFunction1(hr = S_OK); | ||
2360 | |||
2361 | case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE: | ||
2362 | // TODO: This used to be skipped if the package was already cached. | ||
2363 | // Need to figure out new logic for when (if?) to skip it. | ||
2364 | hr = CleanPackage(pEngineState->companionConnection.hPipe, pRollbackAction->uncachePackage.pPackage); | ||
2365 | IgnoreRollbackError(hr, "Failed to uncache package for rollback."); | ||
2366 | break; | ||
2367 | |||
2368 | default: | ||
2369 | hr = E_UNEXPECTED; | ||
2370 | ExitOnFailure(hr, "Invalid rollback action: %d.", pRollbackAction->type); | ||
2371 | } | ||
2372 | |||
2373 | if (*pRestart < restart) | ||
2374 | { | ||
2375 | *pRestart = restart; | ||
2376 | } | ||
2377 | } | ||
2378 | } | ||
2379 | |||
2380 | LExit: | ||
2381 | return hr; | ||
2382 | } | ||
2383 | |||
2384 | static HRESULT ExecuteExePackage( | ||
2385 | __in BURN_ENGINE_STATE* pEngineState, | ||
2386 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
2387 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
2388 | __in BOOL fRollback, | ||
2389 | __out BOOL* pfRetry, | ||
2390 | __out BOOL* pfSuspend, | ||
2391 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
2392 | ) | ||
2393 | { | ||
2394 | HRESULT hr = S_OK; | ||
2395 | HRESULT hrExecute = S_OK; | ||
2396 | GENERIC_EXECUTE_MESSAGE message = { }; | ||
2397 | int nResult = 0; | ||
2398 | BOOL fBeginCalled = FALSE; | ||
2399 | BOOL fExecuted = FALSE; | ||
2400 | BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage; | ||
2401 | |||
2402 | if (FAILED(pPackage->hrCacheResult)) | ||
2403 | { | ||
2404 | LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult); | ||
2405 | ExitFunction1(hr = S_OK); | ||
2406 | } | ||
2407 | |||
2408 | Assert(pContext->fRollback == fRollback); | ||
2409 | pContext->pExecutingPackage = pPackage; | ||
2410 | fBeginCalled = TRUE; | ||
2411 | |||
2412 | // Send package execute begin to BA. | ||
2413 | hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->exePackage.action, INSTALLUILEVEL_NOCHANGE, FALSE); | ||
2414 | ExitOnRootFailure(hr, "BA aborted execute EXE package begin."); | ||
2415 | |||
2416 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
2417 | message.dwAllowedResults = MB_OKCANCEL; | ||
2418 | message.progress.dwPercentage = fRollback ? 100 : 0; | ||
2419 | nResult = GenericExecuteMessageHandler(&message, pContext); | ||
2420 | hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); | ||
2421 | ExitOnRootFailure(hr, "BA aborted EXE progress."); | ||
2422 | |||
2423 | fExecuted = TRUE; | ||
2424 | |||
2425 | // Execute package. | ||
2426 | if (pPackage->fPerMachine) | ||
2427 | { | ||
2428 | hrExecute = ElevationExecuteExePackage(pEngineState->companionConnection.hPipe, pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart); | ||
2429 | ExitOnFailure(hrExecute, "Failed to configure per-machine EXE package."); | ||
2430 | } | ||
2431 | else | ||
2432 | { | ||
2433 | hrExecute = ExeEngineExecutePackage(pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart); | ||
2434 | ExitOnFailure(hrExecute, "Failed to configure per-user EXE package."); | ||
2435 | } | ||
2436 | |||
2437 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
2438 | message.dwAllowedResults = MB_OKCANCEL; | ||
2439 | message.progress.dwPercentage = fRollback ? 0 : 100; | ||
2440 | nResult = GenericExecuteMessageHandler(&message, pContext); | ||
2441 | hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); | ||
2442 | ExitOnRootFailure(hr, "BA aborted EXE progress."); | ||
2443 | |||
2444 | pContext->cExecutedPackages += fRollback ? -1 : 1; | ||
2445 | (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; | ||
2446 | |||
2447 | hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); | ||
2448 | ExitOnRootFailure(hr, "BA aborted EXE package execute progress."); | ||
2449 | |||
2450 | LExit: | ||
2451 | if (fExecuted) | ||
2452 | { | ||
2453 | ExeEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute); | ||
2454 | } | ||
2455 | |||
2456 | if (fBeginCalled) | ||
2457 | { | ||
2458 | hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); | ||
2459 | } | ||
2460 | |||
2461 | return hr; | ||
2462 | } | ||
2463 | |||
2464 | static HRESULT ExecuteMsiPackage( | ||
2465 | __in BURN_ENGINE_STATE* pEngineState, | ||
2466 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
2467 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
2468 | __in BOOL fInsideMsiTransaction, | ||
2469 | __in BOOL fRollback, | ||
2470 | __out BOOL* pfRetry, | ||
2471 | __out BOOL* pfSuspend, | ||
2472 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
2473 | ) | ||
2474 | { | ||
2475 | HRESULT hr = S_OK; | ||
2476 | HRESULT hrExecute = S_OK; | ||
2477 | BOOL fBeginCalled = FALSE; | ||
2478 | BOOL fExecuted = FALSE; | ||
2479 | BURN_PACKAGE* pPackage = pExecuteAction->msiPackage.pPackage; | ||
2480 | |||
2481 | if (FAILED(pPackage->hrCacheResult)) | ||
2482 | { | ||
2483 | LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult); | ||
2484 | ExitFunction1(hr = S_OK); | ||
2485 | } | ||
2486 | |||
2487 | Assert(pContext->fRollback == fRollback); | ||
2488 | pContext->pExecutingPackage = pPackage; | ||
2489 | fBeginCalled = TRUE; | ||
2490 | |||
2491 | // Send package execute begin to BA. | ||
2492 | hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.uiLevel, pExecuteAction->msiPackage.fDisableExternalUiHandler); | ||
2493 | ExitOnRootFailure(hr, "BA aborted execute MSI package begin."); | ||
2494 | |||
2495 | fExecuted = TRUE; | ||
2496 | |||
2497 | // execute package | ||
2498 | if (pPackage->fPerMachine) | ||
2499 | { | ||
2500 | hrExecute = ElevationExecuteMsiPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); | ||
2501 | ExitOnFailure(hrExecute, "Failed to configure per-machine MSI package."); | ||
2502 | } | ||
2503 | else | ||
2504 | { | ||
2505 | hrExecute = MsiEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); | ||
2506 | ExitOnFailure(hrExecute, "Failed to configure per-user MSI package."); | ||
2507 | } | ||
2508 | |||
2509 | pContext->cExecutedPackages += fRollback ? -1 : 1; | ||
2510 | (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; | ||
2511 | |||
2512 | hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); | ||
2513 | ExitOnRootFailure(hr, "BA aborted MSI package execute progress."); | ||
2514 | |||
2515 | LExit: | ||
2516 | if (fExecuted) | ||
2517 | { | ||
2518 | MsiEngineUpdateInstallRegistrationState(pExecuteAction, fRollback, hrExecute, fInsideMsiTransaction); | ||
2519 | } | ||
2520 | |||
2521 | if (fBeginCalled) | ||
2522 | { | ||
2523 | hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); | ||
2524 | } | ||
2525 | |||
2526 | return hr; | ||
2527 | } | ||
2528 | |||
2529 | static HRESULT ExecuteMspPackage( | ||
2530 | __in BURN_ENGINE_STATE* pEngineState, | ||
2531 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
2532 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
2533 | __in BOOL fInsideMsiTransaction, | ||
2534 | __in BOOL fRollback, | ||
2535 | __out BOOL* pfRetry, | ||
2536 | __out BOOL* pfSuspend, | ||
2537 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
2538 | ) | ||
2539 | { | ||
2540 | HRESULT hr = S_OK; | ||
2541 | HRESULT hrExecute = S_OK; | ||
2542 | BOOL fBeginCalled = FALSE; | ||
2543 | BOOL fExecuted = FALSE; | ||
2544 | BURN_PACKAGE* pPackage = pExecuteAction->mspTarget.pPackage; | ||
2545 | |||
2546 | if (FAILED(pPackage->hrCacheResult)) | ||
2547 | { | ||
2548 | LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult); | ||
2549 | ExitFunction1(hr = S_OK); | ||
2550 | } | ||
2551 | |||
2552 | Assert(pContext->fRollback == fRollback); | ||
2553 | pContext->pExecutingPackage = pPackage; | ||
2554 | fBeginCalled = TRUE; | ||
2555 | |||
2556 | // Send package execute begin to BA. | ||
2557 | hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->mspTarget.action, pExecuteAction->mspTarget.uiLevel, pExecuteAction->mspTarget.fDisableExternalUiHandler); | ||
2558 | ExitOnRootFailure(hr, "BA aborted execute MSP package begin."); | ||
2559 | |||
2560 | // Now send all the patches that target this product code. | ||
2561 | for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) | ||
2562 | { | ||
2563 | BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage; | ||
2564 | |||
2565 | hr = UserExperienceOnExecutePatchTarget(&pEngineState->userExperience, pMspPackage->sczId, pExecuteAction->mspTarget.sczTargetProductCode); | ||
2566 | ExitOnRootFailure(hr, "BA aborted execute MSP target."); | ||
2567 | } | ||
2568 | |||
2569 | fExecuted = TRUE; | ||
2570 | |||
2571 | // execute package | ||
2572 | if (pExecuteAction->mspTarget.fPerMachineTarget) | ||
2573 | { | ||
2574 | hrExecute = ElevationExecuteMspPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); | ||
2575 | ExitOnFailure(hrExecute, "Failed to configure per-machine MSP package."); | ||
2576 | } | ||
2577 | else | ||
2578 | { | ||
2579 | hrExecute = MspEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); | ||
2580 | ExitOnFailure(hrExecute, "Failed to configure per-user MSP package."); | ||
2581 | } | ||
2582 | |||
2583 | pContext->cExecutedPackages += fRollback ? -1 : 1; | ||
2584 | (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; | ||
2585 | |||
2586 | hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); | ||
2587 | ExitOnRootFailure(hr, "BA aborted MSP package execute progress."); | ||
2588 | |||
2589 | LExit: | ||
2590 | if (fExecuted) | ||
2591 | { | ||
2592 | MspEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute, fInsideMsiTransaction); | ||
2593 | } | ||
2594 | |||
2595 | if (fBeginCalled) | ||
2596 | { | ||
2597 | hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); | ||
2598 | } | ||
2599 | |||
2600 | return hr; | ||
2601 | } | ||
2602 | |||
2603 | static HRESULT ExecuteMsuPackage( | ||
2604 | __in BURN_ENGINE_STATE* pEngineState, | ||
2605 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
2606 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
2607 | __in BOOL fRollback, | ||
2608 | __in BOOL fStopWusaService, | ||
2609 | __out BOOL* pfRetry, | ||
2610 | __out BOOL* pfSuspend, | ||
2611 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
2612 | ) | ||
2613 | { | ||
2614 | HRESULT hr = S_OK; | ||
2615 | HRESULT hrExecute = S_OK; | ||
2616 | GENERIC_EXECUTE_MESSAGE message = { }; | ||
2617 | int nResult = 0; | ||
2618 | BOOL fBeginCalled = FALSE; | ||
2619 | BOOL fExecuted = FALSE; | ||
2620 | BURN_PACKAGE* pPackage = pExecuteAction->msuPackage.pPackage; | ||
2621 | |||
2622 | if (FAILED(pPackage->hrCacheResult)) | ||
2623 | { | ||
2624 | LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult); | ||
2625 | ExitFunction1(hr = S_OK); | ||
2626 | } | ||
2627 | |||
2628 | Assert(pContext->fRollback == fRollback); | ||
2629 | pContext->pExecutingPackage = pPackage; | ||
2630 | fBeginCalled = TRUE; | ||
2631 | |||
2632 | // Send package execute begin to BA. | ||
2633 | hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->msuPackage.action, INSTALLUILEVEL_NOCHANGE, FALSE); | ||
2634 | ExitOnRootFailure(hr, "BA aborted execute MSU package begin."); | ||
2635 | |||
2636 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
2637 | message.dwAllowedResults = MB_OKCANCEL; | ||
2638 | message.progress.dwPercentage = fRollback ? 100 : 0; | ||
2639 | nResult = GenericExecuteMessageHandler(&message, pContext); | ||
2640 | hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); | ||
2641 | ExitOnRootFailure(hr, "BA aborted MSU progress."); | ||
2642 | |||
2643 | fExecuted = TRUE; | ||
2644 | |||
2645 | // execute package | ||
2646 | if (pPackage->fPerMachine) | ||
2647 | { | ||
2648 | hrExecute = ElevationExecuteMsuPackage(pEngineState->companionConnection.hPipe, pExecuteAction, fRollback, fStopWusaService, GenericExecuteMessageHandler, pContext, pRestart); | ||
2649 | ExitOnFailure(hrExecute, "Failed to configure per-machine MSU package."); | ||
2650 | } | ||
2651 | else | ||
2652 | { | ||
2653 | hrExecute = E_UNEXPECTED; | ||
2654 | ExitOnFailure(hr, "MSU packages cannot be per-user."); | ||
2655 | } | ||
2656 | |||
2657 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
2658 | message.dwAllowedResults = MB_OKCANCEL; | ||
2659 | message.progress.dwPercentage = fRollback ? 0 : 100; | ||
2660 | nResult = GenericExecuteMessageHandler(&message, pContext); | ||
2661 | hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); | ||
2662 | ExitOnRootFailure(hr, "BA aborted MSU progress."); | ||
2663 | |||
2664 | pContext->cExecutedPackages += fRollback ? -1 : 1; | ||
2665 | (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; | ||
2666 | |||
2667 | hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); | ||
2668 | ExitOnRootFailure(hr, "BA aborted MSU package execute progress."); | ||
2669 | |||
2670 | LExit: | ||
2671 | if (fExecuted) | ||
2672 | { | ||
2673 | MsuEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute); | ||
2674 | } | ||
2675 | |||
2676 | if (fBeginCalled) | ||
2677 | { | ||
2678 | hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); | ||
2679 | } | ||
2680 | |||
2681 | return hr; | ||
2682 | } | ||
2683 | |||
2684 | static HRESULT ExecutePackageProviderAction( | ||
2685 | __in BURN_ENGINE_STATE* pEngineState, | ||
2686 | __in BURN_EXECUTE_ACTION* pAction, | ||
2687 | __in BURN_EXECUTE_CONTEXT* /*pContext*/ | ||
2688 | ) | ||
2689 | { | ||
2690 | HRESULT hr = S_OK; | ||
2691 | |||
2692 | if (pAction->packageProvider.pPackage->fPerMachine) | ||
2693 | { | ||
2694 | hr = ElevationExecutePackageProviderAction(pEngineState->companionConnection.hPipe, pAction); | ||
2695 | ExitOnFailure(hr, "Failed to register the package provider on per-machine package."); | ||
2696 | } | ||
2697 | else | ||
2698 | { | ||
2699 | hr = DependencyExecutePackageProviderAction(pAction); | ||
2700 | ExitOnFailure(hr, "Failed to register the package provider on per-user package."); | ||
2701 | } | ||
2702 | |||
2703 | LExit: | ||
2704 | return hr; | ||
2705 | } | ||
2706 | |||
2707 | static HRESULT ExecuteDependencyAction( | ||
2708 | __in BURN_ENGINE_STATE* pEngineState, | ||
2709 | __in BURN_EXECUTE_ACTION* pAction, | ||
2710 | __in BURN_EXECUTE_CONTEXT* /*pContext*/ | ||
2711 | ) | ||
2712 | { | ||
2713 | HRESULT hr = S_OK; | ||
2714 | |||
2715 | if (pAction->packageDependency.pPackage->fPerMachine) | ||
2716 | { | ||
2717 | hr = ElevationExecutePackageDependencyAction(pEngineState->companionConnection.hPipe, pAction); | ||
2718 | ExitOnFailure(hr, "Failed to register the dependency on per-machine package."); | ||
2719 | } | ||
2720 | else | ||
2721 | { | ||
2722 | hr = DependencyExecutePackageDependencyAction(FALSE, pAction); | ||
2723 | ExitOnFailure(hr, "Failed to register the dependency on per-user package."); | ||
2724 | } | ||
2725 | |||
2726 | if (pAction->packageDependency.pPackage->fCanAffectRegistration) | ||
2727 | { | ||
2728 | if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageDependency.action) | ||
2729 | { | ||
2730 | if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pAction->packageDependency.pPackage->cacheRegistrationState) | ||
2731 | { | ||
2732 | pAction->packageDependency.pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; | ||
2733 | } | ||
2734 | |||
2735 | if (BURN_PACKAGE_TYPE_MSP == pAction->packageDependency.pPackage->type) | ||
2736 | { | ||
2737 | for (DWORD i = 0; i < pAction->packageDependency.pPackage->Msp.cTargetProductCodes; ++i) | ||
2738 | { | ||
2739 | BURN_MSPTARGETPRODUCT* pTargetProduct = pAction->packageDependency.pPackage->Msp.rgTargetProducts + i; | ||
2740 | |||
2741 | if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pTargetProduct->registrationState) | ||
2742 | { | ||
2743 | pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; | ||
2744 | } | ||
2745 | } | ||
2746 | } | ||
2747 | else if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pAction->packageDependency.pPackage->installRegistrationState) | ||
2748 | { | ||
2749 | pAction->packageDependency.pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; | ||
2750 | } | ||
2751 | } | ||
2752 | else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageDependency.action) | ||
2753 | { | ||
2754 | if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pAction->packageDependency.pPackage->cacheRegistrationState) | ||
2755 | { | ||
2756 | pAction->packageDependency.pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; | ||
2757 | } | ||
2758 | |||
2759 | if (BURN_PACKAGE_TYPE_MSP == pAction->packageDependency.pPackage->type) | ||
2760 | { | ||
2761 | for (DWORD i = 0; i < pAction->packageDependency.pPackage->Msp.cTargetProductCodes; ++i) | ||
2762 | { | ||
2763 | BURN_MSPTARGETPRODUCT* pTargetProduct = pAction->packageDependency.pPackage->Msp.rgTargetProducts + i; | ||
2764 | |||
2765 | if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pTargetProduct->registrationState) | ||
2766 | { | ||
2767 | pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; | ||
2768 | } | ||
2769 | } | ||
2770 | } | ||
2771 | else if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pAction->packageDependency.pPackage->installRegistrationState) | ||
2772 | { | ||
2773 | pAction->packageDependency.pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; | ||
2774 | } | ||
2775 | } | ||
2776 | } | ||
2777 | |||
2778 | LExit: | ||
2779 | return hr; | ||
2780 | } | ||
2781 | |||
2782 | static HRESULT ExecuteMsiBeginTransaction( | ||
2783 | __in BURN_ENGINE_STATE* pEngineState, | ||
2784 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, | ||
2785 | __in BURN_EXECUTE_CONTEXT* /*pContext*/ | ||
2786 | ) | ||
2787 | { | ||
2788 | HRESULT hr = S_OK; | ||
2789 | BOOL fBeginCalled = FALSE; | ||
2790 | |||
2791 | if (pRollbackBoundary->fActiveTransaction) | ||
2792 | { | ||
2793 | ExitFunction1(hr = E_INVALIDSTATE); | ||
2794 | } | ||
2795 | |||
2796 | fBeginCalled = TRUE; | ||
2797 | hr = UserExperienceOnBeginMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId); | ||
2798 | ExitOnRootFailure(hr, "BA aborted execute begin MSI transaction."); | ||
2799 | |||
2800 | if (pEngineState->plan.fPerMachine) | ||
2801 | { | ||
2802 | hr = ElevationMsiBeginTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary); | ||
2803 | ExitOnFailure(hr, "Failed to begin an elevated MSI transaction."); | ||
2804 | } | ||
2805 | else | ||
2806 | { | ||
2807 | hr = MsiEngineBeginTransaction(pRollbackBoundary); | ||
2808 | } | ||
2809 | |||
2810 | if (SUCCEEDED(hr)) | ||
2811 | { | ||
2812 | pRollbackBoundary->fActiveTransaction = TRUE; | ||
2813 | |||
2814 | ResetTransactionRegistrationState(pEngineState, FALSE); | ||
2815 | } | ||
2816 | |||
2817 | LExit: | ||
2818 | if (fBeginCalled) | ||
2819 | { | ||
2820 | UserExperienceOnBeginMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr); | ||
2821 | } | ||
2822 | |||
2823 | return hr; | ||
2824 | } | ||
2825 | |||
2826 | static HRESULT ExecuteMsiCommitTransaction( | ||
2827 | __in BURN_ENGINE_STATE* pEngineState, | ||
2828 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, | ||
2829 | __in BURN_EXECUTE_CONTEXT* /*pContext*/ | ||
2830 | ) | ||
2831 | { | ||
2832 | HRESULT hr = S_OK; | ||
2833 | BOOL fCommitBeginCalled = FALSE; | ||
2834 | |||
2835 | if (!pRollbackBoundary->fActiveTransaction) | ||
2836 | { | ||
2837 | ExitFunction1(hr = E_INVALIDSTATE); | ||
2838 | } | ||
2839 | |||
2840 | fCommitBeginCalled = TRUE; | ||
2841 | hr = UserExperienceOnCommitMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId); | ||
2842 | ExitOnRootFailure(hr, "BA aborted execute commit MSI transaction."); | ||
2843 | |||
2844 | if (pEngineState->plan.fPerMachine) | ||
2845 | { | ||
2846 | hr = ElevationMsiCommitTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary); | ||
2847 | ExitOnFailure(hr, "Failed to commit an elevated MSI transaction."); | ||
2848 | } | ||
2849 | else | ||
2850 | { | ||
2851 | hr = MsiEngineCommitTransaction(pRollbackBoundary); | ||
2852 | } | ||
2853 | |||
2854 | if (SUCCEEDED(hr)) | ||
2855 | { | ||
2856 | pRollbackBoundary->fActiveTransaction = FALSE; | ||
2857 | |||
2858 | ResetTransactionRegistrationState(pEngineState, TRUE); | ||
2859 | } | ||
2860 | |||
2861 | LExit: | ||
2862 | if (fCommitBeginCalled) | ||
2863 | { | ||
2864 | UserExperienceOnCommitMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr); | ||
2865 | } | ||
2866 | |||
2867 | return hr; | ||
2868 | } | ||
2869 | |||
2870 | static HRESULT ExecuteMsiRollbackTransaction( | ||
2871 | __in BURN_ENGINE_STATE* pEngineState, | ||
2872 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, | ||
2873 | __in BURN_EXECUTE_CONTEXT* /*pContext*/ | ||
2874 | ) | ||
2875 | { | ||
2876 | HRESULT hr = S_OK; | ||
2877 | BOOL fRollbackBeginCalled = FALSE; | ||
2878 | |||
2879 | if (!pRollbackBoundary->fActiveTransaction) | ||
2880 | { | ||
2881 | ExitFunction(); | ||
2882 | } | ||
2883 | |||
2884 | fRollbackBeginCalled = TRUE; | ||
2885 | UserExperienceOnRollbackMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId); | ||
2886 | |||
2887 | if (pEngineState->plan.fPerMachine) | ||
2888 | { | ||
2889 | hr = ElevationMsiRollbackTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary); | ||
2890 | ExitOnFailure(hr, "Failed to rollback an elevated MSI transaction."); | ||
2891 | } | ||
2892 | else | ||
2893 | { | ||
2894 | hr = MsiEngineRollbackTransaction(pRollbackBoundary); | ||
2895 | } | ||
2896 | |||
2897 | LExit: | ||
2898 | pRollbackBoundary->fActiveTransaction = FALSE; | ||
2899 | |||
2900 | ResetTransactionRegistrationState(pEngineState, FALSE); | ||
2901 | |||
2902 | if (fRollbackBeginCalled) | ||
2903 | { | ||
2904 | UserExperienceOnRollbackMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr); | ||
2905 | } | ||
2906 | |||
2907 | return hr; | ||
2908 | } | ||
2909 | |||
2910 | static void ResetTransactionRegistrationState( | ||
2911 | __in BURN_ENGINE_STATE* pEngineState, | ||
2912 | __in BOOL fCommit | ||
2913 | ) | ||
2914 | { | ||
2915 | for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) | ||
2916 | { | ||
2917 | BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; | ||
2918 | |||
2919 | if (BURN_PACKAGE_TYPE_MSP == pPackage->type) | ||
2920 | { | ||
2921 | for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) | ||
2922 | { | ||
2923 | BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j; | ||
2924 | |||
2925 | if (fCommit && BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN != pTargetProduct->transactionRegistrationState) | ||
2926 | { | ||
2927 | pTargetProduct->registrationState = pTargetProduct->transactionRegistrationState; | ||
2928 | } | ||
2929 | |||
2930 | pTargetProduct->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; | ||
2931 | } | ||
2932 | } | ||
2933 | else if (fCommit && BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN != pPackage->transactionRegistrationState) | ||
2934 | { | ||
2935 | pPackage->installRegistrationState = pPackage->transactionRegistrationState; | ||
2936 | } | ||
2937 | |||
2938 | pPackage->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; | ||
2939 | } | ||
2940 | } | ||
2941 | |||
2942 | static HRESULT CleanPackage( | ||
2943 | __in HANDLE hElevatedPipe, | ||
2944 | __in BURN_PACKAGE* pPackage | ||
2945 | ) | ||
2946 | { | ||
2947 | HRESULT hr = S_OK; | ||
2948 | |||
2949 | if (pPackage->fPerMachine) | ||
2950 | { | ||
2951 | hr = ElevationCleanPackage(hElevatedPipe, pPackage); | ||
2952 | } | ||
2953 | else | ||
2954 | { | ||
2955 | hr = CacheRemovePackage(FALSE, pPackage->sczId, pPackage->sczCacheId); | ||
2956 | } | ||
2957 | |||
2958 | if (pPackage->fCanAffectRegistration) | ||
2959 | { | ||
2960 | pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; | ||
2961 | } | ||
2962 | |||
2963 | return hr; | ||
2964 | } | ||
2965 | |||
2966 | static int GenericExecuteMessageHandler( | ||
2967 | __in GENERIC_EXECUTE_MESSAGE* pMessage, | ||
2968 | __in LPVOID pvContext | ||
2969 | ) | ||
2970 | { | ||
2971 | BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext; | ||
2972 | int nResult = IDNOACTION; | ||
2973 | |||
2974 | switch (pMessage->type) | ||
2975 | { | ||
2976 | case GENERIC_EXECUTE_MESSAGE_PROGRESS: | ||
2977 | { | ||
2978 | DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0; | ||
2979 | UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value. | ||
2980 | } | ||
2981 | break; | ||
2982 | |||
2983 | case GENERIC_EXECUTE_MESSAGE_ERROR: | ||
2984 | 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. | ||
2985 | break; | ||
2986 | |||
2987 | case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE: | ||
2988 | UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->filesInUse.cFiles, pMessage->filesInUse.rgwzFiles, &nResult); // ignore return value. | ||
2989 | break; | ||
2990 | } | ||
2991 | |||
2992 | nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult); | ||
2993 | return nResult; | ||
2994 | } | ||
2995 | |||
2996 | static int MsiExecuteMessageHandler( | ||
2997 | __in WIU_MSI_EXECUTE_MESSAGE* pMessage, | ||
2998 | __in_opt LPVOID pvContext | ||
2999 | ) | ||
3000 | { | ||
3001 | BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext; | ||
3002 | int nResult = IDNOACTION; | ||
3003 | |||
3004 | switch (pMessage->type) | ||
3005 | { | ||
3006 | case WIU_MSI_EXECUTE_MESSAGE_PROGRESS: | ||
3007 | { | ||
3008 | DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0; | ||
3009 | UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value. | ||
3010 | } | ||
3011 | break; | ||
3012 | |||
3013 | case WIU_MSI_EXECUTE_MESSAGE_ERROR: | ||
3014 | nResult = pMessage->nResultRecommendation; | ||
3015 | 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. | ||
3016 | break; | ||
3017 | |||
3018 | case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE: | ||
3019 | nResult = pMessage->nResultRecommendation; | ||
3020 | UserExperienceOnExecuteMsiMessage(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiMessage.mt, pMessage->dwAllowedResults, pMessage->msiMessage.wzMessage, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value. | ||
3021 | break; | ||
3022 | |||
3023 | case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE: | ||
3024 | UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiFilesInUse.cFiles, pMessage->msiFilesInUse.rgwzFiles, &nResult); // ignore return value. | ||
3025 | break; | ||
3026 | } | ||
3027 | |||
3028 | nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult); | ||
3029 | return nResult; | ||
3030 | } | ||
3031 | |||
3032 | static HRESULT ReportOverallProgressTicks( | ||
3033 | __in BURN_USER_EXPERIENCE* pUX, | ||
3034 | __in BOOL fRollback, | ||
3035 | __in DWORD cOverallProgressTicksTotal, | ||
3036 | __in DWORD cOverallProgressTicks | ||
3037 | ) | ||
3038 | { | ||
3039 | HRESULT hr = S_OK; | ||
3040 | DWORD dwProgress = cOverallProgressTicksTotal ? (cOverallProgressTicks * 100 / cOverallProgressTicksTotal) : 0; | ||
3041 | |||
3042 | // TODO: consider sending different progress numbers in the future. | ||
3043 | hr = UserExperienceOnProgress(pUX, fRollback, dwProgress, dwProgress); | ||
3044 | |||
3045 | return hr; | ||
3046 | } | ||
3047 | |||
3048 | static HRESULT ExecutePackageComplete( | ||
3049 | __in BURN_USER_EXPERIENCE* pUX, | ||
3050 | __in BURN_VARIABLES* pVariables, | ||
3051 | __in BURN_PACKAGE* pPackage, | ||
3052 | __in HRESULT hrOverall, | ||
3053 | __in HRESULT hrExecute, | ||
3054 | __in BOOL fRollback, | ||
3055 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart, | ||
3056 | __out BOOL* pfRetry, | ||
3057 | __out BOOL* pfSuspend | ||
3058 | ) | ||
3059 | { | ||
3060 | HRESULT hr = FAILED(hrOverall) ? hrOverall : hrExecute; // if the overall function failed use that otherwise use the execution result. | ||
3061 | BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION executePackageCompleteAction = FAILED(hrOverall) || SUCCEEDED(hrExecute) || pPackage->fVital ? BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE; | ||
3062 | |||
3063 | // Send package execute complete to BA. | ||
3064 | UserExperienceOnExecutePackageComplete(pUX, pPackage->sczId, hr, *pRestart, &executePackageCompleteAction); | ||
3065 | if (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RESTART == executePackageCompleteAction) | ||
3066 | { | ||
3067 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; | ||
3068 | } | ||
3069 | *pfRetry = (FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RETRY == executePackageCompleteAction); // allow retry only on failures. | ||
3070 | *pfSuspend = (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_SUSPEND == executePackageCompleteAction); | ||
3071 | |||
3072 | // Remember this package as the package that initiated the forced restart. | ||
3073 | if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart) | ||
3074 | { | ||
3075 | // Best effort to set the forced restart package variable. | ||
3076 | VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, pPackage->sczId, TRUE, FALSE); | ||
3077 | } | ||
3078 | |||
3079 | // If we're retrying, leave a message in the log file and say everything is okay. | ||
3080 | if (*pfRetry) | ||
3081 | { | ||
3082 | LogId(REPORT_STANDARD, MSG_APPLY_RETRYING_PACKAGE, pPackage->sczId, hrExecute); | ||
3083 | hr = S_OK; | ||
3084 | } | ||
3085 | 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. | ||
3086 | { | ||
3087 | LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hrExecute); | ||
3088 | hr = S_OK; | ||
3089 | } | ||
3090 | else | ||
3091 | { | ||
3092 | LogId(REPORT_STANDARD, MSG_APPLY_COMPLETED_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, hr, LoggingRestartToString(*pRestart)); | ||
3093 | } | ||
3094 | |||
3095 | return hr; | ||
3096 | } | ||