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