main
cpp 3,784 lines 139 KB
Raw
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__); }; x = S_OK
8 #else
9 #define IgnoreRollbackError(x, f, ...) x = S_OK
10 #endif
11
12 const DWORD BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS = 2;
13
14 enum BURN_CACHE_PROGRESS_TYPE
15 {
16 BURN_CACHE_PROGRESS_TYPE_ACQUIRE,
17 BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY,
18 BURN_CACHE_PROGRESS_TYPE_EXTRACT,
19 BURN_CACHE_PROGRESS_TYPE_FINALIZE,
20 BURN_CACHE_PROGRESS_TYPE_HASH,
21 BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY,
22 BURN_CACHE_PROGRESS_TYPE_STAGE,
23 };
24
25 // structs
26
27 typedef struct _BURN_CACHE_CONTEXT
28 {
29 BURN_CACHE* pCache;
30 BURN_USER_EXPERIENCE* pUX;
31 BURN_VARIABLES* pVariables;
32 BURN_PAYLOADS* pPayloads;
33 HANDLE hPipe;
34 HANDLE hSourceEngineFile;
35 DWORD64 qwTotalCacheSize;
36 DWORD64 qwSuccessfulCacheProgress;
37 LPCWSTR wzLayoutDirectory;
38 LPWSTR* rgSearchPaths;
39 DWORD cSearchPaths;
40 DWORD cSearchPathsMax;
41 LPWSTR sczLocalAcquisitionSourcePath;
42 } BURN_CACHE_CONTEXT;
43
44 typedef struct _BURN_CACHE_PROGRESS_CONTEXT
45 {
46 BURN_CACHE_CONTEXT* pCacheContext;
47 BURN_CACHE_PROGRESS_TYPE type;
48 BURN_CONTAINER* pContainer;
49 BURN_PACKAGE* pPackage;
50 BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem;
51 BURN_PAYLOAD* pPayload;
52
53 BOOL fCancel;
54 HRESULT hrError;
55 } BURN_CACHE_PROGRESS_CONTEXT;
56
57 typedef struct _BURN_EXECUTE_CONTEXT
58 {
59 BURN_CACHE* pCache;
60 BURN_USER_EXPERIENCE* pUX;
61 BURN_APPLY_CONTEXT* pApplyContext;
62 BOOL fRollback;
63 LPCWSTR wzExecutingPackageId;
64 DWORD cExecutedPackages;
65 DWORD cExecutePackagesTotal;
66 BOOL fAbandonedProcess;
67 } BURN_EXECUTE_CONTEXT;
68
69
70 // internal function declarations
71 static HRESULT WINAPI AuthenticationRequired(
72 __in LPVOID pData,
73 __in HINTERNET hUrl,
74 __in long lHttpCode,
75 __out BOOL* pfRetrySend,
76 __out BOOL* pfRetry
77 );
78
79 static void CalculateKeepRegistration(
80 __in BURN_ENGINE_STATE* pEngineState,
81 __in BOOL fLog,
82 __inout BOOTSTRAPPER_REGISTRATION_TYPE* pRegistrationType,
83 __inout DWORD64* pqwEstimatedSize
84 );
85 static HRESULT ExecuteDependentRegistrationActions(
86 __in HANDLE hPipe,
87 __in const BURN_REGISTRATION* pRegistration,
88 __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions,
89 __in DWORD cActions
90 );
91 static HRESULT ApplyCachePackage(
92 __in BURN_CACHE_CONTEXT* pContext,
93 __in BURN_PACKAGE* pPackage
94 );
95 static void FinalizeContainerAcquisition(
96 __in BURN_CACHE_CONTEXT* pContext,
97 __in BURN_CONTAINER* pContainer,
98 __in BOOL fSuccess
99 );
100 static void FinalizePayloadAcquisition(
101 __in BURN_CACHE_CONTEXT* pContext,
102 __in BURN_PAYLOAD* pPayload,
103 __in BOOL fSuccess
104 );
105 static HRESULT ApplyExtractContainer(
106 __in BURN_CACHE_CONTEXT* pContext,
107 __in BURN_CONTAINER* pContainer
108 );
109 static HRESULT ApplyLayoutBundle(
110 __in BURN_CACHE_CONTEXT* pContext,
111 __in BURN_PAYLOAD_GROUP* pPayloads,
112 __in_z LPCWSTR wzExecutableName,
113 __in_z LPCWSTR wzUnverifiedPath,
114 __in DWORD64 qwBundleSize
115 );
116 static HRESULT ApplyLayoutContainer(
117 __in BURN_CACHE_CONTEXT* pContext,
118 __in BURN_CONTAINER* pContainer
119 );
120 static HRESULT ApplyProcessPayload(
121 __in BURN_CACHE_CONTEXT* pContext,
122 __in_opt BURN_PACKAGE* pPackage,
123 __in BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem,
124 __in BOOL fVital
125 );
126 static HRESULT ApplyCacheVerifyContainerOrPayload(
127 __in BURN_CACHE_CONTEXT* pContext,
128 __in_opt BURN_CONTAINER* pContainer,
129 __in_opt BURN_PACKAGE* pPackage,
130 __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem
131 );
132 static HRESULT ExtractContainer(
133 __in BURN_CACHE_CONTEXT* pContext,
134 __in BURN_CONTAINER* pContainer
135 );
136 static HRESULT LayoutBundle(
137 __in BURN_CACHE_CONTEXT* pContext,
138 __in_z LPCWSTR wzExecutableName,
139 __in_z LPCWSTR wzUnverifiedPath,
140 __in DWORD64 qwBundleSize
141 );
142 static HRESULT ApplyAcquireContainerOrPayload(
143 __in BURN_CACHE_CONTEXT* pContext,
144 __in_opt BURN_CONTAINER* pContainer,
145 __in_opt BURN_PACKAGE* pPackage,
146 __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem
147 );
148 static HRESULT AcquireContainerOrPayload(
149 __in BURN_CACHE_PROGRESS_CONTEXT* pProgress,
150 __out BOOL* pfRetry
151 );
152 static BOOL IsValidLocalFile(
153 __in_z LPCWSTR wzFilePath,
154 __in DWORD64 qwFileSize,
155 __in BOOL fMinimumFileSize
156 );
157 static HRESULT LayoutOrCacheContainerOrPayload(
158 __in BURN_CACHE_CONTEXT* pContext,
159 __in_opt BURN_CONTAINER* pContainer,
160 __in_opt BURN_PACKAGE* pPackage,
161 __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem,
162 __in DWORD cTryAgainAttempts,
163 __out BOOL* pfRetry
164 );
165 static HRESULT PreparePayloadDestinationPath(
166 __in_z LPCWSTR wzDestinationPath
167 );
168 static HRESULT CopyPayload(
169 __in BURN_CACHE_PROGRESS_CONTEXT* pProgress,
170 __in HANDLE hSourceFile,
171 __in_z LPCWSTR wzSourcePath,
172 __in_z LPCWSTR wzDestinationPath
173 );
174 static HRESULT DownloadPayload(
175 __in BURN_CACHE_PROGRESS_CONTEXT* pProgress,
176 __in_z LPCWSTR wzDestinationPath
177 );
178 static HRESULT CALLBACK CacheMessageHandler(
179 __in BURN_CACHE_MESSAGE* pMessage,
180 __in LPVOID pvContext
181 );
182 static HRESULT CompleteCacheProgress(
183 __in BURN_CACHE_PROGRESS_CONTEXT* pContext,
184 __in DWORD64 qwFileSize
185 );
186 static DWORD CALLBACK CacheProgressRoutine(
187 __in LARGE_INTEGER TotalFileSize,
188 __in LARGE_INTEGER TotalBytesTransferred,
189 __in LARGE_INTEGER StreamSize,
190 __in LARGE_INTEGER StreamBytesTransferred,
191 __in DWORD dwStreamNumber,
192 __in DWORD dwCallbackReason,
193 __in HANDLE hSourceFile,
194 __in HANDLE hDestinationFile,
195 __in_opt LPVOID lpData
196 );
197 static void DoRollbackCache(
198 __in BURN_USER_EXPERIENCE* pUX,
199 __in BURN_PLAN* pPlan,
200 __in HANDLE hPipe,
201 __in DWORD dwCheckpoint
202 );
203 static HRESULT DoExecuteAction(
204 __in BURN_ENGINE_STATE* pEngineState,
205 __in BURN_EXECUTE_ACTION* pExecuteAction,
206 __in BURN_EXECUTE_CONTEXT* pContext,
207 __inout BURN_EXECUTE_ACTION_CHECKPOINT** ppCheckpoint,
208 __out BOOL* pfSuspend,
209 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
210 );
211 static HRESULT DoRollbackActions(
212 __in BURN_ENGINE_STATE* pEngineState,
213 __in BURN_EXECUTE_CONTEXT* pContext,
214 __in DWORD dwCheckpoint,
215 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
216 );
217 static BOOL ShouldSkipPackage(
218 __in BURN_PACKAGE* pPackage,
219 __in BOOL fRollback
220 );
221 static HRESULT ExecuteRelatedBundle(
222 __in BURN_ENGINE_STATE* pEngineState,
223 __in BURN_EXECUTE_ACTION* pExecuteAction,
224 __in BURN_EXECUTE_CONTEXT* pContext,
225 __in BOOL fRollback,
226 __out BOOL* pfRetry,
227 __out BOOL* pfSuspend,
228 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
229 );
230 static HRESULT DoRestoreRelatedBundleActions(
231 __in BURN_ENGINE_STATE* pEngineState,
232 __in BURN_EXECUTE_CONTEXT* pContext,
233 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
234 );
235 static HRESULT ExecuteBundlePackage(
236 __in BURN_ENGINE_STATE* pEngineState,
237 __in BURN_EXECUTE_ACTION* pExecuteAction,
238 __in BURN_EXECUTE_CONTEXT* pContext,
239 __in BOOL fRollback,
240 __out BOOL* pfRetry,
241 __out BOOL* pfSuspend,
242 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
243 );
244 static HRESULT ExecuteExePackage(
245 __in BURN_ENGINE_STATE* pEngineState,
246 __in BURN_EXECUTE_ACTION* pExecuteAction,
247 __in BURN_EXECUTE_CONTEXT* pContext,
248 __in BOOL fRollback,
249 __out BOOL* pfRetry,
250 __out BOOL* pfSuspend,
251 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
252 );
253 static HRESULT ExecuteMsiPackage(
254 __in BURN_ENGINE_STATE* pEngineState,
255 __in BURN_EXECUTE_ACTION* pExecuteAction,
256 __in BURN_EXECUTE_CONTEXT* pContext,
257 __in BOOL fInsideMsiTransaction,
258 __in BOOL fRollback,
259 __out BOOL* pfRetry,
260 __out BOOL* pfSuspend,
261 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
262 );
263 static HRESULT ExecuteMspPackage(
264 __in BURN_ENGINE_STATE* pEngineState,
265 __in BURN_EXECUTE_ACTION* pExecuteAction,
266 __in BURN_EXECUTE_CONTEXT* pContext,
267 __in BOOL fInsideMsiTransaction,
268 __in BOOL fRollback,
269 __out BOOL* pfRetry,
270 __out BOOL* pfSuspend,
271 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
272 );
273 static HRESULT ExecuteMsuPackage(
274 __in BURN_ENGINE_STATE* pEngineState,
275 __in BURN_EXECUTE_ACTION* pExecuteAction,
276 __in BURN_EXECUTE_CONTEXT* pContext,
277 __in BOOL fRollback,
278 __in BOOL fStopWusaService,
279 __out BOOL* pfRetry,
280 __out BOOL* pfSuspend,
281 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
282 );
283 static HRESULT ExecutePackageProviderAction(
284 __in BURN_ENGINE_STATE* pEngineState,
285 __in BURN_EXECUTE_ACTION* pAction,
286 __in BURN_EXECUTE_CONTEXT* pContext,
287 __in BOOL fRollback
288 );
289 static HRESULT ExecuteDependencyAction(
290 __in BURN_ENGINE_STATE* pEngineState,
291 __in BURN_EXECUTE_ACTION* pAction,
292 __in BURN_EXECUTE_CONTEXT* pContext,
293 __in BOOL fRollback
294 );
295 static HRESULT ExecuteMsiBeginTransaction(
296 __in BURN_ENGINE_STATE* pEngineState,
297 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
298 __in BURN_EXECUTE_CONTEXT* pContext
299 );
300 static HRESULT ExecuteMsiCommitTransaction(
301 __in BURN_ENGINE_STATE* pEngineState,
302 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
303 __in BURN_EXECUTE_CONTEXT* pContext,
304 __out BOOTSTRAPPER_APPLY_RESTART *pRestart
305 );
306 static HRESULT ExecuteMsiRollbackTransaction(
307 __in BURN_ENGINE_STATE* pEngineState,
308 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
309 __in BURN_EXECUTE_CONTEXT* pContext,
310 __out BOOTSTRAPPER_APPLY_RESTART *pRestart
311 );
312 static void ResetTransactionRegistrationState(
313 __in BURN_ENGINE_STATE* pEngineState,
314 __in BOOL fCommit
315 );
316 static HRESULT ExecuteUninstallMsiCompatiblePackage(
317 __in BURN_ENGINE_STATE* pEngineState,
318 __in BURN_EXECUTE_ACTION* pExecuteAction,
319 __in BURN_EXECUTE_CONTEXT* pContext,
320 __out BOOL* pfRetry,
321 __out BOOL* pfSuspend,
322 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
323 );
324 static HRESULT CleanCompatiblePackage(
325 __in BURN_CACHE* pCache,
326 __in HANDLE hElevatedPipe,
327 __in BURN_PACKAGE* pPackage
328 );
329 static HRESULT CleanPackage(
330 __in BURN_CACHE* pCache,
331 __in HANDLE hElevatedPipe,
332 __in BURN_PACKAGE* pPackage
333 );
334 static int GenericExecuteMessageHandler(
335 __in GENERIC_EXECUTE_MESSAGE* pMessage,
336 __in LPVOID pvContext
337 );
338 static int MsiExecuteMessageHandler(
339 __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
340 __in_opt LPVOID pvContext
341 );
342 static HRESULT ReportOverallProgressTicks(
343 __in BURN_USER_EXPERIENCE* pUX,
344 __in BOOL fRollback,
345 __in DWORD cOverallProgressTicksTotal,
346 __in BURN_APPLY_CONTEXT* pApplyContext
347 );
348 static HRESULT ExecutePackageComplete(
349 __in BURN_ENGINE_STATE* pEngineState,
350 __in LPCWSTR wzPackageId,
351 __in BOOL fPackageVital,
352 __in BOOL fAbandonedProcess,
353 __in HRESULT hrOverall,
354 __in HRESULT hrExecute,
355 __in BOOL fRollback,
356 __out BOOTSTRAPPER_APPLY_RESTART* pRestart,
357 __out BOOL* pfRetry,
358 __out BOOL* pfSuspend
359 );
360
361
362 // function definitions
363
364 extern "C" void ApplyInitialize()
365 {
366 // Prevent the system from sleeping.
367 ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
368 }
369
370 extern "C" void ApplyUninitialize()
371 {
372 ::SetThreadExecutionState(ES_CONTINUOUS);
373 }
374
375 extern "C" HRESULT ApplySetVariables(
376 __in BURN_VARIABLES* pVariables
377 )
378 {
379 HRESULT hr = S_OK;
380
381 hr = VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, NULL, TRUE, FALSE);
382 ExitOnFailure(hr, "Failed to set the bundle forced restart package built-in variable.");
383
384 LExit:
385 return hr;
386 }
387
388 extern "C" void ApplyReset(
389 __in BURN_USER_EXPERIENCE* pUX,
390 __in BURN_PACKAGES* pPackages
391 )
392 {
393 BootstrapperApplicationExecuteReset(pUX);
394
395 for (DWORD i = 0; i < pPackages->cPackages; ++i)
396 {
397 BURN_PACKAGE* pPackage = pPackages->rgPackages + i;
398 pPackage->hrCacheResult = S_OK;
399 pPackage->fAcquireOptionalSource = FALSE;
400 pPackage->fReachedExecution = FALSE;
401 pPackage->fAbandonedProcess = FALSE;
402 pPackage->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
403 }
404 }
405
406 extern "C" HRESULT ApplyLock(
407 __in BOOL /*fPerMachine*/,
408 __out HANDLE* phLock
409 )
410 {
411 HRESULT hr = S_OK;
412 *phLock = NULL;
413
414 #if 0 // eventually figure out the correct way to support this. In its current form, embedded bundles (including related bundles) are hosed.
415 DWORD er = ERROR_SUCCESS;
416 HANDLE hLock = NULL;
417
418 hLock = ::CreateMutexW(NULL, TRUE, fPerMachine ? L"Global\\WixBurnExecutionLock" : L"Local\\WixBurnExecutionLock");
419 ExitOnNullWithLastError(hLock, hr, "Failed to create lock.");
420
421 er = ::GetLastError();
422 if (ERROR_ALREADY_EXISTS == er)
423 {
424 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSTALL_ALREADY_RUNNING));
425 }
426
427 *phLock = hLock;
428 hLock = NULL;
429
430 LExit:
431 ReleaseHandle(hLock);
432 #endif
433 return hr;
434 }
435
436 extern "C" HRESULT ApplyRegister(
437 __in BURN_ENGINE_STATE* pEngineState
438 )
439 {
440 HRESULT hr = S_OK;
441 LPWSTR sczEngineWorkingPath = NULL;
442 BOOTSTRAPPER_REGISTRATION_TYPE registrationType = BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS;
443 DWORD64 qwEstimatedSize = 0;
444
445 CalculateKeepRegistration(pEngineState, FALSE, &registrationType, &qwEstimatedSize);
446
447 hr = BACallbackOnRegisterBegin(&pEngineState->userExperience, &registrationType);
448 ExitOnRootFailure(hr, "BA aborted register begin.");
449
450 hr = CacheCalculateBundleWorkingPath(pEngineState->plan.pCache, pEngineState->registration.sczExecutableName, &sczEngineWorkingPath);
451 ExitOnFailure(hr, "Failed to calculate working path for engine.");
452
453 if (pEngineState->registration.fPerMachine)
454 {
455 hr = ElevationSessionBegin(pEngineState->companionConnection.hPipe, sczEngineWorkingPath, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->registration.fDetectedForeignProviderKeyBundleId, qwEstimatedSize, registrationType);
456 ExitOnFailure(hr, "Failed to begin registration session in per-machine process.");
457 }
458 else
459 {
460 hr = RegistrationSessionBegin(sczEngineWorkingPath, &pEngineState->registration, &pEngineState->cache, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, qwEstimatedSize, registrationType);
461 ExitOnFailure(hr, "Failed to begin registration session.");
462 }
463
464 // Apply any registration actions.
465 HRESULT hrExecuteRegistration = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRegistrationActions, pEngineState->plan.cRegistrationActions);
466 UNREFERENCED_PARAMETER(hrExecuteRegistration);
467
468 // Try to save engine state.
469 hr = CoreSaveEngineState(pEngineState);
470 if (FAILED(hr))
471 {
472 LogErrorId(hr, MSG_STATE_NOT_SAVED);
473 hr = S_OK;
474 }
475
476 LExit:
477 BACallbackOnRegisterComplete(&pEngineState->userExperience, hr);
478 ReleaseStr(sczEngineWorkingPath);
479
480 return hr;
481 }
482
483 extern "C" HRESULT ApplyUnregister(
484 __in BURN_ENGINE_STATE* pEngineState,
485 __in BOOL fFailed,
486 __in BOOL fSuspend,
487 __in BOOTSTRAPPER_APPLY_RESTART restart
488 )
489 {
490 HRESULT hr = S_OK;
491 BURN_RESUME_MODE resumeMode = BURN_RESUME_MODE_NONE;
492 BOOTSTRAPPER_REGISTRATION_TYPE defaultRegistrationType = BOOTSTRAPPER_REGISTRATION_TYPE_NONE;
493 BOOTSTRAPPER_REGISTRATION_TYPE registrationType = BOOTSTRAPPER_REGISTRATION_TYPE_NONE;
494 DWORD64 qwEstimatedSize = 0;
495
496 // Calculate special cases for the resume mode. If a restart has been initiated, that trumps all other
497 // modes. If the user chose to suspend the install then we'll use that as the resume mode.
498 if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart)
499 {
500 resumeMode = BURN_RESUME_MODE_REBOOT_PENDING;
501 }
502 else if (fSuspend)
503 {
504 resumeMode = BURN_RESUME_MODE_SUSPEND;
505 }
506 else if (pEngineState->plan.fDisallowRemoval)
507 {
508 resumeMode = BURN_RESUME_MODE_ARP;
509 }
510
511 // If there was a special case, make sure the registration is kept.
512 if (BURN_RESUME_MODE_NONE < resumeMode)
513 {
514 defaultRegistrationType = BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS;
515 }
516
517 CalculateKeepRegistration(pEngineState, TRUE, &defaultRegistrationType, &qwEstimatedSize);
518
519 registrationType = defaultRegistrationType;
520
521 BACallbackOnUnregisterBegin(&pEngineState->userExperience, &registrationType);
522
523 // Barring the special cases, if it was determined that we should keep the registration then
524 // do that, otherwise the resume mode is NONE and registration will be removed.
525 if (BURN_RESUME_MODE_NONE == resumeMode && BOOTSTRAPPER_REGISTRATION_TYPE_NONE < registrationType)
526 {
527 resumeMode = BURN_RESUME_MODE_ARP;
528 }
529
530 // If apply failed in any way and we're going to be keeping the bundle registered then
531 // execute any rollback dependency registration actions.
532 if (fFailed && BURN_RESUME_MODE_NONE < resumeMode && !pEngineState->plan.fDisableRollback)
533 {
534 // Execute any rollback registration actions.
535 HRESULT hrRegistrationRollback = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRollbackRegistrationActions, pEngineState->plan.cRollbackRegistrationActions);
536 IgnoreRollbackError(hrRegistrationRollback, "Dependent registration actions failed");
537 }
538
539 LogId(REPORT_STANDARD, MSG_SESSION_END, pEngineState->registration.sczRegistrationKey, LoggingInstallScopeToString(pEngineState->registration.fPerMachine), LoggingResumeModeToString(resumeMode), LoggingRestartToString(restart), LoggingBoolToString(pEngineState->registration.fDisableResume), LoggingRegistrationTypeToString(defaultRegistrationType), LoggingRegistrationTypeToString(registrationType));
540
541 if (BOOTSTRAPPER_ACTION_UNSAFE_UNINSTALL == pEngineState->plan.action)
542 {
543 registrationType = BOOTSTRAPPER_REGISTRATION_TYPE_NONE;
544 resumeMode = BURN_RESUME_MODE_NONE;
545
546 LogId(REPORT_STANDARD, MSG_UNSAFE_SESSION_END);
547 }
548
549 if (pEngineState->registration.fPerMachine)
550 {
551 hr = ElevationSessionEnd(pEngineState->companionConnection.hPipe, resumeMode, restart, pEngineState->registration.fDetectedForeignProviderKeyBundleId, qwEstimatedSize, registrationType);
552 ExitOnFailure(hr, "Failed to end session in per-machine process.");
553 }
554 else
555 {
556 hr = RegistrationSessionEnd(&pEngineState->registration, &pEngineState->cache, &pEngineState->variables, &pEngineState->packages, resumeMode, restart, qwEstimatedSize, registrationType);
557 ExitOnFailure(hr, "Failed to end session in per-user process.");
558 }
559
560 pEngineState->resumeMode = resumeMode;
561
562 LExit:
563 BACallbackOnUnregisterComplete(&pEngineState->userExperience, hr);
564
565 return hr;
566 }
567
568 extern "C" HRESULT ApplyCache(
569 __in HANDLE hSourceEngineFile,
570 __in BURN_USER_EXPERIENCE* pUX,
571 __in BURN_VARIABLES* pVariables,
572 __in BURN_PLAN* pPlan,
573 __in HANDLE hPipe,
574 __in BURN_APPLY_CONTEXT* pContext
575 )
576 {
577 HRESULT hr = S_OK;
578 DWORD dwCheckpoint = 0;
579 BURN_CACHE_CONTEXT cacheContext = { };
580 BURN_PACKAGE* pPackage = NULL;
581
582 hr = BACallbackOnCacheBegin(pUX);
583 ExitOnRootFailure(hr, "BA aborted cache.");
584
585 hr = CacheEnsureAcquisitionFolder(pPlan->pCache);
586 ExitOnFailure(hr, "Failed to ensure acquisition folder.");
587
588 cacheContext.hSourceEngineFile = hSourceEngineFile;
589 cacheContext.pCache = pPlan->pCache;
590 cacheContext.pPayloads = pPlan->pPayloads;
591 cacheContext.pUX = pUX;
592 cacheContext.pVariables = pVariables;
593 cacheContext.qwTotalCacheSize = pPlan->qwCacheSizeTotal;
594 cacheContext.wzLayoutDirectory = pPlan->sczLayoutDirectory;
595
596 hr = MemAllocArray(reinterpret_cast<LPVOID*>(&cacheContext.rgSearchPaths), sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
597 ExitOnNull(cacheContext.rgSearchPaths, hr, E_OUTOFMEMORY, "Failed to allocate cache search paths array.");
598
599 for (DWORD i = 0; i < pPlan->cCacheActions; ++i)
600 {
601 BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i;
602 cacheContext.hPipe = hPipe;
603 pPackage = NULL;
604
605 switch (pCacheAction->type)
606 {
607 case BURN_CACHE_ACTION_TYPE_CHECKPOINT:
608 dwCheckpoint = pCacheAction->checkpoint.dwId;
609 break;
610
611 case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE:
612 hr = ApplyLayoutBundle(&cacheContext, pCacheAction->bundleLayout.pPayloadGroup, pCacheAction->bundleLayout.sczExecutableName, pCacheAction->bundleLayout.sczUnverifiedPath, pCacheAction->bundleLayout.qwBundleSize);
613 ExitOnFailure(hr, "Failed cache action: %ls", L"layout bundle");
614
615 hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, pContext);
616 LogExitOnRootFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls", L"layout bundle");
617
618 break;
619
620 case BURN_CACHE_ACTION_TYPE_PACKAGE:
621 pPackage = pCacheAction->package.pPackage;
622
623 if (!cacheContext.wzLayoutDirectory)
624 {
625 if (!pPackage->fPerMachine || INVALID_HANDLE_VALUE == cacheContext.hPipe)
626 {
627 hr = CachePreparePackage(pPlan->pCache, pPackage);
628
629 cacheContext.hPipe = INVALID_HANDLE_VALUE;
630 }
631 else
632 {
633 hr = ElevationCachePreparePackage(hPipe, pPackage);
634 }
635 LogExitOnFailure(hr, MSG_CACHE_PREPARE_PACKAGE_FAILED, "Cache prepare package failed: %ls", pPackage->sczId, NULL, NULL);
636 }
637
638 hr = ApplyCachePackage(&cacheContext, pPackage);
639 ExitOnFailure(hr, "Failed cache action: %ls", L"cache package");
640
641 hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, pContext);
642 LogExitOnRootFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls", L"cache package");
643
644 break;
645
646 case BURN_CACHE_ACTION_TYPE_CONTAINER:
647 Assert(pPlan->sczLayoutDirectory);
648 hr = ApplyLayoutContainer(&cacheContext, pCacheAction->container.pContainer);
649 ExitOnFailure(hr, "Failed cache action: %ls", L"layout container");
650
651 break;
652
653 case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT:
654 if (!::SetEvent(pCacheAction->syncpoint.pPackage->hCacheEvent))
655 {
656 ExitWithLastError(hr, "Failed to set syncpoint event.");
657 }
658 break;
659
660 default:
661 AssertSz(FALSE, "Unknown cache action.");
662 break;
663 }
664 }
665
666 LExit:
667 pContext->dwCacheCheckpoint = dwCheckpoint;
668
669 // Clean up any remanents in the cache.
670 if (INVALID_HANDLE_VALUE != hPipe)
671 {
672 ElevationCacheCleanup(hPipe);
673 }
674
675 CacheCleanup(FALSE, pPlan->pCache);
676
677 for (DWORD i = 0; i < cacheContext.cSearchPathsMax; ++i)
678 {
679 ReleaseNullStr(cacheContext.rgSearchPaths[i]);
680 }
681 ReleaseMem(cacheContext.rgSearchPaths);
682 ReleaseStr(cacheContext.sczLocalAcquisitionSourcePath);
683
684 BACallbackOnCacheComplete(pUX, hr);
685 return hr;
686 }
687
688 extern "C" void ApplyCacheRollback(
689 __in BURN_USER_EXPERIENCE* pUX,
690 __in BURN_PLAN* pPlan,
691 __in HANDLE hPipe,
692 __in BURN_APPLY_CONTEXT* pContext
693 )
694 {
695 DoRollbackCache(pUX, pPlan, hPipe, pContext->dwCacheCheckpoint);
696 }
697
698 extern "C" HRESULT ApplyExecute(
699 __in BURN_ENGINE_STATE* pEngineState,
700 __in BURN_APPLY_CONTEXT* pApplyContext,
701 __out BOOL* pfSuspend,
702 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
703 )
704 {
705 HRESULT hr = S_OK;
706 HRESULT hrRollback = S_OK;
707 BURN_EXECUTE_ACTION_CHECKPOINT* pCheckpoint = NULL;
708 BURN_EXECUTE_CONTEXT context = { };
709 BOOL fSeekRollbackBoundaryEnd = FALSE;
710
711 context.pCache = pEngineState->plan.pCache;
712 context.pUX = &pEngineState->userExperience;
713 context.pApplyContext = pApplyContext;
714 context.cExecutePackagesTotal = pEngineState->plan.cExecutePackagesTotal;
715
716 *pfSuspend = FALSE;
717
718 // Send execute begin to BA.
719 hr = BACallbackOnExecuteBegin(&pEngineState->userExperience, pEngineState->plan.cExecutePackagesTotal);
720 ExitOnRootFailure(hr, "BA aborted execute begin.");
721
722 // Do execute actions.
723 for (DWORD i = 0; i < pEngineState->plan.cExecuteActions; ++i)
724 {
725 BURN_EXECUTE_ACTION* pExecuteAction = &pEngineState->plan.rgExecuteActions[i];
726 if (pExecuteAction->fDeleted)
727 {
728 continue;
729 }
730
731 context.wzExecutingPackageId = NULL;
732 context.fAbandonedProcess = FALSE;
733
734 // If we are seeking the end of the rollback boundary, skip if this action wasn't it.
735 if (fSeekRollbackBoundaryEnd)
736 {
737 if (BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY_END != pExecuteAction->type)
738 {
739 LPCWSTR wzId = NULL;
740 switch (pExecuteAction->type)
741 {
742 case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE:
743 wzId = pExecuteAction->relatedBundle.pRelatedBundle->package.sczId;
744 break;
745 case BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE:
746 wzId = pExecuteAction->bundlePackage.pPackage->sczId;
747 break;
748 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
749 wzId = pExecuteAction->exePackage.pPackage->sczId;
750 break;
751 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
752 wzId = pExecuteAction->msiPackage.pPackage->sczId;
753 break;
754 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
755 wzId = pExecuteAction->mspTarget.pPackage->sczId;
756 break;
757 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
758 wzId = pExecuteAction->msuPackage.pPackage->sczId;
759 break;
760 }
761
762 if (wzId)
763 {
764 LogId(REPORT_WARNING, MSG_APPLY_SKIPPING_NONVITAL_ROLLBACK_BOUNDARY_PACKAGE, wzId);
765 }
766
767 continue;
768 }
769
770 fSeekRollbackBoundaryEnd = FALSE;
771 }
772
773 // Execute the action.
774 hr = DoExecuteAction(pEngineState, pExecuteAction, &context, &pCheckpoint, pfSuspend, pRestart);
775
776 if (*pfSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart)
777 {
778 if (pCheckpoint && pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction)
779 {
780 hr = E_INVALIDSTATE;
781 LogId(REPORT_ERROR, MSG_RESTART_REQUEST_DURING_MSI_TRANSACTION, pCheckpoint->pActiveRollbackBoundary->sczId);
782 }
783 else
784 {
785 ExitFunction();
786 }
787 }
788
789 if (FAILED(hr))
790 {
791 // If rollback is disabled, keep what we have and always end execution here.
792 if (pEngineState->plan.fDisableRollback)
793 {
794 LogId(REPORT_WARNING, MSG_PLAN_ROLLBACK_DISABLED);
795
796 if (pCheckpoint && pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction)
797 {
798 hrRollback = ExecuteMsiCommitTransaction(pEngineState, pCheckpoint->pActiveRollbackBoundary, &context, pRestart);
799 IgnoreRollbackError(hrRollback, "Failed commit transaction from disable rollback");
800 }
801
802 break;
803 }
804
805 // The action failed, roll back to previous rollback boundary.
806 if (pCheckpoint)
807 {
808 // If inside a MSI transaction, roll it back.
809 if (pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction)
810 {
811 hrRollback = ExecuteMsiRollbackTransaction(pEngineState, pCheckpoint->pActiveRollbackBoundary, &context, pRestart);
812 IgnoreRollbackError(hrRollback, "Failed rolling back transaction");
813 }
814
815 hrRollback = DoRollbackActions(pEngineState, &context, pCheckpoint->dwId, pRestart);
816 IgnoreRollbackError(hrRollback, "Failed rollback actions");
817
818 if (pCheckpoint->pActiveRollbackBoundary)
819 {
820 if (pCheckpoint->pActiveRollbackBoundary->fVital)
821 {
822 hrRollback = DoRestoreRelatedBundleActions(pEngineState, &context, pRestart);
823 IgnoreRollbackError(hrRollback, "Failed rollback actions");
824
825 // If the rollback boundary is vital, end execution here.
826 break;
827 }
828
829 // Move forward to the end of this rollback boundary.
830 fSeekRollbackBoundaryEnd = TRUE;
831
832 LogId(REPORT_WARNING, MSG_APPLY_SKIPPING_REST_OF_NONVITAL_ROLLBACK_BOUNDARY, pCheckpoint->pActiveRollbackBoundary->sczId);
833 }
834 }
835
836 // Ignore failure outside of a vital rollback boundary.
837 hr = S_OK;
838 }
839 }
840
841 LExit:
842 // Send execute complete to BA.
843 BACallbackOnExecuteComplete(&pEngineState->userExperience, hr);
844
845 return hr;
846 }
847
848 extern "C" void ApplyClean(
849 __in BURN_USER_EXPERIENCE* /*pUX*/,
850 __in BURN_PLAN* pPlan,
851 __in HANDLE hPipe
852 )
853 {
854 HRESULT hr = S_OK;
855
856 for (DWORD i = 0; i < pPlan->cCleanActions; ++i)
857 {
858 BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + i;
859 BURN_PACKAGE* pPackage = pCleanAction->pPackage;
860
861 switch (pCleanAction->type)
862 {
863 case BURN_CLEAN_ACTION_TYPE_COMPATIBLE_PACKAGE:
864 hr = CleanCompatiblePackage(pPlan->pCache, hPipe, pPackage);
865 break;
866
867 case BURN_CLEAN_ACTION_TYPE_PACKAGE:
868 hr = CleanPackage(pPlan->pCache, hPipe, pPackage);
869 break;
870
871 default:
872 AssertSz(FALSE, "Unknown clean action.");
873 break;
874 }
875 }
876 }
877
878
879 // internal helper functions
880
881 static void CalculateKeepRegistration(
882 __in BURN_ENGINE_STATE* pEngineState,
883 __in BOOL fLog,
884 __inout BOOTSTRAPPER_REGISTRATION_TYPE* pRegistrationType,
885 __inout DWORD64* pqwEstimatedSize
886 )
887 {
888 DWORD64 qwEstimatedSize = pEngineState->section.qwBundleSize;
889
890 if (fLog)
891 {
892 LogId(REPORT_STANDARD, MSG_POST_APPLY_CALCULATE_REGISTRATION);
893 }
894
895 for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i)
896 {
897 BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i;
898
899 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
900 {
901 MspEngineFinalizeInstallRegistrationState(pPackage);
902 }
903
904 if (fLog)
905 {
906 LogId(REPORT_STANDARD, MSG_POST_APPLY_PACKAGE, pPackage->sczId, LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState));
907 }
908
909 if (!pPackage->fCanAffectRegistration)
910 {
911 continue;
912 }
913
914 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState)
915 {
916 *pRegistrationType = BOOTSTRAPPER_REGISTRATION_TYPE_FULL;
917
918 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
919 {
920 qwEstimatedSize += pPackage->qwSize;
921 }
922
923 qwEstimatedSize += pPackage->qwInstallSize;
924 }
925
926 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState)
927 {
928 if (BOOTSTRAPPER_REGISTRATION_TYPE_NONE == *pRegistrationType)
929 {
930 *pRegistrationType = BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS;
931 }
932
933 qwEstimatedSize += pPackage->qwSize;
934 }
935 }
936
937 if (pqwEstimatedSize)
938 {
939 *pqwEstimatedSize = qwEstimatedSize;
940 }
941 }
942
943 static HRESULT ExecuteDependentRegistrationActions(
944 __in HANDLE hPipe,
945 __in const BURN_REGISTRATION* pRegistration,
946 __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions,
947 __in DWORD cActions
948 )
949 {
950 HRESULT hr = S_OK;
951
952 for (DWORD iAction = 0; iAction < cActions; ++iAction)
953 {
954 const BURN_DEPENDENT_REGISTRATION_ACTION* pAction = rgActions + iAction;
955
956 if (pRegistration->fPerMachine)
957 {
958 hr = ElevationProcessDependentRegistration(hPipe, pAction);
959 ExitOnFailure(hr, "Failed to execute dependent registration action.");
960 }
961 else
962 {
963 hr = DependencyProcessDependentRegistration(pRegistration, pAction);
964 ExitOnFailure(hr, "Failed to process dependency registration action.");
965 }
966 }
967
968 LExit:
969 return hr;
970 }
971
972 static HRESULT ApplyCachePackage(
973 __in BURN_CACHE_CONTEXT* pContext,
974 __in BURN_PACKAGE* pPackage
975 )
976 {
977 HRESULT hr = S_OK;
978 BOOL fCanceledBegin = FALSE;
979 BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE;
980 BOOL fVital = pPackage->fCacheVital;
981
982 for (;;)
983 {
984 fCanceledBegin = FALSE;
985
986 hr = BACallbackOnCachePackageBegin(pContext->pUX, pPackage->sczId, pPackage->payloads.cItems, pPackage->payloads.qwTotalSize, fVital);
987 if (FAILED(hr))
988 {
989 fCanceledBegin = TRUE;
990 }
991 else
992 {
993 for (DWORD i = 0; i < pPackage->payloads.cItems; ++i)
994 {
995 BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem = pPackage->payloads.rgItems + i;
996
997 hr = ApplyProcessPayload(pContext, pPackage, pPayloadGroupItem, fVital);
998 if (FAILED(hr))
999 {
1000 break;
1001 }
1002 }
1003 }
1004
1005 pPackage->hrCacheResult = hr;
1006 cachePackageCompleteAction = SUCCEEDED(hr) || (pPackage->fVital && fVital) || fCanceledBegin ? BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE;
1007 BACallbackOnCachePackageComplete(pContext->pUX, pPackage->sczId, hr, &cachePackageCompleteAction);
1008
1009 if (SUCCEEDED(hr))
1010 {
1011 break;
1012 }
1013
1014 if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_RETRY == cachePackageCompleteAction)
1015 {
1016 for (DWORD i = 0; i < pPackage->payloads.cItems; ++i)
1017 {
1018 BURN_PAYLOAD_GROUP_ITEM* pItem = pPackage->payloads.rgItems + i;
1019 if (pItem->fCached)
1020 {
1021 pItem->pPayload->cRemainingInstances += 1;
1022 pItem->fCached = FALSE;
1023 }
1024
1025 if (pItem->qwCommittedCacheProgress)
1026 {
1027 pContext->qwSuccessfulCacheProgress -= pItem->qwCommittedCacheProgress;
1028 pItem->qwCommittedCacheProgress = 0;
1029 }
1030 }
1031
1032 LogErrorId(hr, MSG_CACHE_RETRYING_PACKAGE, pPackage->sczId, NULL, NULL);
1033
1034 continue;
1035 }
1036 else if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE == cachePackageCompleteAction && (!pPackage->fVital || !fVital)) // ignore non-vital download failures.
1037 {
1038 LogId(REPORT_STANDARD, MSG_CACHE_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hr);
1039 hr = S_OK;
1040 }
1041 else if (fCanceledBegin)
1042 {
1043 LogExitOnRootFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls: %ls", L"begin cache package", pPackage->sczId);
1044 }
1045
1046 break;
1047 }
1048
1049 LExit:
1050 return hr;
1051 }
1052
1053 static void FinalizeContainerAcquisition(
1054 __in BURN_CACHE_CONTEXT* pContext,
1055 __in BURN_CONTAINER* pContainer,
1056 __in BOOL fSuccess
1057 )
1058 {
1059 ReleaseNullStr(pContainer->sczFailedLocalAcquisitionPath);
1060
1061 if (fSuccess)
1062 {
1063 ReleaseNullStr(pContext->sczLocalAcquisitionSourcePath);
1064 pContainer->fFailedVerificationFromAcquisition = FALSE;
1065 }
1066 else if (pContext->sczLocalAcquisitionSourcePath)
1067 {
1068 pContainer->sczFailedLocalAcquisitionPath = pContext->sczLocalAcquisitionSourcePath;
1069 pContext->sczLocalAcquisitionSourcePath = NULL;
1070 }
1071 }
1072
1073 static void FinalizePayloadAcquisition(
1074 __in BURN_CACHE_CONTEXT* pContext,
1075 __in BURN_PAYLOAD* pPayload,
1076 __in BOOL fSuccess
1077 )
1078 {
1079 ReleaseNullStr(pPayload->sczFailedLocalAcquisitionPath);
1080
1081 if (fSuccess)
1082 {
1083 ReleaseNullStr(pContext->sczLocalAcquisitionSourcePath);
1084 pPayload->fFailedVerificationFromAcquisition = FALSE;
1085 }
1086 else if (pContext->sczLocalAcquisitionSourcePath)
1087 {
1088 pPayload->sczFailedLocalAcquisitionPath = pContext->sczLocalAcquisitionSourcePath;
1089 pContext->sczLocalAcquisitionSourcePath = NULL;
1090 }
1091 }
1092
1093 static HRESULT ApplyExtractContainer(
1094 __in BURN_CACHE_CONTEXT* pContext,
1095 __in BURN_CONTAINER* pContainer
1096 )
1097 {
1098 HRESULT hr = S_OK;
1099
1100 if (pContainer->qwCommittedCacheProgress)
1101 {
1102 pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedCacheProgress;
1103 pContainer->qwCommittedCacheProgress = 0;
1104 }
1105
1106 if (pContainer->qwCommittedExtractProgress)
1107 {
1108 pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedExtractProgress;
1109 pContainer->qwCommittedExtractProgress = 0;
1110 }
1111
1112 if (!pContainer->fActuallyAttached)
1113 {
1114 hr = ApplyAcquireContainerOrPayload(pContext, pContainer, NULL, NULL);
1115 LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_CONTAINER, "Failed to acquire container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath);
1116 }
1117
1118 hr = ExtractContainer(pContext, pContainer);
1119 LogExitOnFailure(hr, MSG_FAILED_EXTRACT_CONTAINER, "Failed to extract payloads from container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath);
1120
1121 if (pContext->sczLocalAcquisitionSourcePath)
1122 {
1123 // We successfully copied from a source location, set that as the last used source.
1124 CacheSetLastUsedSource(pContext->pVariables, pContext->sczLocalAcquisitionSourcePath, pContainer->sczFilePath);
1125 ReleaseNullStr(pContext->sczLocalAcquisitionSourcePath);
1126 }
1127
1128 if (pContainer->qwExtractSizeTotal < pContainer->qwCommittedExtractProgress)
1129 {
1130 AssertSz(FALSE, "Container extracted more than planned.");
1131 pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedExtractProgress;
1132 pContext->qwSuccessfulCacheProgress += pContainer->qwExtractSizeTotal;
1133 }
1134 else
1135 {
1136 pContext->qwSuccessfulCacheProgress += pContainer->qwExtractSizeTotal - pContainer->qwCommittedExtractProgress;
1137 }
1138
1139 pContainer->qwCommittedExtractProgress = pContainer->qwExtractSizeTotal;
1140
1141 LExit:
1142 FinalizeContainerAcquisition(pContext, pContainer, SUCCEEDED(hr));
1143
1144 return hr;
1145 }
1146
1147 static HRESULT ApplyLayoutBundle(
1148 __in BURN_CACHE_CONTEXT* pContext,
1149 __in BURN_PAYLOAD_GROUP* pPayloads,
1150 __in_z LPCWSTR wzExecutableName,
1151 __in_z LPCWSTR wzUnverifiedPath,
1152 __in DWORD64 qwBundleSize
1153 )
1154 {
1155 HRESULT hr = S_OK;
1156
1157 hr = LayoutBundle(pContext, wzExecutableName, wzUnverifiedPath, qwBundleSize);
1158 ExitOnFailure(hr, "Failed to layout bundle.");
1159
1160 for (DWORD i = 0; i < pPayloads->cItems; ++i)
1161 {
1162 BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem = pPayloads->rgItems + i;
1163
1164 hr = ApplyProcessPayload(pContext, NULL, pPayloadGroupItem, TRUE);
1165 ExitOnFailure(hr, "Failed to layout bundle payload: %ls", pPayloadGroupItem->pPayload->sczKey);
1166 }
1167
1168 LExit:
1169 return hr;
1170 }
1171
1172 static HRESULT ApplyLayoutContainer(
1173 __in BURN_CACHE_CONTEXT* pContext,
1174 __in BURN_CONTAINER* pContainer
1175 )
1176 {
1177 HRESULT hr = S_OK;
1178 DWORD cTryAgainAttempts = 0;
1179 BOOL fRetry = FALSE;
1180
1181 Assert(!pContainer->fAttached);
1182
1183 hr = ApplyCacheVerifyContainerOrPayload(pContext, pContainer, NULL, NULL);
1184 if (SUCCEEDED(hr))
1185 {
1186 ExitFunction();
1187 }
1188
1189 for (;;)
1190 {
1191 fRetry = FALSE;
1192
1193 hr = ApplyAcquireContainerOrPayload(pContext, pContainer, NULL, NULL);
1194 LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_CONTAINER, "Failed to acquire container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath);
1195
1196 hr = LayoutOrCacheContainerOrPayload(pContext, pContainer, NULL, NULL, cTryAgainAttempts, &fRetry);
1197 if (SUCCEEDED(hr))
1198 {
1199 break;
1200 }
1201 else
1202 {
1203 LogErrorId(hr, MSG_FAILED_LAYOUT_CONTAINER, pContainer->sczId, pContext->wzLayoutDirectory, pContainer->sczUnverifiedPath);
1204
1205 if (!fRetry)
1206 {
1207 ExitFunction();
1208 }
1209
1210 ++cTryAgainAttempts;
1211 pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedCacheProgress;
1212 pContainer->qwCommittedCacheProgress = 0;
1213 FinalizeContainerAcquisition(pContext, pContainer, FALSE);
1214 LogErrorId(hr, MSG_CACHE_RETRYING_CONTAINER, pContainer->sczId, NULL, NULL);
1215 }
1216 }
1217
1218 LExit:
1219 FinalizeContainerAcquisition(pContext, pContainer, SUCCEEDED(hr));
1220
1221 return hr;
1222 }
1223
1224 static HRESULT ApplyProcessPayload(
1225 __in BURN_CACHE_CONTEXT* pContext,
1226 __in_opt BURN_PACKAGE* pPackage,
1227 __in BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem,
1228 __in BOOL fVital
1229 )
1230 {
1231 HRESULT hr = S_OK;
1232 DWORD cTryAgainAttempts = 0;
1233 BOOL fRetry = FALSE;
1234 BOOTSTRAPPER_CACHEPACKAGENONVITALVALIDATIONFAILURE_ACTION action = BOOTSTRAPPER_CACHEPACKAGENONVITALVALIDATIONFAILURE_ACTION_NONE;
1235 BURN_PAYLOAD* pPayload = pPayloadGroupItem->pPayload;
1236
1237 Assert(pContext->pPayloads && pPackage || pContext->wzLayoutDirectory);
1238
1239 if (pPayload->pContainer && pContext->wzLayoutDirectory)
1240 {
1241 ExitFunction();
1242 }
1243
1244 hr = ApplyCacheVerifyContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem);
1245 if (SUCCEEDED(hr))
1246 {
1247 ExitFunction();
1248 }
1249 else if (pPackage && !pPackage->fAcquireOptionalSource && !fVital)
1250 {
1251 HRESULT hrResponse = BACallbackOnCachePackageNonVitalValidationFailure(pContext->pUX, pPackage->sczId, hr, &action);
1252 ExitOnRootFailure(hrResponse, "BA aborted cache package non-vital failure.");
1253
1254 if (BOOTSTRAPPER_CACHEPACKAGENONVITALVALIDATIONFAILURE_ACTION_ACQUIRE != action)
1255 {
1256 ExitFunction();
1257 }
1258
1259 pPackage->fAcquireOptionalSource = TRUE;
1260 }
1261
1262 for (;;)
1263 {
1264 fRetry = FALSE;
1265
1266 hr = ApplyAcquireContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem);
1267 LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_PAYLOAD, "Failed to acquire payload: %ls to working path: %ls", pPayload->sczKey, pPayload->sczUnverifiedPath);
1268
1269 hr = LayoutOrCacheContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem, cTryAgainAttempts, &fRetry);
1270 if (SUCCEEDED(hr))
1271 {
1272 break;
1273 }
1274 else
1275 {
1276 LogErrorId(hr, pContext->wzLayoutDirectory ? MSG_FAILED_LAYOUT_PAYLOAD : MSG_FAILED_CACHE_PAYLOAD, pPayload->sczKey, pContext->wzLayoutDirectory, pPayload->sczUnverifiedPath);
1277
1278 if (!fRetry)
1279 {
1280 ExitFunction();
1281 }
1282
1283 ++cTryAgainAttempts;
1284 pContext->qwSuccessfulCacheProgress -= pPayloadGroupItem->qwCommittedCacheProgress;
1285 pPayloadGroupItem->qwCommittedCacheProgress = 0;
1286 FinalizePayloadAcquisition(pContext, pPayload, FALSE);
1287 LogErrorId(hr, MSG_CACHE_RETRYING_PAYLOAD, pPayload->sczKey, NULL, NULL);
1288 }
1289 }
1290
1291 LExit:
1292 FinalizePayloadAcquisition(pContext, pPayload, SUCCEEDED(hr));
1293
1294 return hr;
1295 }
1296
1297 static HRESULT ApplyCacheVerifyContainerOrPayload(
1298 __in BURN_CACHE_CONTEXT* pContext,
1299 __in_opt BURN_CONTAINER* pContainer,
1300 __in_opt BURN_PACKAGE* pPackage,
1301 __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem
1302 )
1303 {
1304 AssertSz(pContainer || pPayloadGroupItem, "Must provide a container or a payload.");
1305
1306 HRESULT hr = S_OK;
1307 BURN_CACHE_PROGRESS_CONTEXT progress = { };
1308
1309 progress.pCacheContext = pContext;
1310 progress.pContainer = pContainer;
1311 progress.pPackage = pPackage;
1312 progress.pPayloadGroupItem = pPayloadGroupItem;
1313
1314 if (pContainer)
1315 {
1316 hr = CacheVerifyContainer(pContainer, pContext->wzLayoutDirectory, CacheMessageHandler, CacheProgressRoutine, &progress);
1317 }
1318 else if (!pContext->wzLayoutDirectory && INVALID_HANDLE_VALUE != pContext->hPipe)
1319 {
1320 hr = ElevationCacheVerifyPayload(pContext->hPipe, pPackage, pPayloadGroupItem->pPayload, CacheMessageHandler, CacheProgressRoutine, &progress);
1321 }
1322 else
1323 {
1324 hr = CacheVerifyPayload(pPayloadGroupItem->pPayload, pContext->wzLayoutDirectory ? pContext->wzLayoutDirectory : pPackage->sczCacheFolder, CacheMessageHandler, CacheProgressRoutine, &progress);
1325 }
1326
1327 return hr;
1328 }
1329
1330 static HRESULT ExtractContainer(
1331 __in BURN_CACHE_CONTEXT* pContext,
1332 __in BURN_CONTAINER* pContainer
1333 )
1334 {
1335 HRESULT hr = S_OK;
1336 BURN_CONTAINER_CONTEXT context = { };
1337 HANDLE hContainerHandle = INVALID_HANDLE_VALUE;
1338 LPWSTR sczStreamName = NULL;
1339 BURN_PAYLOAD* pExtract = NULL;
1340 BURN_CACHE_PROGRESS_CONTEXT progress = { };
1341
1342 progress.pCacheContext = pContext;
1343 progress.pContainer = pContainer;
1344 progress.type = BURN_CACHE_PROGRESS_TYPE_EXTRACT;
1345
1346 // If the container is actually attached, then it was planned to be acquired through hSourceEngineFile.
1347 if (pContainer->fActuallyAttached)
1348 {
1349 hContainerHandle = pContext->hSourceEngineFile;
1350 }
1351
1352 hr = ContainerOpen(&context, pContainer, hContainerHandle, pContainer->sczUnverifiedPath);
1353 ExitOnFailure(hr, "Failed to open container: %ls.", pContainer->sczId);
1354
1355 while (S_OK == (hr = ContainerNextStream(&context, &sczStreamName)))
1356 {
1357 BOOL fExtracted = FALSE;
1358
1359 hr = PayloadFindEmbeddedBySourcePath(pContainer->sdhPayloads, sczStreamName, &pExtract);
1360 if (E_NOTFOUND != hr)
1361 {
1362 ExitOnFailure(hr, "Failed to find embedded payload by source path: %ls container: %ls", sczStreamName, pContainer->sczId);
1363
1364 // Skip payloads that weren't planned or have already been cached.
1365 if (pExtract->sczUnverifiedPath && pExtract->cRemainingInstances)
1366 {
1367 progress.pPayload = pExtract;
1368
1369 hr = PreparePayloadDestinationPath(pExtract->sczUnverifiedPath);
1370 ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", pExtract->sczUnverifiedPath);
1371
1372 hr = BACallbackOnCachePayloadExtractBegin(pContext->pUX, pContainer->sczId, pExtract->sczKey);
1373 if (FAILED(hr))
1374 {
1375 BACallbackOnCachePayloadExtractComplete(pContext->pUX, pContainer->sczId, pExtract->sczKey, hr);
1376 ExitOnRootFailure(hr, "BA aborted cache payload extract begin.");
1377 }
1378
1379 // TODO: Send progress when extracting stream to file.
1380 hr = ContainerStreamToFile(&context, pExtract->sczUnverifiedPath);
1381 // Error handling happens after sending complete message to BA.
1382
1383 // If succeeded, send 100% complete here to make sure progress was sent to the BA.
1384 if (SUCCEEDED(hr))
1385 {
1386 hr = CompleteCacheProgress(&progress, pExtract->qwFileSize);
1387 }
1388
1389 BACallbackOnCachePayloadExtractComplete(pContext->pUX, pContainer->sczId, pExtract->sczKey, hr);
1390 ExitOnFailure(hr, "Failed to extract payload: %ls from container: %ls", sczStreamName, pContainer->sczId);
1391
1392 fExtracted = TRUE;
1393 }
1394 }
1395
1396 if (!fExtracted)
1397 {
1398 hr = ContainerSkipStream(&context);
1399 ExitOnFailure(hr, "Failed to skip the extraction of payload: %ls from container: %ls", sczStreamName, pContainer->sczId);
1400 }
1401 }
1402
1403 if (E_NOMOREITEMS == hr)
1404 {
1405 hr = S_OK;
1406 }
1407 ExitOnFailure(hr, "Failed to extract all payloads from container: %ls", pContainer->sczId);
1408
1409 LExit:
1410 ReleaseStr(sczStreamName);
1411 ContainerClose(&context);
1412
1413 return hr;
1414 }
1415
1416 static HRESULT LayoutBundle(
1417 __in BURN_CACHE_CONTEXT* pContext,
1418 __in_z LPCWSTR wzExecutableName,
1419 __in_z LPCWSTR wzUnverifiedPath,
1420 __in DWORD64 qwBundleSize
1421 )
1422 {
1423 HRESULT hr = S_OK;
1424 LPWSTR sczBundlePath = NULL;
1425 LPWSTR sczBundleDownloadUrl = NULL;
1426 LPWSTR sczDestinationPath = NULL;
1427 BOOL fPathEqual = FALSE;
1428 BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE;
1429 BURN_CACHE_PROGRESS_CONTEXT progress = { };
1430 BOOL fRetry = FALSE;
1431 BOOL fRetryAcquire = FALSE;
1432 BOOL fCanceledBegin = FALSE;
1433
1434 progress.pCacheContext = pContext;
1435
1436 hr = PathForCurrentProcess(&sczBundlePath, NULL);
1437 ExitOnFailure(hr, "Failed to get path to bundle to layout.");
1438
1439 hr = PathConcatRelativeToFullyQualifiedBase(pContext->wzLayoutDirectory, wzExecutableName, &sczDestinationPath);
1440 ExitOnFailure(hr, "Failed to concat layout path for bundle.");
1441
1442 // If the destination path is the currently running bundle, bail.
1443 hr = PathCompareCanonicalized(sczBundlePath, sczDestinationPath, &fPathEqual);
1444 ExitOnFailure(hr, "Failed to determine if layout bundle path was equivalent with current process path.");
1445
1446 if (fPathEqual && FileExistsEx(sczDestinationPath, NULL))
1447 {
1448 hr = BACallbackOnCacheContainerOrPayloadVerifyBegin(pContext->pUX, NULL, NULL);
1449 if (FAILED(hr))
1450 {
1451 BACallbackOnCacheContainerOrPayloadVerifyComplete(pContext->pUX, NULL, NULL, hr);
1452 ExitOnRootFailure(hr, "BA aborted cache payload verify begin.");
1453 }
1454
1455 progress.type = BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY;
1456 hr = CompleteCacheProgress(&progress, qwBundleSize);
1457
1458 BACallbackOnCacheContainerOrPayloadVerifyComplete(pContext->pUX, NULL, NULL, hr);
1459
1460 ExitFunction();
1461 }
1462
1463 do
1464 {
1465 hr = S_OK;
1466 fRetry = FALSE;
1467 progress.type = BURN_CACHE_PROGRESS_TYPE_ACQUIRE;
1468
1469 for (;;)
1470 {
1471 fRetryAcquire = FALSE;
1472 progress.fCancel = FALSE;
1473 fCanceledBegin = FALSE;
1474
1475 hr = BACallbackOnCacheAcquireBegin(pContext->pUX, NULL, NULL, &sczBundlePath, &sczBundleDownloadUrl, NULL, &cacheOperation);
1476
1477 if (FAILED(hr))
1478 {
1479 fCanceledBegin = TRUE;
1480 }
1481 else
1482 {
1483 hr = CopyPayload(&progress, pContext->hSourceEngineFile, sczBundlePath, wzUnverifiedPath);
1484 // Error handling happens after sending complete message to BA.
1485
1486 // If succeeded, send 100% complete here to make sure progress was sent to the BA.
1487 if (SUCCEEDED(hr))
1488 {
1489 hr = CompleteCacheProgress(&progress, qwBundleSize);
1490 }
1491 }
1492
1493 BACallbackOnCacheAcquireComplete(pContext->pUX, NULL, NULL, hr, &fRetryAcquire);
1494 if (fRetryAcquire)
1495 {
1496 continue;
1497 }
1498 else if (fCanceledBegin)
1499 {
1500 ExitOnRootFailure(hr, "BA aborted cache acquire begin.");
1501 }
1502
1503 ExitOnFailure(hr, "Failed to copy bundle from: '%ls' to: '%ls'", sczBundlePath, wzUnverifiedPath);
1504 break;
1505 }
1506
1507 do
1508 {
1509 fCanceledBegin = FALSE;
1510
1511 hr = BACallbackOnCacheVerifyBegin(pContext->pUX, NULL, NULL);
1512
1513 if (FAILED(hr))
1514 {
1515 fCanceledBegin = TRUE;
1516 }
1517 else
1518 {
1519 hr = CacheLayoutBundle(wzExecutableName, pContext->wzLayoutDirectory, wzUnverifiedPath, qwBundleSize, CacheMessageHandler, CacheProgressRoutine, &progress);
1520 }
1521
1522 BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE;
1523 BACallbackOnCacheVerifyComplete(pContext->pUX, NULL, NULL, hr, &action);
1524 if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action)
1525 {
1526 hr = S_FALSE; // retry verify.
1527 }
1528 else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action)
1529 {
1530 fRetry = TRUE; // go back and retry acquire.
1531 }
1532 else if (fCanceledBegin)
1533 {
1534 ExitOnRootFailure(hr, "BA aborted cache verify begin.");
1535 }
1536 } while (S_FALSE == hr);
1537
1538 if (fRetry)
1539 {
1540 pContext->qwSuccessfulCacheProgress -= qwBundleSize; // Acquire
1541 }
1542 } while (fRetry);
1543 LogExitOnFailure(hr, MSG_FAILED_LAYOUT_BUNDLE, "Failed to layout bundle: %ls to layout directory: %ls", sczBundlePath, pContext->wzLayoutDirectory);
1544
1545 LExit:
1546 ReleaseStr(sczDestinationPath);
1547 ReleaseStr(sczBundleDownloadUrl);
1548 ReleaseStr(sczBundlePath);
1549
1550 return hr;
1551 }
1552
1553 static HRESULT ApplyAcquireContainerOrPayload(
1554 __in BURN_CACHE_CONTEXT* pContext,
1555 __in_opt BURN_CONTAINER* pContainer,
1556 __in_opt BURN_PACKAGE* pPackage,
1557 __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem
1558 )
1559 {
1560 AssertSz(pContainer || pPayloadGroupItem, "Must provide a container or a payload.");
1561
1562 HRESULT hr = S_OK;
1563 BURN_CACHE_PROGRESS_CONTEXT progress = { };
1564 BOOL fRetry = FALSE;
1565
1566 progress.pCacheContext = pContext;
1567 progress.type = BURN_CACHE_PROGRESS_TYPE_ACQUIRE;
1568 progress.pContainer = pContainer;
1569 progress.pPackage = pPackage;
1570 progress.pPayloadGroupItem = pPayloadGroupItem;
1571
1572 do
1573 {
1574 hr = AcquireContainerOrPayload(&progress, &fRetry);
1575
1576 if (fRetry)
1577 {
1578 LogErrorId(hr, pContainer ? MSG_APPLY_RETRYING_ACQUIRE_CONTAINER : MSG_APPLY_RETRYING_ACQUIRE_PAYLOAD, pContainer ? pContainer->sczId : pPayloadGroupItem->pPayload->sczKey, NULL, NULL);
1579 hr = S_OK;
1580 }
1581
1582 ExitOnFailure(hr, "Failed to acquire %hs: %ls", pContainer ? "container" : "payload", pContainer ? pContainer->sczId : pPayloadGroupItem->pPayload->sczKey);
1583 } while (fRetry);
1584
1585 LExit:
1586 return hr;
1587 }
1588
1589 static HRESULT AcquireContainerOrPayload(
1590 __in BURN_CACHE_PROGRESS_CONTEXT* pProgress,
1591 __out BOOL* pfRetry
1592 )
1593 {
1594 BURN_CACHE_CONTEXT* pContext = pProgress->pCacheContext;
1595 BURN_CONTAINER* pContainer = pProgress->pContainer;
1596 BURN_PACKAGE* pPackage = pProgress->pPackage;
1597 BURN_PAYLOAD* pPayload = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload : NULL;
1598 AssertSz(pContainer || pPayload, "Must provide a container or a payload.");
1599
1600 HRESULT hr = S_OK;
1601 BOOL fPathEqual = FALSE;
1602 LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL;
1603 LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL;
1604 LPCWSTR wzPayloadContainerId = pPayload && pPayload->pContainer ? pPayload->pContainer->sczId : NULL;
1605 LPCWSTR wzDestinationPath = pContainer ? pContainer->sczUnverifiedPath: pPayload->sczUnverifiedPath;
1606 LPCWSTR wzRelativePath = pContainer ? pContainer->sczFilePath : pPayload->sczFilePath;
1607 DWORD dwChosenSearchPath = 0;
1608 DWORD dwDestinationSearchPath = 0;
1609 BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE;
1610 BOOTSTRAPPER_CACHE_RESOLVE_OPERATION resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_NONE;
1611 LPWSTR* pwzDownloadUrl = pContainer ? &pContainer->downloadSource.sczUrl : &pPayload->downloadSource.sczUrl;
1612 LPWSTR* pwzSourcePath = pContainer ? &pContainer->sczSourcePath : &pPayload->sczSourcePath;
1613 BOOL fFoundLocal = FALSE;
1614 BOOL fPreferExtract = FALSE;
1615 DWORD64 qwFileSize = 0;
1616 BOOL fMinimumFileSize = FALSE;
1617 BOOL fEqual = FALSE;
1618
1619 if (pContainer)
1620 {
1621 if (pContainer->fAttached)
1622 {
1623 fMinimumFileSize = TRUE;
1624 qwFileSize = pContainer->qwAttachedOffset + pContainer->qwFileSize;
1625 }
1626 else
1627 {
1628 qwFileSize = pContainer->qwFileSize;
1629 }
1630 }
1631 else if (BURN_PAYLOAD_VERIFICATION_HASH == pPayload->verification || BURN_PAYLOAD_VERIFICATION_UPDATE_BUNDLE == pPayload->verification)
1632 {
1633 qwFileSize = pPayload->qwFileSize;
1634 }
1635
1636 pContext->cSearchPaths = 0;
1637 *pfRetry = FALSE;
1638 pProgress->fCancel = FALSE;
1639
1640 hr = BACallbackOnCacheAcquireBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pwzSourcePath, pwzDownloadUrl, wzPayloadContainerId, &cacheOperation);
1641 ExitOnRootFailure(hr, "BA aborted cache acquire begin.");
1642
1643 // Skip the Resolving event and probing local paths if the BA already knew it wanted to download or extract.
1644 if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD != cacheOperation &&
1645 BOOTSTRAPPER_CACHE_OPERATION_EXTRACT != cacheOperation)
1646 {
1647 do
1648 {
1649 fFoundLocal = FALSE;
1650 fPreferExtract = FALSE;
1651 resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_NONE;
1652 dwChosenSearchPath = 0;
1653 dwDestinationSearchPath = 0;
1654
1655 hr = CacheGetLocalSourcePaths(wzRelativePath, *pwzSourcePath, wzDestinationPath, pContext->wzLayoutDirectory, pContext->pCache, pContext->pVariables, &pContext->rgSearchPaths, &pContext->cSearchPaths, &dwChosenSearchPath, &dwDestinationSearchPath);
1656 ExitOnFailure(hr, "Failed to search local source.");
1657
1658 if (wzPayloadContainerId)
1659 {
1660 // When a payload comes from a container, the container has the highest chance of being correct.
1661 // But we want to avoid extracting the container multiple times.
1662 // So only consider the destination path, which means the container was already extracted.
1663 if (!pPayload->fFailedVerificationFromAcquisition && IsValidLocalFile(pContext->rgSearchPaths[dwDestinationSearchPath], qwFileSize, fMinimumFileSize))
1664 {
1665 fFoundLocal = TRUE;
1666 dwChosenSearchPath = dwDestinationSearchPath;
1667 }
1668 else // don't prefer the container if it was already extracted.
1669 {
1670 fPreferExtract = !pPayload->pContainer->fExtracted;
1671 }
1672 }
1673
1674 if (!fFoundLocal)
1675 {
1676 BOOL fFailedVerificationFromAcquisition = pContainer ? pContainer->fFailedVerificationFromAcquisition : pPayload->fFailedVerificationFromAcquisition;
1677 LPCWSTR wzFailedLocalAcquisitionPath = pContainer ? pContainer->sczFailedLocalAcquisitionPath : pPayload->sczFailedLocalAcquisitionPath;
1678
1679 for (DWORD i = 0; i < pContext->cSearchPaths; ++i)
1680 {
1681 // If the file failed verification from acquisition then certain paths should not be considered.
1682 if (fFailedVerificationFromAcquisition)
1683 {
1684 if (i == dwDestinationSearchPath)
1685 {
1686 continue;
1687 }
1688
1689 if (wzFailedLocalAcquisitionPath)
1690 {
1691 hr = PathCompareCanonicalized(pContext->rgSearchPaths[i], wzFailedLocalAcquisitionPath, &fEqual);
1692 ExitOnFailure(hr, "Failed to compare '%ls' to '%ls'.", pContext->rgSearchPaths[i], wzFailedLocalAcquisitionPath);
1693
1694 if (fEqual)
1695 {
1696 continue;
1697 }
1698 }
1699 }
1700
1701 // If the file exists locally with the correct size, choose it.
1702 if (IsValidLocalFile(pContext->rgSearchPaths[i], qwFileSize, fMinimumFileSize))
1703 {
1704 dwChosenSearchPath = i;
1705
1706 fFoundLocal = TRUE;
1707 break;
1708 }
1709 }
1710 }
1711
1712 if (BOOTSTRAPPER_CACHE_OPERATION_COPY == cacheOperation)
1713 {
1714 if (fFoundLocal)
1715 {
1716 resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_LOCAL;
1717 }
1718 }
1719 else
1720 {
1721 if (fPreferExtract) // the file comes from a container which hasn't been extracted yet, so extract it.
1722 {
1723 resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER;
1724 }
1725 else if (fFoundLocal) // the file exists locally, so copy it.
1726 {
1727 resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_LOCAL;
1728 }
1729 else if (*pwzDownloadUrl && **pwzDownloadUrl)
1730 {
1731 resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD;
1732 }
1733 else if (wzPayloadContainerId)
1734 {
1735 resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER;
1736 }
1737 }
1738
1739 // Let the BA have a chance to override the source.
1740 hr = BACallbackOnCacheAcquireResolving(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pContext->rgSearchPaths, pContext->cSearchPaths, fFoundLocal, &dwChosenSearchPath, pwzDownloadUrl, wzPayloadContainerId, &resolveOperation);
1741 ExitOnRootFailure(hr, "BA aborted cache acquire resolving.");
1742
1743 switch (resolveOperation)
1744 {
1745 case BOOTSTRAPPER_CACHE_RESOLVE_LOCAL:
1746 cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_COPY;
1747 break;
1748 case BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD:
1749 cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD;
1750 break;
1751 case BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER:
1752 cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_EXTRACT;
1753 break;
1754 case BOOTSTRAPPER_CACHE_RESOLVE_RETRY:
1755 pContext->cSearchPathsMax = max(pContext->cSearchPaths, pContext->cSearchPathsMax);
1756 break;
1757 }
1758 } while (BOOTSTRAPPER_CACHE_RESOLVE_RETRY == resolveOperation);
1759 }
1760
1761 switch (cacheOperation)
1762 {
1763 case BOOTSTRAPPER_CACHE_OPERATION_COPY:
1764 // If the source path and destination path are different, do the copy (otherwise there's no point).
1765 hr = PathCompareCanonicalized(pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath, &fPathEqual);
1766 ExitOnFailure(hr, "Failed to determine if payload paths were equivalent, source: %ls, destination: %ls.", pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath);
1767
1768 if (!fPathEqual)
1769 {
1770 hr = CopyPayload(pProgress, INVALID_HANDLE_VALUE, pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath);
1771 ExitOnFailure(hr, "Failed to copy payload: %ls", wzPayloadId);
1772
1773 // Store the source path so it can be used as the LastUsedFolder if it passes verification.
1774 pContext->sczLocalAcquisitionSourcePath = pContext->rgSearchPaths[dwChosenSearchPath];
1775 pContext->rgSearchPaths[dwChosenSearchPath] = NULL;
1776 }
1777
1778 break;
1779 case BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD:
1780 hr = DownloadPayload(pProgress, wzDestinationPath);
1781 ExitOnFailure(hr, "Failed to download payload: %ls", wzPayloadId);
1782
1783 break;
1784 case BOOTSTRAPPER_CACHE_OPERATION_EXTRACT:
1785 Assert(pPayload && pPayload->pContainer);
1786
1787 hr = ApplyExtractContainer(pContext, pPayload->pContainer);
1788 ExitOnFailure(hr, "Failed to extract container for payload: %ls", wzPayloadId);
1789
1790 break;
1791 default:
1792 LogExitWithRootFailure(hr, E_FILENOTFOUND, MSG_RESOLVE_SOURCE_FAILED, "Failed to resolve source, payload: %ls, package: %ls, container: %ls", wzPayloadId ? wzPayloadId : L"n/a", pPackage ? pPackage->sczId : L"n/a", pContainer ? pContainer->sczId : L"n/a");
1793 }
1794
1795 // Send 100% complete here. This is sometimes the only progress sent to the BA.
1796 hr = CompleteCacheProgress(pProgress, pContainer ? pContainer->qwFileSize : pPayload->qwFileSize);
1797
1798 LExit:
1799 if (BOOTSTRAPPER_CACHE_OPERATION_EXTRACT == cacheOperation)
1800 {
1801 // If this was the first extraction attempt and it failed
1802 // but there was a different method of acquisition available then recommend retrying.
1803 if (FAILED(hr) && !pPayload->pContainer->fExtracted &&
1804 (fFoundLocal || pPayload->downloadSource.sczUrl && *pPayload->downloadSource.sczUrl))
1805 {
1806 *pfRetry = TRUE;
1807 }
1808 pPayload->pContainer->fExtracted = TRUE;
1809 }
1810 BACallbackOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, pfRetry);
1811
1812 pContext->cSearchPathsMax = max(pContext->cSearchPaths, pContext->cSearchPathsMax);
1813
1814 return hr;
1815 }
1816
1817 static BOOL IsValidLocalFile(
1818 __in_z LPCWSTR wzFilePath,
1819 __in DWORD64 qwFileSize,
1820 __in BOOL fMinimumFileSize
1821 )
1822 {
1823 LONGLONG llFileSize = 0;
1824
1825 if (!qwFileSize)
1826 {
1827 return FileExistsEx(wzFilePath, NULL);
1828 }
1829 else
1830 {
1831 return SUCCEEDED(FileSize(wzFilePath, &llFileSize)) &&
1832 (static_cast<DWORD64>(llFileSize) == qwFileSize ||
1833 fMinimumFileSize && static_cast<DWORD64>(llFileSize) > qwFileSize);
1834 }
1835 }
1836
1837 static HRESULT LayoutOrCacheContainerOrPayload(
1838 __in BURN_CACHE_CONTEXT* pContext,
1839 __in_opt BURN_CONTAINER* pContainer,
1840 __in_opt BURN_PACKAGE* pPackage,
1841 __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem,
1842 __in DWORD cTryAgainAttempts,
1843 __out BOOL* pfRetry
1844 )
1845 {
1846 HRESULT hr = S_OK;
1847 BURN_PAYLOAD* pPayload = pPayloadGroupItem ? pPayloadGroupItem->pPayload : NULL;
1848 LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : L"";
1849 LPCWSTR wzUnverifiedPath = pContainer ? pContainer->sczUnverifiedPath : pPayload->sczUnverifiedPath;
1850 LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : L"";
1851 BOOL fCanAffectRegistration = FALSE;
1852 BURN_CACHE_PROGRESS_CONTEXT progress = { };
1853 BOOL fMove = !pPayload || 1 == pPayload->cRemainingInstances;
1854 BOOL fCanceledBegin = FALSE;
1855
1856 if (pContainer)
1857 {
1858 Assert(!pPayloadGroupItem);
1859 }
1860 else
1861 {
1862 Assert(pPayload);
1863 AssertSz(0 < pPayload->cRemainingInstances, "Laying out payload more times than planned.");
1864 AssertSz(!pPayloadGroupItem->fCached, "Laying out payload group item that was already cached.");
1865 }
1866
1867 if (!pContext->wzLayoutDirectory)
1868 {
1869 Assert(!pContainer);
1870 Assert(pPackage);
1871
1872 fCanAffectRegistration = pPackage->fCanAffectRegistration;
1873 }
1874
1875 *pfRetry = FALSE;
1876 progress.pCacheContext = pContext;
1877 progress.pContainer = pContainer;
1878 progress.pPackage = pPackage;
1879 progress.pPayloadGroupItem = pPayloadGroupItem;
1880
1881 do
1882 {
1883 fCanceledBegin = FALSE;
1884
1885 hr = BACallbackOnCacheVerifyBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId);
1886
1887 if (FAILED(hr))
1888 {
1889 fCanceledBegin = TRUE;
1890 }
1891 else
1892 {
1893 if (pContext->wzLayoutDirectory) // layout the container or payload.
1894 {
1895 if (pContainer)
1896 {
1897 hr = CacheLayoutContainer(pContainer, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress);
1898 }
1899 else
1900 {
1901 hr = CacheLayoutPayload(pPayload, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress);
1902 }
1903 }
1904 else if (INVALID_HANDLE_VALUE != pContext->hPipe) // pass the decision off to the elevated process.
1905 {
1906 hr = ElevationCacheCompletePayload(pContext->hPipe, pPackage, pPayload, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress);
1907 }
1908 else // complete the payload.
1909 {
1910 hr = CacheCompletePayload(pContext->pCache, pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress);
1911 }
1912 }
1913
1914 if (SUCCEEDED(hr) && fCanAffectRegistration)
1915 {
1916 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
1917 }
1918
1919 BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = FAILED(hr) && !fCanceledBegin && cTryAgainAttempts < BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS ? BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION : BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE;
1920 BACallbackOnCacheVerifyComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &action);
1921 if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action)
1922 {
1923 hr = S_FALSE; // retry verify.
1924 }
1925 else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action)
1926 {
1927 *pfRetry = TRUE; // go back and retry acquire.
1928 }
1929 else if (fCanceledBegin)
1930 {
1931 ExitOnRootFailure(hr, "BA aborted cache verify begin.");
1932 }
1933 } while (S_FALSE == hr);
1934
1935 if (SUCCEEDED(hr) && pPayloadGroupItem)
1936 {
1937 pPayload->cRemainingInstances -= 1;
1938 pPayloadGroupItem->fCached = TRUE;
1939 }
1940
1941 LExit:
1942 return hr;
1943 }
1944
1945 static HRESULT PreparePayloadDestinationPath(
1946 __in_z LPCWSTR wzDestinationPath
1947 )
1948 {
1949 HRESULT hr = S_OK;
1950 DWORD dwFileAttributes = 0;
1951
1952 // If the destination file already exists, clear the readonly bit to avoid E_ACCESSDENIED.
1953 if (FileExistsEx(wzDestinationPath, &dwFileAttributes))
1954 {
1955 if (FILE_ATTRIBUTE_READONLY & dwFileAttributes)
1956 {
1957 dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;
1958 if (!::SetFileAttributes(wzDestinationPath, dwFileAttributes))
1959 {
1960 ExitWithPathLastError(hr, "Failed to clear readonly bit on payload destination path: %ls", wzDestinationPath);
1961 }
1962 }
1963 }
1964
1965 LExit:
1966 return hr;
1967 }
1968
1969 static HRESULT CopyPayload(
1970 __in BURN_CACHE_PROGRESS_CONTEXT* pProgress,
1971 __in HANDLE hSourceFile,
1972 __in_z LPCWSTR wzSourcePath,
1973 __in_z LPCWSTR wzDestinationPath
1974 )
1975 {
1976 HRESULT hr = S_OK;
1977 LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L"";
1978 LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : L"";
1979 HANDLE hDestinationFile = INVALID_HANDLE_VALUE;
1980 HANDLE hSourceOpenedFile = INVALID_HANDLE_VALUE;
1981
1982 DWORD dwLogId = pProgress->pContainer ? MSG_ACQUIRE_CONTAINER : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD;
1983 LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "copy", wzSourcePath);
1984
1985 hr = PreparePayloadDestinationPath(wzDestinationPath);
1986 ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", wzDestinationPath);
1987
1988 if (INVALID_HANDLE_VALUE == hSourceFile)
1989 {
1990 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);
1991 if (INVALID_HANDLE_VALUE == hSourceOpenedFile)
1992 {
1993 ExitWithLastError(hr, "Failed to open source file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath);
1994 }
1995
1996 hSourceFile = hSourceOpenedFile;
1997 }
1998 else
1999 {
2000 hr = FileSetPointer(hSourceFile, 0, NULL, FILE_BEGIN);
2001 ExitOnRootFailure(hr, "Failed to read from start of source file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath);
2002 }
2003
2004 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);
2005 if (INVALID_HANDLE_VALUE == hDestinationFile)
2006 {
2007 ExitWithLastError(hr, "Failed to open destination file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath);
2008 }
2009
2010 hr = FileCopyUsingHandlesWithProgress(hSourceFile, hDestinationFile, 0, CacheProgressRoutine, pProgress);
2011 if (FAILED(hr))
2012 {
2013 if (pProgress->fCancel)
2014 {
2015 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
2016 ExitOnRootFailure(hr, "BA aborted copy of payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath);
2017 }
2018 else
2019 {
2020 ExitOnRootFailure(hr, "Failed attempt to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath);
2021 }
2022 }
2023
2024 LExit:
2025 ReleaseFileHandle(hDestinationFile);
2026 ReleaseFileHandle(hSourceOpenedFile);
2027
2028 return hr;
2029 }
2030
2031 static HRESULT DownloadPayload(
2032 __in BURN_CACHE_PROGRESS_CONTEXT* pProgress,
2033 __in_z LPCWSTR wzDestinationPath
2034 )
2035 {
2036 HRESULT hr = S_OK;
2037 LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L"";
2038 LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : L"";
2039 DOWNLOAD_SOURCE* pDownloadSource = pProgress->pContainer ? &pProgress->pContainer->downloadSource : &pProgress->pPayloadGroupItem->pPayload->downloadSource;
2040 DWORD64 qwDownloadSize = pProgress->pContainer ? pProgress->pContainer->qwFileSize : pProgress->pPayloadGroupItem->pPayload->qwFileSize;
2041 DOWNLOAD_CACHE_CALLBACK cacheCallback = { };
2042 DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { };
2043 APPLY_AUTHENTICATION_REQUIRED_DATA authenticationData = { };
2044
2045 DWORD dwLogId = pProgress->pContainer ? MSG_ACQUIRE_CONTAINER : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD;
2046 LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "download", pDownloadSource->sczUrl);
2047
2048 hr = PreparePayloadDestinationPath(wzDestinationPath);
2049 ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", wzDestinationPath);
2050
2051 cacheCallback.pfnProgress = CacheProgressRoutine;
2052 cacheCallback.pfnCancel = NULL; // TODO: set this
2053 cacheCallback.pv = pProgress;
2054
2055 authenticationData.pUX = pProgress->pCacheContext->pUX;
2056 authenticationData.wzPackageOrContainerId = wzPackageOrContainerId;
2057 authenticationData.wzPayloadId = wzPayloadId;
2058 authenticationCallback.pv = static_cast<LPVOID>(&authenticationData);
2059 authenticationCallback.pfnAuthenticate = &AuthenticationRequired;
2060
2061 hr = DownloadUrl(pDownloadSource, qwDownloadSize, wzDestinationPath, &cacheCallback, &authenticationCallback);
2062 ExitOnFailure(hr, "Failed attempt to download URL: '%ls' to: '%ls'", pDownloadSource->sczUrl, wzDestinationPath);
2063
2064 LExit:
2065 return hr;
2066 }
2067
2068 static HRESULT WINAPI AuthenticationRequired(
2069 __in LPVOID pData,
2070 __in HINTERNET hUrl,
2071 __in long lHttpCode,
2072 __out BOOL* pfRetrySend,
2073 __out BOOL* pfRetry
2074 )
2075 {
2076 Assert(401 == lHttpCode || 407 == lHttpCode);
2077
2078 HRESULT hr = S_OK;
2079 DWORD er = ERROR_SUCCESS;
2080 BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY;
2081 LPWSTR sczError = NULL;
2082 int nResult = IDNOACTION;
2083
2084 *pfRetrySend = FALSE;
2085 *pfRetry = FALSE;
2086
2087 hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL);
2088 ExitOnFailure(hr, "Failed to allocation error string.");
2089
2090 APPLY_AUTHENTICATION_REQUIRED_DATA* authenticationData = reinterpret_cast<APPLY_AUTHENTICATION_REQUIRED_DATA*>(pData);
2091
2092 BACallbackOnError(authenticationData->pUX, errorType, authenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYCANCEL, 0, NULL, &nResult); // ignore return value;
2093 nResult = BootstrapperApplicationCheckExecuteResult(authenticationData->pUX, FALSE, BURN_MB_RETRYTRYAGAIN, nResult);
2094 if (IDTRYAGAIN == nResult && authenticationData->pUX->hwndApply)
2095 {
2096 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);
2097 if (ERROR_SUCCESS == er || ERROR_CANCELLED == er)
2098 {
2099 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
2100 }
2101 else if (ERROR_INTERNET_FORCE_RETRY == er)
2102 {
2103 *pfRetrySend = TRUE;
2104 hr = S_OK;
2105 }
2106 else
2107 {
2108 hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
2109 }
2110 }
2111 else if (IDRETRY == nResult)
2112 {
2113 *pfRetry = TRUE;
2114 hr = S_OK;
2115 }
2116 else
2117 {
2118 hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
2119 }
2120
2121 LExit:
2122 ReleaseStr(sczError);
2123
2124 return hr;
2125 }
2126
2127 static HRESULT CALLBACK CacheMessageHandler(
2128 __in BURN_CACHE_MESSAGE* pMessage,
2129 __in LPVOID pvContext
2130 )
2131 {
2132 HRESULT hr = S_OK;
2133 BURN_CACHE_PROGRESS_CONTEXT* pProgress = static_cast<BURN_CACHE_PROGRESS_CONTEXT*>(pvContext);
2134 LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL;
2135 LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : pProgress->pPayload ? pProgress->pPayload->sczKey : NULL;
2136
2137 switch (pMessage->type)
2138 {
2139 case BURN_CACHE_MESSAGE_BEGIN:
2140 switch (pMessage->begin.cacheStep)
2141 {
2142 case BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE:
2143 pProgress->type = BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY;
2144 hr = BACallbackOnCacheContainerOrPayloadVerifyBegin(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId);
2145 break;
2146 case BURN_CACHE_STEP_HASH_TO_SKIP_VERIFY:
2147 pProgress->type = BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY;
2148 break;
2149 case BURN_CACHE_STEP_STAGE:
2150 pProgress->type = BURN_CACHE_PROGRESS_TYPE_STAGE;
2151 break;
2152 case BURN_CACHE_STEP_HASH:
2153 pProgress->type = BURN_CACHE_PROGRESS_TYPE_HASH;
2154 break;
2155 case BURN_CACHE_STEP_FINALIZE:
2156 pProgress->type = BURN_CACHE_PROGRESS_TYPE_FINALIZE;
2157 break;
2158 }
2159 break;
2160 case BURN_CACHE_MESSAGE_SUCCESS:
2161 hr = CompleteCacheProgress(pProgress, pMessage->success.qwFileSize);
2162 break;
2163 case BURN_CACHE_MESSAGE_COMPLETE:
2164 switch (pProgress->type)
2165 {
2166 case BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY:
2167 hr = BACallbackOnCacheContainerOrPayloadVerifyComplete(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, hr);
2168 break;
2169 }
2170 case BURN_CACHE_MESSAGE_FAILURE:
2171 switch (pMessage->failure.cacheStep)
2172 {
2173 case BURN_CACHE_STEP_HASH:
2174 if (pProgress->pContainer)
2175 {
2176 LogStringLine(REPORT_DEBUG, "Verification failed on container: %ls", pProgress->pContainer->sczId);
2177 pProgress->pContainer->fFailedVerificationFromAcquisition = TRUE;
2178 }
2179 else if (pProgress->pPayloadGroupItem)
2180 {
2181 LogStringLine(REPORT_DEBUG, "Verification failed on payload group item: %ls", pProgress->pPayloadGroupItem->pPayload->sczKey);
2182 pProgress->pPayloadGroupItem->pPayload->fFailedVerificationFromAcquisition = TRUE;
2183 }
2184 else if (pProgress->pPayload)
2185 {
2186 LogStringLine(REPORT_DEBUG, "Verification failed on payload: %ls", pProgress->pPayload->sczKey);
2187 pProgress->pPayload->fFailedVerificationFromAcquisition = TRUE;
2188 }
2189 else
2190 {
2191 LogStringLine(REPORT_DEBUG, "Verification failed on unknown item");
2192 }
2193 break;
2194 }
2195 }
2196
2197 return hr;
2198 }
2199
2200 static HRESULT CompleteCacheProgress(
2201 __in BURN_CACHE_PROGRESS_CONTEXT* pContext,
2202 __in DWORD64 qwFileSize
2203 )
2204 {
2205 HRESULT hr = S_OK;
2206 LARGE_INTEGER liContainerOrPayloadSize = { };
2207 LARGE_INTEGER liZero = { };
2208 DWORD dwResult = 0;
2209 DWORD64 qwCommitSize = 0;
2210
2211 liContainerOrPayloadSize.QuadPart = qwFileSize;
2212
2213 // Need to commit the steps that were skipped.
2214 if (BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY == pContext->type || BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY == pContext->type)
2215 {
2216 Assert(!pContext->pPayload);
2217
2218 qwCommitSize = qwFileSize * (pContext->pCacheContext->wzLayoutDirectory ? 2 : 3); // Acquire (+ Stage) + Hash + Finalize - 1 (that's added later)
2219
2220 pContext->pCacheContext->qwSuccessfulCacheProgress += qwCommitSize;
2221
2222 if (pContext->pContainer)
2223 {
2224 pContext->pContainer->qwCommittedCacheProgress += qwCommitSize;
2225 }
2226 else if (pContext->pPayloadGroupItem)
2227 {
2228 pContext->pPayloadGroupItem->qwCommittedCacheProgress += qwCommitSize;
2229 }
2230 }
2231
2232 dwResult = CacheProgressRoutine(liContainerOrPayloadSize, liContainerOrPayloadSize, liZero, liZero, 0, 0, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, pContext);
2233
2234 if (PROGRESS_CONTINUE == dwResult)
2235 {
2236 pContext->pCacheContext->qwSuccessfulCacheProgress += qwFileSize;
2237
2238 if (pContext->pPayload)
2239 {
2240 pContext->pContainer->qwCommittedExtractProgress += qwFileSize;
2241 }
2242 else if (pContext->pContainer)
2243 {
2244 pContext->pContainer->qwCommittedCacheProgress += qwFileSize;
2245 }
2246 else if (pContext->pPayloadGroupItem)
2247 {
2248 pContext->pPayloadGroupItem->qwCommittedCacheProgress += qwFileSize;
2249 }
2250
2251 if (BURN_CACHE_PROGRESS_TYPE_FINALIZE == pContext->type && pContext->pCacheContext->sczLocalAcquisitionSourcePath)
2252 {
2253 // We successfully copied from a source location, set that as the last used source.
2254 CacheSetLastUsedSource(pContext->pCacheContext->pVariables, pContext->pCacheContext->sczLocalAcquisitionSourcePath, pContext->pContainer ? pContext->pContainer->sczFilePath : pContext->pPayloadGroupItem->pPayload->sczFilePath);
2255 ReleaseNullStr(pContext->pCacheContext->sczLocalAcquisitionSourcePath);
2256 }
2257 }
2258 else if (PROGRESS_CANCEL == dwResult)
2259 {
2260 if (pContext->fCancel)
2261 {
2262 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
2263 }
2264 else
2265 {
2266 hr = pContext->hrError;
2267 }
2268
2269 if (qwCommitSize)
2270 {
2271 pContext->pCacheContext->qwSuccessfulCacheProgress -= qwCommitSize;
2272
2273 if (pContext->pContainer)
2274 {
2275 pContext->pContainer->qwCommittedCacheProgress -= qwCommitSize;
2276 }
2277 else if (pContext->pPayloadGroupItem)
2278 {
2279 pContext->pPayloadGroupItem->qwCommittedCacheProgress -= qwCommitSize;
2280 }
2281 }
2282 }
2283
2284 return hr;
2285 }
2286
2287 static DWORD CALLBACK CacheProgressRoutine(
2288 __in LARGE_INTEGER TotalFileSize,
2289 __in LARGE_INTEGER TotalBytesTransferred,
2290 __in LARGE_INTEGER /*StreamSize*/,
2291 __in LARGE_INTEGER /*StreamBytesTransferred*/,
2292 __in DWORD /*dwStreamNumber*/,
2293 __in DWORD /*dwCallbackReason*/,
2294 __in HANDLE /*hSourceFile*/,
2295 __in HANDLE /*hDestinationFile*/,
2296 __in_opt LPVOID lpData
2297 )
2298 {
2299 HRESULT hr = S_OK;
2300 DWORD dwResult = PROGRESS_CONTINUE;
2301 BURN_CACHE_PROGRESS_CONTEXT* pProgress = static_cast<BURN_CACHE_PROGRESS_CONTEXT*>(lpData);
2302 LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL;
2303 LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : pProgress->pPayload ? pProgress->pPayload->sczKey : NULL;
2304 DWORD64 qwCacheProgress = pProgress->pCacheContext->qwSuccessfulCacheProgress + TotalBytesTransferred.QuadPart;
2305 if (qwCacheProgress > pProgress->pCacheContext->qwTotalCacheSize)
2306 {
2307 //AssertSz(FALSE, "Apply has cached more than Plan envisioned.");
2308 qwCacheProgress = pProgress->pCacheContext->qwTotalCacheSize;
2309 }
2310 DWORD dwOverallPercentage = pProgress->pCacheContext->qwTotalCacheSize ? static_cast<DWORD>(qwCacheProgress * 100 / pProgress->pCacheContext->qwTotalCacheSize) : 0;
2311
2312 switch (pProgress->type)
2313 {
2314 case BURN_CACHE_PROGRESS_TYPE_ACQUIRE:
2315 hr = BACallbackOnCacheAcquireProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage);
2316 ExitOnRootFailure(hr, "BA aborted acquire of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId);
2317 break;
2318 case BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY:
2319 hr = BACallbackOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_HASH);
2320 ExitOnRootFailure(hr, "BA aborted payload verify step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId);
2321 break;
2322 case BURN_CACHE_PROGRESS_TYPE_STAGE:
2323 hr = BACallbackOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_STAGE);
2324 ExitOnRootFailure(hr, "BA aborted stage step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId);
2325 break;
2326 case BURN_CACHE_PROGRESS_TYPE_HASH:
2327 hr = BACallbackOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_HASH);
2328 ExitOnRootFailure(hr, "BA aborted hash step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId);
2329 break;
2330 case BURN_CACHE_PROGRESS_TYPE_FINALIZE:
2331 hr = BACallbackOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_FINALIZE);
2332 ExitOnRootFailure(hr, "BA aborted finalize step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId);
2333 break;
2334 case BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY:
2335 hr = BACallbackOnCacheContainerOrPayloadVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage);
2336 ExitOnRootFailure(hr, "BA aborted container or payload verify: %ls", wzPayloadId);
2337 break;
2338 case BURN_CACHE_PROGRESS_TYPE_EXTRACT:
2339 hr = BACallbackOnCachePayloadExtractProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage);
2340 ExitOnRootFailure(hr, "BA aborted extract container: %ls, payload: %ls", wzPackageOrContainerId, wzPayloadId);
2341 break;
2342 }
2343
2344 LExit:
2345 if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr)
2346 {
2347 dwResult = PROGRESS_CANCEL;
2348 pProgress->fCancel = TRUE;
2349 }
2350 else if (FAILED(hr))
2351 {
2352 dwResult = PROGRESS_CANCEL;
2353 pProgress->hrError = hr;
2354 }
2355 else
2356 {
2357 dwResult = PROGRESS_CONTINUE;
2358 }
2359
2360 return dwResult;
2361 }
2362
2363 static void DoRollbackCache(
2364 __in BURN_USER_EXPERIENCE* /*pUX*/,
2365 __in BURN_PLAN* pPlan,
2366 __in HANDLE hPipe,
2367 __in DWORD dwCheckpoint
2368 )
2369 {
2370 HRESULT hr = S_OK;
2371 BURN_PACKAGE* pPackage = NULL;
2372 DWORD dwLastCheckpoint = 0;
2373
2374 // Rollback cache actions.
2375 for (DWORD i = pPlan->cRollbackCacheActions; i > 0; --i)
2376 {
2377 BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i - 1];
2378
2379 switch (pRollbackCacheAction->type)
2380 {
2381 case BURN_CACHE_ACTION_TYPE_CHECKPOINT:
2382 dwLastCheckpoint = pRollbackCacheAction->checkpoint.dwId;
2383 break;
2384
2385 case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE:
2386 pPackage = pRollbackCacheAction->rollbackPackage.pPackage;
2387
2388 // If the package was executed then it's up to ApplyExecute to rollback its cache.
2389 if (!pPackage->fReachedExecution)
2390 {
2391 if (!pPackage->fCached) // only rollback when it wasn't already cached.
2392 {
2393 if (dwLastCheckpoint <= dwCheckpoint) // only rollback when it was attempted to be cached.
2394 {
2395 hr = CleanPackage(pPlan->pCache, hPipe, pPackage);
2396 }
2397 }
2398 else if (pPackage->fCanAffectRegistration)
2399 {
2400 // Don't let this already cached package cause the registration to be kept if the bundle failed and wasn't already registered.
2401 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
2402 }
2403 }
2404 break;
2405
2406 default:
2407 AssertSz(FALSE, "Invalid rollback cache action.");
2408 break;
2409 }
2410 }
2411 }
2412
2413 static HRESULT DoExecuteAction(
2414 __in BURN_ENGINE_STATE* pEngineState,
2415 __in BURN_EXECUTE_ACTION* pExecuteAction,
2416 __in BURN_EXECUTE_CONTEXT* pContext,
2417 __inout BURN_EXECUTE_ACTION_CHECKPOINT** ppCheckpoint,
2418 __out BOOL* pfSuspend,
2419 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2420 )
2421 {
2422 Assert(!pExecuteAction->fDeleted);
2423
2424 HRESULT hr = S_OK;
2425 HANDLE rghWait[2] = { };
2426 DWORD dwSignaledIndex = 0;
2427 DWORD dwExitCode = 0;
2428 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2429 BOOL fRetry = FALSE;
2430 BOOL fStopWusaService = FALSE;
2431 BOOL fInsideMsiTransaction = FALSE;
2432
2433 pContext->fRollback = FALSE;
2434
2435 do
2436 {
2437 fInsideMsiTransaction = *ppCheckpoint && (*ppCheckpoint)->pActiveRollbackBoundary && (*ppCheckpoint)->pActiveRollbackBoundary->fActiveTransaction;
2438
2439 switch (pExecuteAction->type)
2440 {
2441 case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT:
2442 *ppCheckpoint = &pExecuteAction->checkpoint;
2443 break;
2444
2445 case BURN_EXECUTE_ACTION_TYPE_WAIT_CACHE_PACKAGE:
2446 pExecuteAction->waitCachePackage.pPackage->fReachedExecution = TRUE;
2447
2448 // wait for cache sync-point
2449 rghWait[0] = pExecuteAction->waitCachePackage.pPackage->hCacheEvent;
2450 rghWait[1] = pContext->pApplyContext->hCacheThread;
2451
2452 hr = AppWaitForMultipleObjects(rghWait[1] ? 2 : 1, rghWait, FALSE, INFINITE, &dwSignaledIndex);
2453 ExitOnFailure(hr, "Failed to wait for cache check-point.");
2454
2455 switch (dwSignaledIndex)
2456 {
2457 case 0:
2458 break;
2459 case 1:
2460 if (!::GetExitCodeThread(pContext->pApplyContext->hCacheThread, &dwExitCode))
2461 {
2462 ExitWithLastError(hr, "Failed to get cache thread exit code.");
2463 }
2464
2465 ExitWithRootFailure(hr, E_UNEXPECTED, "Cache thread exited unexpectedly with exit code: %u.", dwExitCode);
2466 }
2467
2468 break;
2469
2470 case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE:
2471 hr = ExecuteRelatedBundle(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart);
2472 ExitOnFailure(hr, "Failed to execute related bundle.");
2473 break;
2474
2475 case BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE:
2476 hr = ExecuteBundlePackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart);
2477 ExitOnFailure(hr, "Failed to execute BUNDLE package.");
2478 break;
2479
2480 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
2481 hr = ExecuteExePackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart);
2482 ExitOnFailure(hr, "Failed to execute EXE package.");
2483 break;
2484
2485 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
2486 hr = ExecuteMsiPackage(pEngineState, pExecuteAction, pContext, fInsideMsiTransaction, FALSE, &fRetry, pfSuspend, &restart);
2487 ExitOnFailure(hr, "Failed to execute MSI package.");
2488 break;
2489
2490 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
2491 hr = ExecuteMspPackage(pEngineState, pExecuteAction, pContext, fInsideMsiTransaction, FALSE, &fRetry, pfSuspend, &restart);
2492 ExitOnFailure(hr, "Failed to execute MSP package.");
2493 break;
2494
2495 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
2496 hr = ExecuteMsuPackage(pEngineState, pExecuteAction, pContext, FALSE, fStopWusaService, &fRetry, pfSuspend, &restart);
2497 fStopWusaService = fRetry;
2498 ExitOnFailure(hr, "Failed to execute MSU package.");
2499 break;
2500
2501 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER:
2502 hr = ExecutePackageProviderAction(pEngineState, pExecuteAction, pContext, FALSE);
2503 ExitOnFailure(hr, "Failed to execute package provider registration action.");
2504 break;
2505
2506 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
2507 hr = ExecuteDependencyAction(pEngineState, pExecuteAction, pContext, FALSE);
2508 ExitOnFailure(hr, "Failed to execute dependency action.");
2509 break;
2510
2511 case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY_START: __fallthrough;
2512 case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY_END:
2513 *ppCheckpoint = NULL;
2514 break;
2515
2516 case BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION:
2517 hr = ExecuteMsiBeginTransaction(pEngineState, pExecuteAction->msiTransaction.pRollbackBoundary, pContext);
2518 ExitOnFailure(hr, "Failed to execute begin MSI transaction action.");
2519 break;
2520
2521 case BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION:
2522 hr = ExecuteMsiCommitTransaction(pEngineState, pExecuteAction->msiTransaction.pRollbackBoundary, pContext, &restart);
2523 ExitOnFailure(hr, "Failed to execute commit MSI transaction action.");
2524 break;
2525
2526 case BURN_EXECUTE_ACTION_TYPE_UNINSTALL_MSI_COMPATIBLE_PACKAGE:
2527 hr = ExecuteUninstallMsiCompatiblePackage(pEngineState, pExecuteAction, pContext, &fRetry, pfSuspend, &restart);
2528 ExitOnFailure(hr, "Failed to execute uninstall MSI compatible package.");
2529 break;
2530
2531 default:
2532 hr = E_UNEXPECTED;
2533 ExitOnFailure(hr, "Invalid execute action.");
2534 }
2535
2536 if (*pRestart < restart)
2537 {
2538 *pRestart = restart;
2539 }
2540 } while (fRetry && *pRestart < BOOTSTRAPPER_APPLY_RESTART_INITIATED);
2541
2542 LExit:
2543 if (*pRestart < restart)
2544 {
2545 *pRestart = restart;
2546 }
2547
2548 return hr;
2549 }
2550
2551 static HRESULT DoRollbackActions(
2552 __in BURN_ENGINE_STATE* pEngineState,
2553 __in BURN_EXECUTE_CONTEXT* pContext,
2554 __in DWORD dwCheckpoint,
2555 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2556 )
2557 {
2558 HRESULT hr = S_OK;
2559 DWORD iCheckpoint = 0;
2560 BOOL fRetryIgnored = FALSE;
2561 BOOL fSuspendIgnored = FALSE;
2562 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2563
2564 pContext->fRollback = TRUE;
2565
2566 // scan to last checkpoint
2567 for (DWORD i = 0; i < pEngineState->plan.cRollbackActions; ++i)
2568 {
2569 BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i];
2570 if (pRollbackAction->fDeleted)
2571 {
2572 continue;
2573 }
2574
2575 pContext->wzExecutingPackageId = NULL;
2576 pContext->fAbandonedProcess = FALSE;
2577
2578 if (BURN_EXECUTE_ACTION_TYPE_CHECKPOINT == pRollbackAction->type)
2579 {
2580 if (pRollbackAction->checkpoint.dwId == dwCheckpoint)
2581 {
2582 iCheckpoint = i;
2583 break;
2584 }
2585 }
2586 }
2587
2588 // execute rollback actions
2589 if (iCheckpoint)
2590 {
2591 // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF.
2592 for (int i = iCheckpoint - 1; i >= 0; --i)
2593 {
2594 BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i];
2595 if (pRollbackAction->fDeleted)
2596 {
2597 continue;
2598 }
2599
2600 restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2601
2602 switch (pRollbackAction->type)
2603 {
2604 case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT:
2605 break;
2606
2607 case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE:
2608 hr = ExecuteRelatedBundle(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
2609 ExitOnFailure(hr, "Failed to execute related bundle.");
2610 break;
2611
2612 case BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE:
2613 hr = ExecuteBundlePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
2614 IgnoreRollbackError(hr, "Failed to rollback BUNDLE package.");
2615 break;
2616
2617 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
2618 hr = ExecuteExePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
2619 IgnoreRollbackError(hr, "Failed to rollback EXE package.");
2620 break;
2621
2622 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
2623 hr = ExecuteMsiPackage(pEngineState, pRollbackAction, pContext, FALSE, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
2624 IgnoreRollbackError(hr, "Failed to rollback MSI package.");
2625 break;
2626
2627 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
2628 hr = ExecuteMspPackage(pEngineState, pRollbackAction, pContext, FALSE, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
2629 IgnoreRollbackError(hr, "Failed to rollback MSP package.");
2630 break;
2631
2632 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
2633 hr = ExecuteMsuPackage(pEngineState, pRollbackAction, pContext, TRUE, FALSE, &fRetryIgnored, &fSuspendIgnored, &restart);
2634 IgnoreRollbackError(hr, "Failed to rollback MSU package.");
2635 break;
2636
2637 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER:
2638 hr = ExecutePackageProviderAction(pEngineState, pRollbackAction, pContext, TRUE);
2639 IgnoreRollbackError(hr, "Failed to rollback package provider action.");
2640 break;
2641
2642 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
2643 hr = ExecuteDependencyAction(pEngineState, pRollbackAction, pContext, TRUE);
2644 IgnoreRollbackError(hr, "Failed to rollback dependency action.");
2645 break;
2646
2647 case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY_START:
2648 ExitFunction1(hr = S_OK);
2649
2650 case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE:
2651 if (!pRollbackAction->uncachePackage.pPackage->fCached) // only rollback when it wasn't already cached.
2652 {
2653 hr = CleanPackage(pEngineState->plan.pCache, pEngineState->companionConnection.hPipe, pRollbackAction->uncachePackage.pPackage);
2654 IgnoreRollbackError(hr, "Failed to uncache package for rollback.");
2655 }
2656 else if (pRollbackAction->uncachePackage.pPackage->fCanAffectRegistration)
2657 {
2658 // Don't let this already cached package cause the registration to be kept if the bundle failed and wasn't already registered.
2659 pRollbackAction->uncachePackage.pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
2660 }
2661
2662 break;
2663
2664 default:
2665 hr = E_UNEXPECTED;
2666 ExitOnFailure(hr, "Invalid rollback action: %d.", pRollbackAction->type);
2667 }
2668
2669 if (*pRestart < restart)
2670 {
2671 *pRestart = restart;
2672 }
2673 }
2674 }
2675
2676 LExit:
2677 if (*pRestart < restart)
2678 {
2679 *pRestart = restart;
2680 }
2681
2682 return hr;
2683 }
2684
2685 static BOOL ShouldSkipPackage(
2686 __in BURN_PACKAGE* pPackage,
2687 __in BOOL fRollback
2688 )
2689 {
2690 BOOL fSkip = FALSE;
2691 BURN_CACHE_PACKAGE_TYPE cachePackageType = fRollback ? pPackage->rollbackCacheType : pPackage->executeCacheType;
2692 BOOL fCacheVital = BURN_CACHE_PACKAGE_TYPE_REQUIRED == cachePackageType;
2693
2694 if (fCacheVital && FAILED(pPackage->hrCacheResult))
2695 {
2696 if (fRollback)
2697 {
2698 LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_ROLLBACK_NO_CACHE, pPackage->sczId, pPackage->hrCacheResult, LoggingActionStateToString(pPackage->rollback));
2699 }
2700 else
2701 {
2702 LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult);
2703 }
2704
2705 ExitFunction1(fSkip = TRUE);
2706 }
2707 else if (fRollback && pPackage->fAbandonedProcess)
2708 {
2709 LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_PACKAGE_WITH_ABANDONED_PROCESS, pPackage->sczId);
2710 ExitFunction1(fSkip = TRUE);
2711 }
2712
2713 LExit:
2714 return fSkip;
2715 }
2716
2717 static HRESULT ExecuteRelatedBundle(
2718 __in BURN_ENGINE_STATE* pEngineState,
2719 __in BURN_EXECUTE_ACTION* pExecuteAction,
2720 __in BURN_EXECUTE_CONTEXT* pContext,
2721 __in BOOL fRollback,
2722 __out BOOL* pfRetry,
2723 __out BOOL* pfSuspend,
2724 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2725 )
2726 {
2727 HRESULT hr = S_OK;
2728 HRESULT hrExecute = S_OK;
2729 GENERIC_EXECUTE_MESSAGE message = { };
2730 int nResult = 0;
2731 BOOL fBeginCalled = FALSE;
2732 BURN_RELATED_BUNDLE* pRelatedBundle = pExecuteAction->relatedBundle.pRelatedBundle;
2733 BURN_PACKAGE* pPackage = &pRelatedBundle->package;
2734
2735 Assert(pContext->fRollback == fRollback);
2736
2737 if (ShouldSkipPackage(pPackage, fRollback))
2738 {
2739 ExitFunction1(hr = S_OK);
2740 }
2741
2742 pContext->wzExecutingPackageId = pPackage->sczId;
2743 fBeginCalled = TRUE;
2744
2745 // Send package execute begin to BA.
2746 hr = BACallbackOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->relatedBundle.action, INSTALLUILEVEL_NOCHANGE, FALSE);
2747 ExitOnRootFailure(hr, "BA aborted execute related bundle begin.");
2748
2749 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2750 message.dwUIHint = MB_OKCANCEL;
2751 message.progress.dwPercentage = fRollback ? 100 : 0;
2752 nResult = GenericExecuteMessageHandler(&message, pContext);
2753 hr = BootstrapperApplicationInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwUIHint, nResult);
2754 ExitOnRootFailure(hr, "BA aborted related bundle progress.");
2755
2756 // Execute package.
2757 if (pPackage->fPerMachine)
2758 {
2759 hrExecute = ElevationExecuteRelatedBundle(pEngineState->companionConnection.hPipe, pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart);
2760 ExitOnFailure(hrExecute, "Failed to configure per-machine related bundle.");
2761 }
2762 else
2763 {
2764 hrExecute = BundlePackageEngineExecuteRelatedBundle(pExecuteAction, pContext->pCache, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart);
2765 ExitOnFailure(hrExecute, "Failed to configure per-user related bundle.");
2766 }
2767
2768 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2769 message.dwUIHint = MB_OKCANCEL;
2770 message.progress.dwPercentage = fRollback ? 0 : 100;
2771 nResult = GenericExecuteMessageHandler(&message, pContext);
2772 hr = BootstrapperApplicationInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwUIHint, nResult);
2773 ExitOnRootFailure(hr, "BA aborted related bundle progress.");
2774
2775 pContext->cExecutedPackages += fRollback ? -1 : 1;
2776
2777 hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, pContext->pApplyContext);
2778 ExitOnRootFailure(hr, "BA aborted related bundle execute progress.");
2779
2780 LExit:
2781 if (fBeginCalled)
2782 {
2783 pPackage->fAbandonedProcess = pContext->fAbandonedProcess;
2784 hr = ExecutePackageComplete(pEngineState, pPackage->sczId, pPackage->fVital, pPackage->fAbandonedProcess, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2785 }
2786
2787 return hr;
2788 }
2789
2790 static HRESULT DoRestoreRelatedBundleActions(
2791 __in BURN_ENGINE_STATE* pEngineState,
2792 __in BURN_EXECUTE_CONTEXT* pContext,
2793 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2794 )
2795 {
2796 HRESULT hr = S_OK;
2797 BOOL fRetryIgnored = FALSE;
2798 BOOL fSuspendIgnored = FALSE;
2799 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2800
2801 // execute restore related bundle actions
2802 for (DWORD i = 0; i < pEngineState->plan.cRestoreRelatedBundleActions; ++i)
2803 {
2804 BURN_EXECUTE_ACTION* pRestoreRelatedBundleAction = &pEngineState->plan.rgRestoreRelatedBundleActions[i];
2805 if (pRestoreRelatedBundleAction->fDeleted)
2806 {
2807 continue;
2808 }
2809
2810 pContext->wzExecutingPackageId = NULL;
2811 pContext->fAbandonedProcess = FALSE;
2812
2813 restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2814
2815 switch (pRestoreRelatedBundleAction->type)
2816 {
2817 case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE:
2818 hr = ExecuteRelatedBundle(pEngineState, pRestoreRelatedBundleAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
2819 IgnoreRollbackError(hr, "Failed to restore related bundle package.");
2820 break;
2821
2822 default:
2823 hr = E_UNEXPECTED;
2824 ExitOnFailure(hr, "Invalid restore related bundle action: %d.", pRestoreRelatedBundleAction->type);
2825 }
2826
2827 if (*pRestart < restart)
2828 {
2829 *pRestart = restart;
2830 }
2831 }
2832
2833 LExit:
2834 if (*pRestart < restart)
2835 {
2836 *pRestart = restart;
2837 }
2838
2839 return hr;
2840 }
2841
2842 static HRESULT ExecuteBundlePackage(
2843 __in BURN_ENGINE_STATE* pEngineState,
2844 __in BURN_EXECUTE_ACTION* pExecuteAction,
2845 __in BURN_EXECUTE_CONTEXT* pContext,
2846 __in BOOL fRollback,
2847 __out BOOL* pfRetry,
2848 __out BOOL* pfSuspend,
2849 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2850 )
2851 {
2852 HRESULT hr = S_OK;
2853 HRESULT hrExecute = S_OK;
2854 GENERIC_EXECUTE_MESSAGE message = { };
2855 int nResult = 0;
2856 BOOL fBeginCalled = FALSE;
2857 BOOL fExecuted = FALSE;
2858 BURN_PACKAGE* pPackage = pExecuteAction->bundlePackage.pPackage;
2859
2860 Assert(pContext->fRollback == fRollback);
2861
2862 if (ShouldSkipPackage(pPackage, fRollback))
2863 {
2864 ExitFunction1(hr = S_OK);
2865 }
2866
2867 pContext->wzExecutingPackageId = pPackage->sczId;
2868 fBeginCalled = TRUE;
2869
2870 // Send package execute begin to BA.
2871 hr = BACallbackOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->bundlePackage.action, INSTALLUILEVEL_NOCHANGE, FALSE);
2872 ExitOnRootFailure(hr, "BA aborted execute BUNDLE package begin.");
2873
2874 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2875 message.dwUIHint = MB_OKCANCEL;
2876 message.progress.dwPercentage = fRollback ? 100 : 0;
2877 nResult = GenericExecuteMessageHandler(&message, pContext);
2878 hr = BootstrapperApplicationInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwUIHint, nResult);
2879 ExitOnRootFailure(hr, "BA aborted BUNDLE progress.");
2880
2881 fExecuted = TRUE;
2882
2883 // Execute package.
2884 if (pPackage->fPerMachine)
2885 {
2886 hrExecute = ElevationExecuteBundlePackage(pEngineState->companionConnection.hPipe, pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart);
2887 ExitOnFailure(hrExecute, "Failed to configure per-machine BUNDLE package.");
2888 }
2889 else
2890 {
2891 hrExecute = BundlePackageEngineExecutePackage(pExecuteAction, pContext->pCache, &pEngineState->variables, fRollback, SUCCEEDED(pExecuteAction->bundlePackage.pPackage->hrCacheResult), GenericExecuteMessageHandler, pContext, pRestart);
2892 ExitOnFailure(hrExecute, "Failed to configure per-user BUNDLE package.");
2893 }
2894
2895 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2896 message.dwUIHint = MB_OKCANCEL;
2897 message.progress.dwPercentage = fRollback ? 0 : 100;
2898 nResult = GenericExecuteMessageHandler(&message, pContext);
2899 hr = BootstrapperApplicationInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwUIHint, nResult);
2900 ExitOnRootFailure(hr, "BA aborted BUNDLE progress.");
2901
2902 pContext->cExecutedPackages += fRollback ? -1 : 1;
2903
2904 hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, pContext->pApplyContext);
2905 ExitOnRootFailure(hr, "BA aborted BUNDLE package execute progress.");
2906
2907 LExit:
2908 if (fExecuted)
2909 {
2910 BundlePackageEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute);
2911 }
2912
2913 if (fBeginCalled)
2914 {
2915 pPackage->fAbandonedProcess = pContext->fAbandonedProcess;
2916 hr = ExecutePackageComplete(pEngineState, pPackage->sczId, pPackage->fVital, pPackage->fAbandonedProcess, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2917 }
2918
2919 return hr;
2920 }
2921
2922 static HRESULT ExecuteExePackage(
2923 __in BURN_ENGINE_STATE* pEngineState,
2924 __in BURN_EXECUTE_ACTION* pExecuteAction,
2925 __in BURN_EXECUTE_CONTEXT* pContext,
2926 __in BOOL fRollback,
2927 __out BOOL* pfRetry,
2928 __out BOOL* pfSuspend,
2929 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2930 )
2931 {
2932 HRESULT hr = S_OK;
2933 HRESULT hrExecute = S_OK;
2934 GENERIC_EXECUTE_MESSAGE message = { };
2935 int nResult = 0;
2936 BOOL fBeginCalled = FALSE;
2937 BOOL fExecuted = FALSE;
2938 BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage;
2939
2940 Assert(pContext->fRollback == fRollback);
2941
2942 if (ShouldSkipPackage(pPackage, fRollback))
2943 {
2944 ExitFunction1(hr = S_OK);
2945 }
2946
2947 pContext->wzExecutingPackageId = pPackage->sczId;
2948 fBeginCalled = TRUE;
2949
2950 // Send package execute begin to BA.
2951 hr = BACallbackOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->exePackage.action, INSTALLUILEVEL_NOCHANGE, FALSE);
2952 ExitOnRootFailure(hr, "BA aborted execute EXE package begin.");
2953
2954 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2955 message.dwUIHint = MB_OKCANCEL;
2956 message.progress.dwPercentage = fRollback ? 100 : 0;
2957 nResult = GenericExecuteMessageHandler(&message, pContext);
2958 hr = BootstrapperApplicationInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwUIHint, nResult);
2959 ExitOnRootFailure(hr, "BA aborted EXE progress.");
2960
2961 fExecuted = TRUE;
2962
2963 // Execute package.
2964 if (pPackage->fPerMachine)
2965 {
2966 hrExecute = ElevationExecuteExePackage(pEngineState->companionConnection.hPipe, pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart);
2967 ExitOnFailure(hrExecute, "Failed to configure per-machine EXE package.");
2968 }
2969 else
2970 {
2971 hrExecute = ExeEngineExecutePackage(pExecuteAction, pContext->pCache, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart);
2972 ExitOnFailure(hrExecute, "Failed to configure per-user EXE package.");
2973 }
2974
2975 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2976 message.dwUIHint = MB_OKCANCEL;
2977 message.progress.dwPercentage = fRollback ? 0 : 100;
2978 nResult = GenericExecuteMessageHandler(&message, pContext);
2979 hr = BootstrapperApplicationInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwUIHint, nResult);
2980 ExitOnRootFailure(hr, "BA aborted EXE progress.");
2981
2982 pContext->cExecutedPackages += fRollback ? -1 : 1;
2983
2984 hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, pContext->pApplyContext);
2985 ExitOnRootFailure(hr, "BA aborted EXE package execute progress.");
2986
2987 LExit:
2988 if (fExecuted)
2989 {
2990 ExeEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute);
2991 }
2992
2993 if (fBeginCalled)
2994 {
2995 pPackage->fAbandonedProcess = pContext->fAbandonedProcess;
2996 hr = ExecutePackageComplete(pEngineState, pPackage->sczId, pPackage->fVital, pPackage->fAbandonedProcess, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2997 }
2998
2999 return hr;
3000 }
3001
3002 static HRESULT ExecuteMsiPackage(
3003 __in BURN_ENGINE_STATE* pEngineState,
3004 __in BURN_EXECUTE_ACTION* pExecuteAction,
3005 __in BURN_EXECUTE_CONTEXT* pContext,
3006 __in BOOL fInsideMsiTransaction,
3007 __in BOOL fRollback,
3008 __out BOOL* pfRetry,
3009 __out BOOL* pfSuspend,
3010 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
3011 )
3012 {
3013 HRESULT hr = S_OK;
3014 HRESULT hrExecute = S_OK;
3015 BOOL fBeginCalled = FALSE;
3016 BOOL fExecuted = FALSE;
3017 BURN_PACKAGE* pPackage = pExecuteAction->msiPackage.pPackage;
3018
3019 Assert(pContext->fRollback == fRollback);
3020
3021 if (ShouldSkipPackage(pPackage, fRollback))
3022 {
3023 ExitFunction1(hr = S_OK);
3024 }
3025
3026 pContext->wzExecutingPackageId = pPackage->sczId;
3027 fBeginCalled = TRUE;
3028
3029 // Send package execute begin to BA.
3030 hr = BACallbackOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.uiLevel, pExecuteAction->msiPackage.fDisableExternalUiHandler);
3031 ExitOnRootFailure(hr, "BA aborted execute MSI package begin.");
3032
3033 fExecuted = TRUE;
3034
3035 // execute package
3036 if (pPackage->fPerMachine)
3037 {
3038 hrExecute = ElevationExecuteMsiPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
3039 ExitOnFailure(hrExecute, "Failed to configure per-machine MSI package.");
3040 }
3041 else
3042 {
3043 hrExecute = MsiEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, pContext->pCache, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
3044 ExitOnFailure(hrExecute, "Failed to configure per-user MSI package.");
3045 }
3046
3047 pContext->cExecutedPackages += fRollback ? -1 : 1;
3048
3049 hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, pContext->pApplyContext);
3050 ExitOnRootFailure(hr, "BA aborted MSI package execute progress.");
3051
3052 LExit:
3053 if (fExecuted)
3054 {
3055 MsiEngineUpdateInstallRegistrationState(pExecuteAction, fRollback, hrExecute, fInsideMsiTransaction);
3056 }
3057
3058 if (fBeginCalled)
3059 {
3060 Assert(!pContext->fAbandonedProcess);
3061 hr = ExecutePackageComplete(pEngineState, pPackage->sczId, pPackage->fVital, FALSE, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
3062 }
3063
3064 return hr;
3065 }
3066
3067 static HRESULT ExecuteMspPackage(
3068 __in BURN_ENGINE_STATE* pEngineState,
3069 __in BURN_EXECUTE_ACTION* pExecuteAction,
3070 __in BURN_EXECUTE_CONTEXT* pContext,
3071 __in BOOL fInsideMsiTransaction,
3072 __in BOOL fRollback,
3073 __out BOOL* pfRetry,
3074 __out BOOL* pfSuspend,
3075 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
3076 )
3077 {
3078 HRESULT hr = S_OK;
3079 HRESULT hrExecute = S_OK;
3080 BOOL fBeginCalled = FALSE;
3081 BOOL fExecuted = FALSE;
3082 BURN_PACKAGE* pPackage = pExecuteAction->mspTarget.pPackage;
3083
3084 Assert(pContext->fRollback == fRollback);
3085
3086 if (ShouldSkipPackage(pPackage, fRollback))
3087 {
3088 ExitFunction1(hr = S_OK);
3089 }
3090
3091 pContext->wzExecutingPackageId = pPackage->sczId;
3092 fBeginCalled = TRUE;
3093
3094 // Send package execute begin to BA.
3095 hr = BACallbackOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->mspTarget.action, pExecuteAction->mspTarget.uiLevel, pExecuteAction->mspTarget.fDisableExternalUiHandler);
3096 ExitOnRootFailure(hr, "BA aborted execute MSP package begin.");
3097
3098 // Now send all the patches that target this product code.
3099 for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i)
3100 {
3101 BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage;
3102
3103 hr = BACallbackOnExecutePatchTarget(&pEngineState->userExperience, pMspPackage->sczId, pExecuteAction->mspTarget.sczTargetProductCode);
3104 ExitOnRootFailure(hr, "BA aborted execute MSP target.");
3105 }
3106
3107 fExecuted = TRUE;
3108
3109 // execute package
3110 if (pExecuteAction->mspTarget.fPerMachineTarget)
3111 {
3112 hrExecute = ElevationExecuteMspPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
3113 ExitOnFailure(hrExecute, "Failed to configure per-machine MSP package.");
3114 }
3115 else
3116 {
3117 hrExecute = MspEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, pContext->pCache, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
3118 ExitOnFailure(hrExecute, "Failed to configure per-user MSP package.");
3119 }
3120
3121 pContext->cExecutedPackages += fRollback ? -1 : 1;
3122
3123 hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, pContext->pApplyContext);
3124 ExitOnRootFailure(hr, "BA aborted MSP package execute progress.");
3125
3126 LExit:
3127 if (fExecuted)
3128 {
3129 MspEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute, fInsideMsiTransaction);
3130 }
3131
3132 if (fBeginCalled)
3133 {
3134 Assert(!pContext->fAbandonedProcess);
3135 hr = ExecutePackageComplete(pEngineState, pPackage->sczId, pPackage->fVital, FALSE, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
3136 }
3137
3138 return hr;
3139 }
3140
3141 static HRESULT ExecuteMsuPackage(
3142 __in BURN_ENGINE_STATE* pEngineState,
3143 __in BURN_EXECUTE_ACTION* pExecuteAction,
3144 __in BURN_EXECUTE_CONTEXT* pContext,
3145 __in BOOL fRollback,
3146 __in BOOL fStopWusaService,
3147 __out BOOL* pfRetry,
3148 __out BOOL* pfSuspend,
3149 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
3150 )
3151 {
3152 HRESULT hr = S_OK;
3153 HRESULT hrExecute = S_OK;
3154 GENERIC_EXECUTE_MESSAGE message = { };
3155 int nResult = 0;
3156 BOOL fBeginCalled = FALSE;
3157 BOOL fExecuted = FALSE;
3158 BURN_PACKAGE* pPackage = pExecuteAction->msuPackage.pPackage;
3159
3160 Assert(pContext->fRollback == fRollback);
3161
3162 if (ShouldSkipPackage(pPackage, fRollback))
3163 {
3164 ExitFunction1(hr = S_OK);
3165 }
3166
3167 pContext->wzExecutingPackageId = pPackage->sczId;
3168 fBeginCalled = TRUE;
3169
3170 // Send package execute begin to BA.
3171 hr = BACallbackOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->msuPackage.action, INSTALLUILEVEL_NOCHANGE, FALSE);
3172 ExitOnRootFailure(hr, "BA aborted execute MSU package begin.");
3173
3174 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
3175 message.dwUIHint = MB_OKCANCEL;
3176 message.progress.dwPercentage = fRollback ? 100 : 0;
3177 nResult = GenericExecuteMessageHandler(&message, pContext);
3178 hr = BootstrapperApplicationInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwUIHint, nResult);
3179 ExitOnRootFailure(hr, "BA aborted MSU progress.");
3180
3181 fExecuted = TRUE;
3182
3183 // execute package
3184 if (pPackage->fPerMachine)
3185 {
3186 hrExecute = ElevationExecuteMsuPackage(pEngineState->companionConnection.hPipe, pExecuteAction, fRollback, fStopWusaService, GenericExecuteMessageHandler, pContext, pRestart);
3187 ExitOnFailure(hrExecute, "Failed to configure per-machine MSU package.");
3188 }
3189 else
3190 {
3191 hrExecute = E_UNEXPECTED;
3192 ExitOnFailure(hr, "MSU packages cannot be per-user.");
3193 }
3194
3195 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
3196 message.dwUIHint = MB_OKCANCEL;
3197 message.progress.dwPercentage = fRollback ? 0 : 100;
3198 nResult = GenericExecuteMessageHandler(&message, pContext);
3199 hr = BootstrapperApplicationInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwUIHint, nResult);
3200 ExitOnRootFailure(hr, "BA aborted MSU progress.");
3201
3202 pContext->cExecutedPackages += fRollback ? -1 : 1;
3203
3204 hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, pContext->pApplyContext);
3205 ExitOnRootFailure(hr, "BA aborted MSU package execute progress.");
3206
3207 LExit:
3208 if (fExecuted)
3209 {
3210 MsuEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute);
3211 }
3212
3213 if (fBeginCalled)
3214 {
3215 pPackage->fAbandonedProcess = pContext->fAbandonedProcess;
3216 hr = ExecutePackageComplete(pEngineState, pPackage->sczId, pPackage->fVital, pPackage->fAbandonedProcess, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
3217 }
3218
3219 return hr;
3220 }
3221
3222 static HRESULT ExecutePackageProviderAction(
3223 __in BURN_ENGINE_STATE* pEngineState,
3224 __in BURN_EXECUTE_ACTION* pAction,
3225 __in BURN_EXECUTE_CONTEXT* pContext,
3226 __in BOOL fRollback
3227 )
3228 {
3229 HRESULT hr = S_OK;
3230
3231 Assert(pContext->fRollback == fRollback);
3232 UNREFERENCED_PARAMETER(pContext);
3233
3234 if (pAction->packageProvider.pPackage->fPerMachine)
3235 {
3236 hr = ElevationExecutePackageProviderAction(pEngineState->companionConnection.hPipe, pAction, fRollback);
3237 ExitOnFailure(hr, "Failed to register the package provider on per-machine package.");
3238 }
3239 else
3240 {
3241 hr = DependencyExecutePackageProviderAction(pAction, fRollback);
3242 ExitOnFailure(hr, "Failed to register the package provider on per-user package.");
3243 }
3244
3245 LExit:
3246 return hr;
3247 }
3248
3249 static HRESULT ExecuteDependencyAction(
3250 __in BURN_ENGINE_STATE* pEngineState,
3251 __in BURN_EXECUTE_ACTION* pAction,
3252 __in BURN_EXECUTE_CONTEXT* pContext,
3253 __in BOOL fRollback
3254 )
3255 {
3256 HRESULT hr = S_OK;
3257 BURN_PACKAGE* pPackage = pAction->packageDependency.pPackage;
3258
3259 Assert(pContext->fRollback == fRollback);
3260 UNREFERENCED_PARAMETER(pContext);
3261
3262 if (pPackage->fPerMachine)
3263 {
3264 hr = ElevationExecutePackageDependencyAction(pEngineState->companionConnection.hPipe, pAction, fRollback);
3265 ExitOnFailure(hr, "Failed to register the dependency on per-machine package.");
3266 }
3267 else
3268 {
3269 hr = DependencyExecutePackageDependencyAction(FALSE, pAction, fRollback);
3270 ExitOnFailure(hr, "Failed to register the dependency on per-user package.");
3271 }
3272
3273 if (pPackage->fCanAffectRegistration)
3274 {
3275 BURN_DEPENDENCY_ACTION dependencyAction = fRollback ? pPackage->dependencyRollback : pPackage->dependencyExecute;
3276 if (BURN_DEPENDENCY_ACTION_REGISTER == dependencyAction)
3277 {
3278 if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->cacheRegistrationState)
3279 {
3280 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
3281 }
3282
3283 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
3284 {
3285 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
3286 {
3287 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
3288
3289 if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pTargetProduct->registrationState)
3290 {
3291 pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
3292 }
3293 }
3294 }
3295 else if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->installRegistrationState)
3296 {
3297 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
3298 }
3299 }
3300 else if (BURN_DEPENDENCY_ACTION_UNREGISTER == dependencyAction)
3301 {
3302 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState)
3303 {
3304 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
3305 }
3306
3307 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
3308 {
3309 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
3310 {
3311 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
3312
3313 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pTargetProduct->registrationState)
3314 {
3315 pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
3316 }
3317 }
3318 }
3319 else if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState)
3320 {
3321 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
3322 }
3323 }
3324 }
3325
3326 LExit:
3327 return hr;
3328 }
3329
3330 static HRESULT ExecuteMsiBeginTransaction(
3331 __in BURN_ENGINE_STATE* pEngineState,
3332 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
3333 __in BURN_EXECUTE_CONTEXT* /*pContext*/
3334 )
3335 {
3336 HRESULT hr = S_OK;
3337 BOOL fBeginCalled = FALSE;
3338
3339 if (pRollbackBoundary->fActiveTransaction)
3340 {
3341 ExitFunction1(hr = E_INVALIDSTATE);
3342 }
3343
3344 fBeginCalled = TRUE;
3345 hr = BACallbackOnBeginMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId);
3346 ExitOnRootFailure(hr, "BA aborted execute begin MSI transaction.");
3347
3348 if (pEngineState->plan.fPerMachine)
3349 {
3350 hr = ElevationMsiBeginTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary);
3351 ExitOnFailure(hr, "Failed to begin an elevated MSI transaction.");
3352 }
3353 else
3354 {
3355 hr = MsiEngineBeginTransaction(pRollbackBoundary);
3356 }
3357
3358 if (SUCCEEDED(hr))
3359 {
3360 pRollbackBoundary->fActiveTransaction = TRUE;
3361
3362 ResetTransactionRegistrationState(pEngineState, FALSE);
3363 }
3364
3365 LExit:
3366 if (fBeginCalled)
3367 {
3368 BACallbackOnBeginMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr);
3369 }
3370
3371 return hr;
3372 }
3373
3374 static HRESULT ExecuteMsiCommitTransaction(
3375 __in BURN_ENGINE_STATE* pEngineState,
3376 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
3377 __in BURN_EXECUTE_CONTEXT* pContext,
3378 __out BOOTSTRAPPER_APPLY_RESTART *pRestart
3379 )
3380 {
3381 HRESULT hr = S_OK;
3382 BOOL fCommitBeginCalled = FALSE;
3383 BOOTSTRAPPER_EXECUTEMSITRANSACTIONCOMPLETE_ACTION action = BOOTSTRAPPER_EXECUTEMSITRANSACTIONCOMPLETE_ACTION_NONE;
3384
3385 if (!pRollbackBoundary->fActiveTransaction)
3386 {
3387 ExitFunction1(hr = E_INVALIDSTATE);
3388 }
3389
3390 fCommitBeginCalled = TRUE;
3391 hr = BACallbackOnCommitMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId);
3392 ExitOnRootFailure(hr, "BA aborted execute commit MSI transaction.");
3393
3394 if (pEngineState->plan.fPerMachine)
3395 {
3396 hr = ElevationMsiCommitTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary, MsiExecuteMessageHandler, pContext, pRestart);
3397 ExitOnFailure(hr, "Failed to commit an elevated MSI transaction.");
3398 }
3399 else
3400 {
3401 hr = MsiEngineCommitTransaction(pRollbackBoundary, pRestart);
3402 }
3403
3404 // Assume that MsiEndTransaction can only be called once for each MsiBeginTransaction.
3405 pRollbackBoundary->fActiveTransaction = FALSE;
3406
3407 ResetTransactionRegistrationState(pEngineState, TRUE);
3408
3409 LExit:
3410 if (fCommitBeginCalled)
3411 {
3412 BACallbackOnCommitMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr, *pRestart, &action);
3413
3414 if (action == BOOTSTRAPPER_EXECUTEMSITRANSACTIONCOMPLETE_ACTION_RESTART)
3415 {
3416 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
3417 }
3418 }
3419
3420 return hr;
3421 }
3422
3423 static HRESULT ExecuteMsiRollbackTransaction(
3424 __in BURN_ENGINE_STATE* pEngineState,
3425 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
3426 __in BURN_EXECUTE_CONTEXT* pContext,
3427 __out BOOTSTRAPPER_APPLY_RESTART *pRestart
3428 )
3429 {
3430 HRESULT hr = S_OK;
3431 BOOL fRollbackBeginCalled = FALSE;
3432 BOOTSTRAPPER_EXECUTEMSITRANSACTIONCOMPLETE_ACTION action = BOOTSTRAPPER_EXECUTEMSITRANSACTIONCOMPLETE_ACTION_NONE;
3433
3434 if (!pRollbackBoundary->fActiveTransaction)
3435 {
3436 ExitFunction();
3437 }
3438
3439 fRollbackBeginCalled = TRUE;
3440 BACallbackOnRollbackMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId);
3441
3442 if (pEngineState->plan.fPerMachine)
3443 {
3444 hr = ElevationMsiRollbackTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary, MsiExecuteMessageHandler, pContext, pRestart);
3445 ExitOnFailure(hr, "Failed to rollback an elevated MSI transaction.");
3446 }
3447 else
3448 {
3449 hr = MsiEngineRollbackTransaction(pRollbackBoundary, pRestart);
3450 }
3451
3452 LExit:
3453 pRollbackBoundary->fActiveTransaction = FALSE;
3454
3455 ResetTransactionRegistrationState(pEngineState, FALSE);
3456
3457 if (fRollbackBeginCalled)
3458 {
3459 BACallbackOnRollbackMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr, *pRestart, &action);
3460
3461 if (action == BOOTSTRAPPER_EXECUTEMSITRANSACTIONCOMPLETE_ACTION_RESTART)
3462 {
3463 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
3464 }
3465 }
3466
3467 return hr;
3468 }
3469
3470 static void ResetTransactionRegistrationState(
3471 __in BURN_ENGINE_STATE* pEngineState,
3472 __in BOOL fCommit
3473 )
3474 {
3475 for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i)
3476 {
3477 BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i;
3478
3479 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
3480 {
3481 for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j)
3482 {
3483 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j;
3484
3485 if (fCommit && BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN != pTargetProduct->transactionRegistrationState)
3486 {
3487 pTargetProduct->registrationState = pTargetProduct->transactionRegistrationState;
3488 }
3489
3490 pTargetProduct->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
3491 }
3492 }
3493 else if (fCommit && BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN != pPackage->transactionRegistrationState)
3494 {
3495 pPackage->installRegistrationState = pPackage->transactionRegistrationState;
3496 }
3497
3498 pPackage->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
3499 }
3500 }
3501
3502 static HRESULT ExecuteUninstallMsiCompatiblePackage(
3503 __in BURN_ENGINE_STATE* pEngineState,
3504 __in BURN_EXECUTE_ACTION* pExecuteAction,
3505 __in BURN_EXECUTE_CONTEXT* pContext,
3506 __out BOOL* pfRetry,
3507 __out BOOL* pfSuspend,
3508 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
3509 )
3510 {
3511 HRESULT hr = S_OK;
3512 HRESULT hrExecute = S_OK;
3513 BOOL fRollback = FALSE;
3514 BOOTSTRAPPER_ACTION_STATE action = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
3515 INSTALLUILEVEL uiLevel = INSTALLUILEVEL_NONE;
3516 BOOL fDisableExternalUiHandler = FALSE;
3517 BOOL fBeginCalled = FALSE;
3518 BURN_PACKAGE* pParentPackage = pExecuteAction->uninstallMsiCompatiblePackage.pParentPackage;
3519
3520 Assert(pContext->fRollback == fRollback);
3521 pContext->wzExecutingPackageId = pParentPackage->compatiblePackage.compatibleEntry.sczId;
3522 fBeginCalled = TRUE;
3523
3524 // Send package execute begin to BA.
3525 hr = BACallbackOnExecutePackageBegin(&pEngineState->userExperience, pContext->wzExecutingPackageId, !fRollback, action, uiLevel, fDisableExternalUiHandler);
3526 ExitOnRootFailure(hr, "BA aborted execute MSI compatible package begin.");
3527
3528 // execute package
3529 if (pParentPackage->fPerMachine)
3530 {
3531 hrExecute = ElevationUninstallMsiCompatiblePackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
3532 ExitOnFailure(hrExecute, "Failed to uninstall per-machine MSI compatible package.");
3533 }
3534 else
3535 {
3536 hrExecute = MsiEngineUninstallCompatiblePackage(pEngineState->userExperience.hwndApply, pExecuteAction, pContext->pCache, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
3537 ExitOnFailure(hrExecute, "Failed to uninstall per-user MSI compatible package.");
3538 }
3539
3540 pContext->cExecutedPackages += fRollback ? -1 : 1;
3541
3542 hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, pContext->pApplyContext);
3543 ExitOnRootFailure(hr, "BA aborted MSI compatible package execute progress.");
3544
3545 LExit:
3546
3547 if (fBeginCalled)
3548 {
3549 Assert(!pContext->fAbandonedProcess);
3550 hr = ExecutePackageComplete(pEngineState, pContext->wzExecutingPackageId, FALSE, FALSE, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
3551 }
3552
3553 return hr;
3554 }
3555
3556 static HRESULT CleanCompatiblePackage(
3557 __in BURN_CACHE* pCache,
3558 __in HANDLE hElevatedPipe,
3559 __in BURN_PACKAGE* pPackage
3560 )
3561 {
3562 HRESULT hr = S_OK;
3563
3564 if (pPackage->fPerMachine)
3565 {
3566 hr = ElevationCleanCompatiblePackage(hElevatedPipe, pPackage);
3567 }
3568 else
3569 {
3570 hr = CacheRemovePackage(pCache, FALSE, pPackage->compatiblePackage.compatibleEntry.sczId, pPackage->compatiblePackage.sczCacheId);
3571 }
3572
3573 return hr;
3574 }
3575
3576 static HRESULT CleanPackage(
3577 __in BURN_CACHE* pCache,
3578 __in HANDLE hElevatedPipe,
3579 __in BURN_PACKAGE* pPackage
3580 )
3581 {
3582 HRESULT hr = S_OK;
3583
3584 if (pPackage->fPerMachine)
3585 {
3586 hr = ElevationCleanPackage(hElevatedPipe, pPackage);
3587 }
3588 else
3589 {
3590 hr = CacheRemovePackage(pCache, FALSE, pPackage->sczId, pPackage->sczCacheId);
3591 }
3592
3593 if (pPackage->fCanAffectRegistration)
3594 {
3595 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
3596 }
3597
3598 return hr;
3599 }
3600
3601 static int GenericExecuteMessageHandler(
3602 __in GENERIC_EXECUTE_MESSAGE* pMessage,
3603 __in LPVOID pvContext
3604 )
3605 {
3606 BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext;
3607 DWORD dwAllowedResults = pMessage->dwUIHint & MB_TYPEMASK;
3608 int nResult = IDNOACTION;
3609 BOOL fPassthrough = FALSE;
3610
3611 switch (pMessage->type)
3612 {
3613 case GENERIC_EXECUTE_MESSAGE_PROGRESS:
3614 {
3615 DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0;
3616 BACallbackOnExecuteProgress(pContext->pUX, pContext->wzExecutingPackageId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value.
3617 }
3618 break;
3619
3620 case GENERIC_EXECUTE_MESSAGE_PROCESS_CANCEL:
3621 {
3622 BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION action = BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION_ABANDON;
3623 BACallbackOnExecuteProcessCancel(pContext->pUX, pContext->wzExecutingPackageId, pMessage->processCancel.dwProcessId, &action); // ignore return value.
3624 nResult = BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION_WAIT == action ? IDRETRY : IDIGNORE;
3625 }
3626 break;
3627
3628 case GENERIC_EXECUTE_MESSAGE_PROCESS_STARTED:
3629 pContext->fAbandonedProcess = TRUE;
3630 break;
3631
3632 case GENERIC_EXECUTE_MESSAGE_PROCESS_COMPLETED:
3633 pContext->fAbandonedProcess = FALSE;
3634 break;
3635
3636 case GENERIC_EXECUTE_MESSAGE_ERROR:
3637 BACallbackOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE, pContext->wzExecutingPackageId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwUIHint, 0, NULL, &nResult); // ignore return value.
3638 break;
3639
3640 case GENERIC_EXECUTE_MESSAGE_NETFX_FILES_IN_USE:
3641 BACallbackOnExecuteFilesInUse(pContext->pUX, pContext->wzExecutingPackageId, pMessage->filesInUse.cFiles, pMessage->filesInUse.rgwzFiles, BOOTSTRAPPER_FILES_IN_USE_TYPE_NETFX, &nResult); // ignore return value.
3642 fPassthrough = TRUE;
3643 break;
3644 }
3645
3646 if (!fPassthrough)
3647 {
3648 nResult = BootstrapperApplicationCheckExecuteResult(pContext->pUX, pContext->fRollback, dwAllowedResults, nResult);
3649 }
3650
3651 return nResult;
3652 }
3653
3654 static int MsiExecuteMessageHandler(
3655 __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
3656 __in_opt LPVOID pvContext
3657 )
3658 {
3659 BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext;
3660 DWORD dwAllowedResults = pMessage->dwUIHint & MB_TYPEMASK;
3661 int nResult = IDNOACTION;
3662 BOOL fRestartManager = FALSE;
3663 BOOL fPassthrough = FALSE;
3664
3665 switch (pMessage->type)
3666 {
3667 case WIU_MSI_EXECUTE_MESSAGE_PROGRESS:
3668 {
3669 DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0;
3670 BACallbackOnExecuteProgress(pContext->pUX, pContext->wzExecutingPackageId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value.
3671 }
3672 break;
3673
3674 case WIU_MSI_EXECUTE_MESSAGE_ERROR:
3675 nResult = pMessage->nResultRecommendation;
3676 BACallbackOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER, pContext->wzExecutingPackageId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwUIHint, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value.
3677 break;
3678
3679 case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE:
3680 nResult = pMessage->nResultRecommendation;
3681 BACallbackOnExecuteMsiMessage(pContext->pUX, pContext->wzExecutingPackageId, pMessage->msiMessage.mt, pMessage->dwUIHint, pMessage->msiMessage.wzMessage, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value.
3682 break;
3683
3684 case WIU_MSI_EXECUTE_MESSAGE_MSI_RM_FILES_IN_USE:
3685 fRestartManager = TRUE;
3686 __fallthrough;
3687 case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE:
3688 BACallbackOnExecuteFilesInUse(pContext->pUX, pContext->wzExecutingPackageId, pMessage->msiFilesInUse.cFiles, pMessage->msiFilesInUse.rgwzFiles, fRestartManager ? BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI_RM : BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI, &nResult); // ignore return value.
3689 fPassthrough = TRUE;
3690 break;
3691 }
3692
3693 if (!fPassthrough)
3694 {
3695 nResult = BootstrapperApplicationCheckExecuteResult(pContext->pUX, pContext->fRollback, dwAllowedResults, nResult);
3696 }
3697
3698 return nResult;
3699 }
3700
3701 static HRESULT ReportOverallProgressTicks(
3702 __in BURN_USER_EXPERIENCE* pUX,
3703 __in BOOL fRollback,
3704 __in DWORD cOverallProgressTicksTotal,
3705 __in BURN_APPLY_CONTEXT* pApplyContext
3706 )
3707 {
3708 HRESULT hr = S_OK;
3709 DWORD dwProgress = 0;
3710
3711 ::EnterCriticalSection(&pApplyContext->csApply);
3712
3713 pApplyContext->cOverallProgressTicks += fRollback ? -1 : 1;
3714
3715 dwProgress = cOverallProgressTicksTotal ? (pApplyContext->cOverallProgressTicks * 100 / cOverallProgressTicksTotal) : 0;
3716
3717 // TODO: consider sending different progress numbers in the future.
3718 hr = BACallbackOnProgress(pUX, fRollback, dwProgress, dwProgress);
3719
3720 ::LeaveCriticalSection(&pApplyContext->csApply);
3721
3722 return hr;
3723 }
3724
3725 static HRESULT ExecutePackageComplete(
3726 __in BURN_ENGINE_STATE* pEngineState,
3727 __in LPCWSTR wzPackageId,
3728 __in BOOL fPackageVital,
3729 __in BOOL fAbandonedProcess,
3730 __in HRESULT hrOverall,
3731 __in HRESULT hrExecute,
3732 __in BOOL fRollback,
3733 __out BOOTSTRAPPER_APPLY_RESTART* pRestart,
3734 __out BOOL* pfRetry,
3735 __out BOOL* pfSuspend
3736 )
3737 {
3738 HRESULT hr = FAILED(hrOverall) ? hrOverall : hrExecute; // if the overall function failed use that otherwise use the execution result.
3739 BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION executePackageCompleteAction = FAILED(hrOverall) || SUCCEEDED(hrExecute) || fPackageVital ? BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE;
3740 BURN_USER_EXPERIENCE* pUX = &pEngineState->userExperience;
3741 BURN_VARIABLES* pVariables = &pEngineState->variables;
3742
3743 if (pEngineState->fCriticalShutdownInitiated)
3744 {
3745 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
3746 }
3747
3748 // Send package execute complete to BA.
3749 BACallbackOnExecutePackageComplete(pUX, wzPackageId, hr, *pRestart, &executePackageCompleteAction);
3750 if (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RESTART == executePackageCompleteAction)
3751 {
3752 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
3753 }
3754 *pfRetry = (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RETRY == executePackageCompleteAction &&
3755 FAILED(hrExecute) && !fAbandonedProcess && BOOTSTRAPPER_APPLY_RESTART_INITIATED != *pRestart); // allow retry only on non-fatal failures.
3756 *pfSuspend = (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_SUSPEND == executePackageCompleteAction);
3757
3758 // Remember this package as the package that initiated the forced restart.
3759 if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart && !pEngineState->wzRestartInitiatedPackageId)
3760 {
3761 pEngineState->wzRestartInitiatedPackageId = wzPackageId;
3762
3763 // Best effort to set the forced restart package variable.
3764 VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, wzPackageId, TRUE, FALSE);
3765 }
3766
3767 // If we're retrying, leave a message in the log file and say everything is okay.
3768 if (*pfRetry)
3769 {
3770 LogId(REPORT_STANDARD, MSG_APPLY_RETRYING_PACKAGE, wzPackageId, hrExecute);
3771 hr = S_OK;
3772 }
3773 else if (SUCCEEDED(hrOverall) && FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE == executePackageCompleteAction && !fPackageVital) // If we *only* failed to execute and the BA ignored this *not-vital* package, say everything is okay.
3774 {
3775 LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, wzPackageId, hrExecute);
3776 hr = S_OK;
3777 }
3778 else
3779 {
3780 LogId(REPORT_STANDARD, MSG_APPLY_COMPLETED_PACKAGE, LoggingRollbackOrExecute(fRollback), wzPackageId, hr, LoggingRestartToString(*pRestart));
3781 }
3782
3783 return hr;
3784 }