summaryrefslogtreecommitdiff
path: root/src/burn/engine/apply.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/burn/engine/apply.cpp')
-rw-r--r--src/burn/engine/apply.cpp3096
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
12const DWORD BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS = 2;
13
14enum 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
27typedef 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
43typedef 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
56typedef 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
68static 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
76static void CalculateKeepRegistration(
77 __in BURN_ENGINE_STATE* pEngineState,
78 __inout BOOL* pfKeepRegistration
79 );
80static 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 );
86static HRESULT ApplyCachePackage(
87 __in BURN_CACHE_CONTEXT* pContext,
88 __in BURN_PACKAGE* pPackage
89 );
90static HRESULT ApplyExtractContainer(
91 __in BURN_CACHE_CONTEXT* pContext,
92 __in BURN_CONTAINER* pContainer
93 );
94static 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 );
101static HRESULT ApplyLayoutContainer(
102 __in BURN_CACHE_CONTEXT* pContext,
103 __in BURN_CONTAINER* pContainer
104 );
105static HRESULT ApplyProcessPayload(
106 __in BURN_CACHE_CONTEXT* pContext,
107 __in_opt BURN_PACKAGE* pPackage,
108 __in BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem
109 );
110static 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 );
116static HRESULT ExtractContainer(
117 __in BURN_CACHE_CONTEXT* pContext,
118 __in BURN_CONTAINER* pContainer
119 );
120static HRESULT LayoutBundle(
121 __in BURN_CACHE_CONTEXT* pContext,
122 __in_z LPCWSTR wzExecutableName,
123 __in_z LPCWSTR wzUnverifiedPath,
124 __in DWORD64 qwBundleSize
125 );
126static 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 );
132static HRESULT AcquireContainerOrPayload(
133 __in BURN_CACHE_PROGRESS_CONTEXT* pProgress,
134 __out BOOL* pfRetry
135 );
136static BOOL IsValidLocalFile(
137 __in_z LPCWSTR wzFilePath,
138 __in DWORD64 qwFileSize,
139 __in BOOL fMinimumFileSize
140 );
141static 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 );
149static HRESULT PreparePayloadDestinationPath(
150 __in_z LPCWSTR wzDestinationPath
151 );
152static HRESULT CopyPayload(
153 __in BURN_CACHE_PROGRESS_CONTEXT* pProgress,
154 __in HANDLE hSourceFile,
155 __in_z LPCWSTR wzSourcePath,
156 __in_z LPCWSTR wzDestinationPath
157 );
158static HRESULT DownloadPayload(
159 __in BURN_CACHE_PROGRESS_CONTEXT* pProgress,
160 __in_z LPCWSTR wzDestinationPath
161 );
162static HRESULT CALLBACK CacheMessageHandler(
163 __in BURN_CACHE_MESSAGE* pMessage,
164 __in LPVOID pvContext
165 );
166static HRESULT CompleteCacheProgress(
167 __in BURN_CACHE_PROGRESS_CONTEXT* pContext,
168 __in DWORD64 qwFileSize
169 );
170static 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 );
181static void DoRollbackCache(
182 __in BURN_USER_EXPERIENCE* pUX,
183 __in BURN_PLAN* pPlan,
184 __in HANDLE hPipe,
185 __in DWORD dwCheckpoint
186 );
187static 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 );
197static 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 );
203static 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 );
212static 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 );
222static 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 );
232static 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 );
242static HRESULT ExecutePackageProviderAction(
243 __in BURN_ENGINE_STATE* pEngineState,
244 __in BURN_EXECUTE_ACTION* pAction,
245 __in BURN_EXECUTE_CONTEXT* pContext
246 );
247static HRESULT ExecuteDependencyAction(
248 __in BURN_ENGINE_STATE* pEngineState,
249 __in BURN_EXECUTE_ACTION* pAction,
250 __in BURN_EXECUTE_CONTEXT* pContext
251 );
252static HRESULT ExecuteMsiBeginTransaction(
253 __in BURN_ENGINE_STATE* pEngineState,
254 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
255 __in BURN_EXECUTE_CONTEXT* pContext
256 );
257static HRESULT ExecuteMsiCommitTransaction(
258 __in BURN_ENGINE_STATE* pEngineState,
259 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
260 __in BURN_EXECUTE_CONTEXT* pContext
261 );
262static HRESULT ExecuteMsiRollbackTransaction(
263 __in BURN_ENGINE_STATE* pEngineState,
264 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
265 __in BURN_EXECUTE_CONTEXT* pContext
266 );
267static void ResetTransactionRegistrationState(
268 __in BURN_ENGINE_STATE* pEngineState,
269 __in BOOL fCommit
270 );
271static HRESULT CleanPackage(
272 __in HANDLE hElevatedPipe,
273 __in BURN_PACKAGE* pPackage
274 );
275static int GenericExecuteMessageHandler(
276 __in GENERIC_EXECUTE_MESSAGE* pMessage,
277 __in LPVOID pvContext
278 );
279static int MsiExecuteMessageHandler(
280 __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
281 __in_opt LPVOID pvContext
282 );
283static HRESULT ReportOverallProgressTicks(
284 __in BURN_USER_EXPERIENCE* pUX,
285 __in BOOL fRollback,
286 __in DWORD cOverallProgressTicksTotal,
287 __in DWORD cOverallProgressTicks
288 );
289static 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
304extern "C" void ApplyInitialize()
305{
306 // Prevent the system from sleeping.
307 ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
308}
309
310extern "C" void ApplyUninitialize()
311{
312 ::SetThreadExecutionState(ES_CONTINUOUS);
313}
314
315extern "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
324LExit:
325 return hr;
326}
327
328extern "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
343extern "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
367LExit:
368 ReleaseHandle(hLock);
369#endif
370 return hr;
371}
372
373extern "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
428LExit:
429 UserExperienceOnRegisterComplete(&pEngineState->userExperience, hr);
430 ReleaseStr(sczEngineWorkingPath);
431
432 return hr;
433}
434
435extern "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
490LExit:
491 UserExperienceOnUnregisterComplete(&pEngineState->userExperience, hr);
492
493 return hr;
494}
495
496extern "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
590LExit:
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
616extern "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
724LExit:
725 // Send execute complete to BA.
726 UserExperienceOnExecuteComplete(&pEngineState->userExperience, hr);
727
728 return hr;
729}
730
731extern "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
751static 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
782static 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
807LExit:
808 return hr;
809}
810
811static 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
887LExit:
888 return hr;
889}
890
891static 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
938LExit:
939 ReleaseNullStr(pContext->sczLastUsedFolderCandidate);
940
941 return hr;
942}
943
944static 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
965LExit:
966 return hr;
967}
968
969static 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
1015LExit:
1016 ReleaseNullStr(pContext->sczLastUsedFolderCandidate);
1017
1018 return hr;
1019}
1020
1021static 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
1074LExit:
1075 ReleaseNullStr(pContext->sczLastUsedFolderCandidate);
1076
1077 return hr;
1078}
1079
1080static 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
1113static 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
1192LExit:
1193 ReleaseStr(sczStreamName);
1194 ContainerClose(&context);
1195
1196 return hr;
1197}
1198
1199static 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
1337LExit:
1338 ReleaseStr(sczDestinationPath);
1339 ReleaseStr(sczBundleDownloadUrl);
1340 ReleaseStr(sczBundlePath);
1341
1342 return hr;
1343}
1344
1345static 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
1377LExit:
1378 return hr;
1379}
1380
1381static 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
1567LExit:
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
1584static 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
1604static 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
1708LExit:
1709 return hr;
1710}
1711
1712static 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
1732LExit:
1733 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
1734 {
1735 hr = S_OK;
1736 }
1737
1738 return hr;
1739}
1740
1741static 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
1796LExit:
1797 ReleaseFileHandle(hDestinationFile);
1798 ReleaseFileHandle(hSourceOpenedFile);
1799
1800 return hr;
1801}
1802
1803static 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
1836LExit:
1837 return hr;
1838}
1839
1840static 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
1893LExit:
1894 ReleaseStr(sczError);
1895
1896 return hr;
1897}
1898
1899static 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
1947static 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
2033static 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
2090LExit:
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
2109static 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
2156static 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
2273LExit:
2274 return hr;
2275}
2276
2277static 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
2380LExit:
2381 return hr;
2382}
2383
2384static 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
2450LExit:
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
2464static 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
2515LExit:
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
2529static 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
2589LExit:
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
2603static 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
2670LExit:
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
2684static 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
2703LExit:
2704 return hr;
2705}
2706
2707static 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
2778LExit:
2779 return hr;
2780}
2781
2782static 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
2817LExit:
2818 if (fBeginCalled)
2819 {
2820 UserExperienceOnBeginMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr);
2821 }
2822
2823 return hr;
2824}
2825
2826static 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
2861LExit:
2862 if (fCommitBeginCalled)
2863 {
2864 UserExperienceOnCommitMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr);
2865 }
2866
2867 return hr;
2868}
2869
2870static 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
2897LExit:
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
2910static 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
2942static 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
2966static 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
2996static 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
3032static 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
3048static 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}