aboutsummaryrefslogtreecommitdiff
path: root/src/engine/plan.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/plan.cpp')
-rw-r--r--src/engine/plan.cpp3169
1 files changed, 3169 insertions, 0 deletions
diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp
new file mode 100644
index 00000000..01c7a31d
--- /dev/null
+++ b/src/engine/plan.cpp
@@ -0,0 +1,3169 @@
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// internal struct definitions
6
7struct PLAN_NONPERMANENT_PACKAGE_INDICES
8{
9 DWORD iAfterExecuteFirstNonPermanentPackage;
10 DWORD iBeforeRollbackFirstNonPermanentPackage;
11 DWORD iAfterExecuteLastNonPermanentPackage;
12 DWORD iAfterRollbackLastNonPermanentPackage;
13};
14
15// internal function definitions
16
17static void UninitializeRegistrationAction(
18 __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction
19 );
20static void UninitializeCacheAction(
21 __in BURN_CACHE_ACTION* pCacheAction
22 );
23static void ResetPlannedPackageState(
24 __in BURN_PACKAGE* pPackage
25 );
26static HRESULT ProcessPackage(
27 __in BOOL fBundlePerMachine,
28 __in BURN_PACKAGE* pCompatiblePackageParent,
29 __in BURN_USER_EXPERIENCE* pUX,
30 __in BURN_PLAN* pPlan,
31 __in BURN_PACKAGE* pPackage,
32 __in BURN_LOGGING* pLog,
33 __in BURN_VARIABLES* pVariables,
34 __in BOOTSTRAPPER_DISPLAY display,
35 __in BOOTSTRAPPER_RELATION_TYPE relationType,
36 __in_z_opt LPCWSTR wzLayoutDirectory,
37 __inout HANDLE* phSyncpointEvent,
38 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary,
39 __in_opt PLAN_NONPERMANENT_PACKAGE_INDICES* pNonpermanentPackageIndices
40 );
41static HRESULT ProcessPackageRollbackBoundary(
42 __in BURN_PLAN* pPlan,
43 __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary,
44 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
45 );
46static HRESULT GetActionDefaultRequestState(
47 __in BOOTSTRAPPER_ACTION action,
48 __in BOOL fPermanent,
49 __in BOOTSTRAPPER_PACKAGE_STATE currentState,
50 __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
51 );
52static HRESULT AddRegistrationAction(
53 __in BURN_PLAN* pPlan,
54 __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type,
55 __in_z LPCWSTR wzDependentProviderKey,
56 __in_z LPCWSTR wzOwnerBundleId
57 );
58static HRESULT AddCachePackage(
59 __in BURN_PLAN* pPlan,
60 __in BURN_PACKAGE* pPackage,
61 __out HANDLE* phSyncpointEvent
62 );
63static HRESULT AddCachePackageHelper(
64 __in BURN_PLAN* pPlan,
65 __in BURN_PACKAGE* pPackage,
66 __out HANDLE* phSyncpointEvent
67 );
68static HRESULT AddCacheSlipstreamMsps(
69 __in BURN_PLAN* pPlan,
70 __in BURN_PACKAGE* pPackage
71 );
72static BOOL AlreadyPlannedCachePackage(
73 __in BURN_PLAN* pPlan,
74 __in_z LPCWSTR wzPackageId,
75 __out HANDLE* phSyncpointEvent
76 );
77static DWORD GetNextCheckpointId();
78static HRESULT AppendCacheAction(
79 __in BURN_PLAN* pPlan,
80 __out BURN_CACHE_ACTION** ppCacheAction
81 );
82static HRESULT AppendRollbackCacheAction(
83 __in BURN_PLAN* pPlan,
84 __out BURN_CACHE_ACTION** ppCacheAction
85 );
86static HRESULT AppendLayoutContainerAction(
87 __in BURN_PLAN* pPlan,
88 __in_opt BURN_PACKAGE* pPackage,
89 __in DWORD iPackageStartAction,
90 __in BURN_CONTAINER* pContainer,
91 __in BOOL fContainerCached,
92 __in_z LPCWSTR wzLayoutDirectory
93 );
94static HRESULT AppendCacheOrLayoutPayloadAction(
95 __in BURN_PLAN* pPlan,
96 __in_opt BURN_PACKAGE* pPackage,
97 __in DWORD iPackageStartAction,
98 __in BURN_PAYLOAD* pPayload,
99 __in BOOL fPayloadCached,
100 __in_z_opt LPCWSTR wzLayoutDirectory
101 );
102static BOOL FindContainerCacheAction(
103 __in BURN_CACHE_ACTION_TYPE type,
104 __in BURN_PLAN* pPlan,
105 __in BURN_CONTAINER* pContainer,
106 __in DWORD iSearchStart,
107 __in DWORD iSearchEnd,
108 __out_opt BURN_CACHE_ACTION** ppCacheAction,
109 __out_opt DWORD* piCacheAction
110 );
111static HRESULT CreateContainerAcquireAndExtractAction(
112 __in BURN_PLAN* pPlan,
113 __in BURN_CONTAINER* pContainer,
114 __in DWORD iPackageStartAction,
115 __in BOOL fPayloadCached,
116 __out BURN_CACHE_ACTION** ppContainerExtractAction,
117 __out DWORD* piContainerTryAgainAction
118 );
119static HRESULT AddAcquireContainer(
120 __in BURN_PLAN* pPlan,
121 __in BURN_CONTAINER* pContainer,
122 __out_opt BURN_CACHE_ACTION** ppCacheAction,
123 __out_opt DWORD* piCacheAction
124 );
125static HRESULT AddExtractPayload(
126 __in BURN_CACHE_ACTION* pCacheAction,
127 __in_opt BURN_PACKAGE* pPackage,
128 __in BURN_PAYLOAD* pPayload,
129 __in_z LPCWSTR wzPayloadWorkingPath
130 );
131static BURN_CACHE_ACTION* ProcessSharedPayload(
132 __in BURN_PLAN* pPlan,
133 __in BURN_PAYLOAD* pPayload
134 );
135static HRESULT RemoveUnnecessaryActions(
136 __in BOOL fExecute,
137 __in BURN_EXECUTE_ACTION* rgActions,
138 __in DWORD cActions
139 );
140static HRESULT FinalizeSlipstreamPatchActions(
141 __in BOOL fExecute,
142 __in BURN_EXECUTE_ACTION* rgActions,
143 __in DWORD cActions
144 );
145static HRESULT PlanDependencyActions(
146 __in BOOL fBundlePerMachine,
147 __in BURN_PLAN* pPlan,
148 __in BURN_PACKAGE* pPackage
149 );
150static HRESULT CalculateExecuteActions(
151 __in BURN_USER_EXPERIENCE* pUserExperience,
152 __in BURN_PACKAGE* pPackage,
153 __in BURN_VARIABLES* pVariables,
154 __out_opt BOOL* pfBARequestedCache
155 );
156static BOOL NeedsCache(
157 __in BURN_PLAN* pPlan,
158 __in BURN_PACKAGE* pPackage
159 );
160static HRESULT CreateContainerProgress(
161 __in BURN_PLAN* pPlan,
162 __in BURN_CONTAINER* pContainer,
163 __out BURN_CACHE_CONTAINER_PROGRESS** ppContainerProgress
164 );
165static HRESULT CreatePayloadProgress(
166 __in BURN_PLAN* pPlan,
167 __in BURN_PAYLOAD* pPayload,
168 __out BURN_CACHE_PAYLOAD_PROGRESS** ppPayloadProgress
169 );
170
171// function definitions
172
173extern "C" void PlanReset(
174 __in BURN_PLAN* pPlan,
175 __in BURN_PACKAGES* pPackages
176 )
177{
178 if (pPlan->rgRegistrationActions)
179 {
180 for (DWORD i = 0; i < pPlan->cRegistrationActions; ++i)
181 {
182 UninitializeRegistrationAction(&pPlan->rgRegistrationActions[i]);
183 }
184 MemFree(pPlan->rgRegistrationActions);
185 }
186
187 if (pPlan->rgRollbackRegistrationActions)
188 {
189 for (DWORD i = 0; i < pPlan->cRollbackRegistrationActions; ++i)
190 {
191 UninitializeRegistrationAction(&pPlan->rgRollbackRegistrationActions[i]);
192 }
193 MemFree(pPlan->rgRollbackRegistrationActions);
194 }
195
196 if (pPlan->rgCacheActions)
197 {
198 for (DWORD i = 0; i < pPlan->cCacheActions; ++i)
199 {
200 UninitializeCacheAction(&pPlan->rgCacheActions[i]);
201 }
202 MemFree(pPlan->rgCacheActions);
203 }
204
205 if (pPlan->rgExecuteActions)
206 {
207 for (DWORD i = 0; i < pPlan->cExecuteActions; ++i)
208 {
209 PlanUninitializeExecuteAction(&pPlan->rgExecuteActions[i]);
210 }
211 MemFree(pPlan->rgExecuteActions);
212 }
213
214 if (pPlan->rgRollbackActions)
215 {
216 for (DWORD i = 0; i < pPlan->cRollbackActions; ++i)
217 {
218 PlanUninitializeExecuteAction(&pPlan->rgRollbackActions[i]);
219 }
220 MemFree(pPlan->rgRollbackActions);
221 }
222
223 if (pPlan->rgCleanActions)
224 {
225 // Nothing needs to be freed inside clean actions today.
226 MemFree(pPlan->rgCleanActions);
227 }
228
229 if (pPlan->rgPlannedProviders)
230 {
231 ReleaseDependencyArray(pPlan->rgPlannedProviders, pPlan->cPlannedProviders);
232 }
233
234 if (pPlan->rgContainerProgress)
235 {
236 MemFree(pPlan->rgContainerProgress);
237 }
238
239 if (pPlan->shContainerProgress)
240 {
241 ReleaseDict(pPlan->shContainerProgress);
242 }
243
244 if (pPlan->rgPayloadProgress)
245 {
246 MemFree(pPlan->rgPayloadProgress);
247 }
248
249 if (pPlan->shPayloadProgress)
250 {
251 ReleaseDict(pPlan->shPayloadProgress);
252 }
253
254 memset(pPlan, 0, sizeof(BURN_PLAN));
255
256 // Reset the planned actions for each package.
257 if (pPackages->rgPackages)
258 {
259 for (DWORD i = 0; i < pPackages->cPackages; ++i)
260 {
261 ResetPlannedPackageState(&pPackages->rgPackages[i]);
262 }
263 }
264}
265
266extern "C" void PlanUninitializeExecuteAction(
267 __in BURN_EXECUTE_ACTION* pExecuteAction
268 )
269{
270 switch (pExecuteAction->type)
271 {
272 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
273 ReleaseStr(pExecuteAction->exePackage.sczIgnoreDependencies);
274 ReleaseStr(pExecuteAction->exePackage.sczAncestors);
275 break;
276
277 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
278 ReleaseStr(pExecuteAction->msiPackage.sczLogPath);
279 ReleaseMem(pExecuteAction->msiPackage.rgFeatures);
280 ReleaseMem(pExecuteAction->msiPackage.rgSlipstreamPatches);
281 ReleaseMem(pExecuteAction->msiPackage.rgOrderedPatches);
282 break;
283
284 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
285 ReleaseStr(pExecuteAction->mspTarget.sczTargetProductCode);
286 ReleaseStr(pExecuteAction->mspTarget.sczLogPath);
287 ReleaseMem(pExecuteAction->mspTarget.rgOrderedPatches);
288 break;
289
290 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
291 ReleaseStr(pExecuteAction->msuPackage.sczLogPath);
292 break;
293
294 case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough;
295 case BURN_EXECUTE_ACTION_TYPE_SERVICE_START:
296 ReleaseStr(pExecuteAction->service.sczServiceName);
297 break;
298
299 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
300 ReleaseStr(pExecuteAction->packageDependency.sczBundleProviderKey);
301 break;
302
303 case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE:
304 ReleaseStr(pExecuteAction->compatiblePackage.sczInstalledProductCode);
305 break;
306 }
307}
308
309extern "C" HRESULT PlanSetVariables(
310 __in BOOTSTRAPPER_ACTION action,
311 __in BURN_VARIABLES* pVariables
312 )
313{
314 HRESULT hr = S_OK;
315
316 hr = VariableSetNumeric(pVariables, BURN_BUNDLE_ACTION, action, TRUE);
317 ExitOnFailure(hr, "Failed to set the bundle action built-in variable.");
318
319LExit:
320 return hr;
321}
322
323extern "C" HRESULT PlanDefaultPackageRequestState(
324 __in BURN_PACKAGE_TYPE packageType,
325 __in BOOTSTRAPPER_PACKAGE_STATE currentState,
326 __in BOOL fPermanent,
327 __in BOOTSTRAPPER_ACTION action,
328 __in BURN_VARIABLES* pVariables,
329 __in_z_opt LPCWSTR wzInstallCondition,
330 __in BOOTSTRAPPER_RELATION_TYPE relationType,
331 __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
332 )
333{
334 HRESULT hr = S_OK;
335 BOOTSTRAPPER_REQUEST_STATE defaultRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
336 BOOL fCondition = FALSE;
337
338 // If doing layout, then always default to requesting the file be cached.
339 if (BOOTSTRAPPER_ACTION_LAYOUT == action)
340 {
341 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE;
342 }
343 else if (BOOTSTRAPPER_RELATION_PATCH == relationType && BURN_PACKAGE_TYPE_MSP == packageType)
344 {
345 // For patch related bundles, only install a patch if currently absent during install, modify, or repair.
346 if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == currentState && BOOTSTRAPPER_ACTION_INSTALL <= action)
347 {
348 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
349 }
350 else
351 {
352 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
353 }
354 }
355 else if (BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == currentState && BOOTSTRAPPER_ACTION_UNINSTALL != action)
356 {
357 // Superseded means the package is on the machine but not active, so only uninstall operations are allowed.
358 // All other operations do nothing.
359 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
360 }
361 else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == currentState && !(BOOTSTRAPPER_ACTION_UNINSTALL == action && BURN_PACKAGE_TYPE_MSP == packageType))
362 {
363 // Obsolete means the package is not on the machine and should not be installed, *except* patches can be obsolete
364 // and present so allow them to be removed during uninstall. Everyone else, gets nothing.
365 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
366 }
367 else // pick the best option for the action state and install condition.
368 {
369 hr = GetActionDefaultRequestState(action, fPermanent, currentState, &defaultRequestState);
370 ExitOnFailure(hr, "Failed to get default request state for action.");
371
372 // If there is an install condition (and we're doing an install) evaluate the condition
373 // to determine whether to use the default request state or make the package absent.
374 if (BOOTSTRAPPER_ACTION_UNINSTALL != action && wzInstallCondition && *wzInstallCondition)
375 {
376 hr = ConditionEvaluate(pVariables, wzInstallCondition, &fCondition);
377 ExitOnFailure(hr, "Failed to evaluate install condition.");
378
379 *pRequestState = fCondition ? defaultRequestState : BOOTSTRAPPER_REQUEST_STATE_ABSENT;
380 }
381 else // just set the package to the default request state.
382 {
383 *pRequestState = defaultRequestState;
384 }
385 }
386
387LExit:
388 return hr;
389}
390
391extern "C" HRESULT PlanLayoutBundle(
392 __in BURN_PLAN* pPlan,
393 __in_z LPCWSTR wzExecutableName,
394 __in DWORD64 qwBundleSize,
395 __in BURN_VARIABLES* pVariables,
396 __in BURN_PAYLOADS* pPayloads,
397 __out_z LPWSTR* psczLayoutDirectory
398 )
399{
400 HRESULT hr = S_OK;
401 BURN_CACHE_ACTION* pCacheAction = NULL;
402 LPWSTR sczExecutablePath = NULL;
403 LPWSTR sczLayoutDirectory = NULL;
404
405 // Get the layout directory.
406 hr = VariableGetString(pVariables, BURN_BUNDLE_LAYOUT_DIRECTORY, &sczLayoutDirectory);
407 if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory.
408 {
409 hr = VariableGetString(pVariables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, &sczLayoutDirectory);
410 if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory.
411 {
412 hr = PathForCurrentProcess(&sczExecutablePath, NULL);
413 ExitOnFailure(hr, "Failed to get path for current executing process as layout directory.");
414
415 hr = PathGetDirectory(sczExecutablePath, &sczLayoutDirectory);
416 ExitOnFailure(hr, "Failed to get executing process as layout directory.");
417 }
418 }
419 ExitOnFailure(hr, "Failed to get bundle layout directory property.");
420
421 hr = PathBackslashTerminate(&sczLayoutDirectory);
422 ExitOnFailure(hr, "Failed to ensure layout directory is backslash terminated.");
423
424 // Plan the layout of the bundle engine itself.
425 hr = AppendCacheAction(pPlan, &pCacheAction);
426 ExitOnFailure(hr, "Failed to append bundle start action.");
427
428 pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE;
429
430 hr = StrAllocString(&pCacheAction->bundleLayout.sczExecutableName, wzExecutableName, 0);
431 ExitOnFailure(hr, "Failed to to copy executable name for bundle.");
432
433 hr = StrAllocString(&pCacheAction->bundleLayout.sczLayoutDirectory, sczLayoutDirectory, 0);
434 ExitOnFailure(hr, "Failed to to copy layout directory for bundle.");
435
436 hr = CacheCalculateBundleLayoutWorkingPath(pPlan->wzBundleId, &pCacheAction->bundleLayout.sczUnverifiedPath);
437 ExitOnFailure(hr, "Failed to calculate bundle layout working path.");
438
439 pCacheAction->bundleLayout.qwBundleSize = qwBundleSize;
440
441 pPlan->qwCacheSizeTotal += qwBundleSize;
442
443 ++pPlan->cOverallProgressTicksTotal;
444
445 // Plan the layout of layout-only payloads.
446 for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
447 {
448 BURN_PAYLOAD* pPayload = pPayloads->rgPayloads + i;
449 if (pPayload->fLayoutOnly)
450 {
451 // TODO: determine if a payload already exists in the layout and pass appropriate value fPayloadCached
452 // (instead of always FALSE).
453 hr = AppendCacheOrLayoutPayloadAction(pPlan, NULL, BURN_PLAN_INVALID_ACTION_INDEX, pPayload, FALSE, sczLayoutDirectory);
454 ExitOnFailure(hr, "Failed to plan layout payload.");
455 }
456 }
457
458 *psczLayoutDirectory = sczLayoutDirectory;
459 sczLayoutDirectory = NULL;
460
461LExit:
462 ReleaseStr(sczLayoutDirectory);
463 ReleaseStr(sczExecutablePath);
464
465 return hr;
466}
467
468extern "C" HRESULT PlanPackages(
469 __in BURN_REGISTRATION* pRegistration,
470 __in BURN_USER_EXPERIENCE* pUX,
471 __in BURN_PACKAGES* pPackages,
472 __in BURN_PLAN* pPlan,
473 __in BURN_LOGGING* pLog,
474 __in BURN_VARIABLES* pVariables,
475 __in BOOL fBundleInstalled,
476 __in BOOTSTRAPPER_DISPLAY display,
477 __in BOOTSTRAPPER_RELATION_TYPE relationType,
478 __in_z_opt LPCWSTR wzLayoutDirectory,
479 __inout HANDLE* phSyncpointEvent
480 )
481{
482 HRESULT hr = S_OK;
483 BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine.
484 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
485
486 PLAN_NONPERMANENT_PACKAGE_INDICES nonpermanentPackageIndices;
487 nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX;
488 nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX;
489 nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX;
490 nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX;
491
492 // Plan the packages.
493 for (DWORD i = 0; i < pPackages->cPackages; ++i)
494 {
495 DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cPackages - 1 - i : i;
496 BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage;
497
498 // Support passing Ancestors to embedded burn bundles
499 if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol)
500 {
501 // Pass along any ancestors and ourself to prevent infinite loops.
502 if (pRegistration->sczAncestors && *pRegistration->sczAncestors)
503 {
504 hr = StrAllocFormatted(&pPackage->Exe.sczAncestors, L"%ls;%ls", pRegistration->sczAncestors, pRegistration->sczId);
505 ExitOnFailure(hr, "Failed to copy ancestors and self to related bundle ancestors.");
506 }
507 else
508 {
509 hr = StrAllocString(&pPackage->Exe.sczAncestors, pRegistration->sczId, 0);
510 ExitOnFailure(hr, "Failed to copy self to related bundle ancestors.");
511 }
512 }
513
514 hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices);
515 ExitOnFailure(hr, "Failed to process package.");
516
517 // Attempt to remove orphaned packages during uninstall. Currently only MSI packages are supported and should not require source.
518 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.fCompatibleInstalled)
519 {
520 BURN_PACKAGE* pCompatiblePackage = NULL;
521 BURN_EXECUTE_ACTION* pAction = NULL;
522
523 // Add the compatible package to the list.
524 hr = MsiEngineAddCompatiblePackage(pPackages, pPackage, &pCompatiblePackage);
525 ExitOnFailure(hr, "Failed to add compatible package for package: %ls", pPackage->sczId);
526
527 // Plan to load the compatible package into the elevated engine before its needed.
528 hr = PlanAppendExecuteAction(pPlan, &pAction);
529 ExitOnFailure(hr, "Failed to append execute action.");
530
531 pAction->type = BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE;
532 pAction->compatiblePackage.pReferencePackage = pPackage;
533 pAction->compatiblePackage.qwInstalledVersion = pCompatiblePackage->Msi.qwVersion;
534
535 hr = StrAllocString(&pAction->compatiblePackage.sczInstalledProductCode, pCompatiblePackage->Msi.sczProductCode, 0);
536 ExitOnFailure(hr, "Failed to copy installed ProductCode");
537
538 // Process the compatible MSI package like any other.
539 hr = ProcessPackage(fBundlePerMachine, pPackage, pUX, pPlan, pCompatiblePackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices);
540 ExitOnFailure(hr, "Failed to process compatible package.");
541
542 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pCompatiblePackage->execute)
543 {
544 LogId(REPORT_STANDARD, MSG_PLANNED_ORPHAN_PACKAGE_FROM_PROVIDER, pPackage->sczId, pCompatiblePackage->Msi.sczProductCode, pPackage->Msi.sczProductCode);
545 }
546 }
547 }
548
549 // Insert the "keep registration" and "remove registration" actions in the plan when installing the first time and anytime we are uninstalling respectively.
550 if (!fBundleInstalled && (BOOTSTRAPPER_ACTION_INSTALL == pPlan->action || BOOTSTRAPPER_ACTION_MODIFY == pPlan->action || BOOTSTRAPPER_ACTION_REPAIR == pPlan->action))
551 {
552 if (BURN_PLAN_INVALID_ACTION_INDEX == nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage)
553 {
554 nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage = pPlan->cExecuteActions;
555 nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage = pPlan->cRollbackActions;
556 }
557
558 hr = PlanKeepRegistration(pPlan, nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage, nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage);
559 ExitOnFailure(hr, "Failed to plan install keep registration.");
560 }
561 else if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
562 {
563 if (BURN_PLAN_INVALID_ACTION_INDEX == nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage)
564 {
565 nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage = pPlan->cExecuteActions;
566 nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage = pPlan->cRollbackActions;
567 }
568
569 hr = PlanRemoveRegistration(pPlan, nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage, nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage);
570 ExitOnFailure(hr, "Failed to plan uninstall remove registration.");
571 }
572
573 // If we still have an open rollback boundary, complete it.
574 if (pRollbackBoundary)
575 {
576 hr = PlanRollbackBoundaryComplete(pPlan);
577 ExitOnFailure(hr, "Failed to plan rollback boundary begin.");
578
579 pRollbackBoundary = NULL;
580 }
581
582 // Plan clean up of packages.
583 for (DWORD i = 0; i < pPackages->cPackages; ++i)
584 {
585 DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cPackages - 1 - i : i;
586 BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage;
587
588 hr = PlanCleanPackage(pPlan, pPackage);
589 ExitOnFailure(hr, "Failed to plan clean package.");
590 }
591
592 // Plan best-effort clean up of compatible packages.
593 for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i)
594 {
595 DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cCompatiblePackages - 1 - i : i;
596 BURN_PACKAGE* pCompatiblePackage = pPackages->rgCompatiblePackages + iPackage;
597
598 PlanCleanPackage(pPlan, pCompatiblePackage);
599 }
600
601LExit:
602 return hr;
603}
604
605extern "C" HRESULT PlanRegistration(
606 __in BURN_PLAN* pPlan,
607 __in BURN_REGISTRATION* pRegistration,
608 __in BOOTSTRAPPER_RESUME_TYPE resumeType,
609 __in BOOTSTRAPPER_RELATION_TYPE relationType,
610 __in_z_opt LPCWSTR wzIgnoreDependencies,
611 __out BOOL* pfContinuePlanning
612 )
613{
614 HRESULT hr = S_OK;
615 LPCWSTR wzSelfDependent = NULL;
616 STRINGDICT_HANDLE sdIgnoreDependents = NULL;
617 DEPENDENCY* rgDependencies = NULL;
618 UINT cDependencies = 0;
619
620 pPlan->fRegister = TRUE; // register the bundle since we're modifying machine state.
621
622 // Keep the registration if the bundle was already installed or we are planning after a restart.
623 pPlan->fKeepRegistrationDefault = (pRegistration->fInstalled || BOOTSTRAPPER_RESUME_TYPE_REBOOT == resumeType);
624
625 pPlan->fDisallowRemoval = FALSE; // by default the bundle can be planned to be removed
626
627 // If no parent was specified at all, use the bundle id as the self dependent.
628 if (!pRegistration->sczActiveParent)
629 {
630 wzSelfDependent = pRegistration->sczId;
631 }
632 else if (*pRegistration->sczActiveParent) // if parent was specified use that as the self dependent.
633 {
634 wzSelfDependent = pRegistration->sczActiveParent;
635 }
636 // else parent:none was used which means we should not register a dependency on ourself.
637
638 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
639 {
640 // If our provider key was detected and it points to our current bundle then we can
641 // unregister the bundle dependency.
642 if (pRegistration->sczDetectedProviderKeyBundleId &&
643 CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczId, -1, pRegistration->sczDetectedProviderKeyBundleId, -1))
644 {
645 pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER;
646 }
647 else // log that another bundle already owned our registration, hopefully this only happens when a newer version
648 { // of a bundle installed and is in the process of upgrading us.
649 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL, pRegistration->sczProviderKey, pRegistration->sczDetectedProviderKeyBundleId);
650 }
651
652 // Create the dictionary of dependents that should be ignored.
653 hr = DictCreateStringList(&sdIgnoreDependents, 5, DICT_FLAG_CASEINSENSITIVE);
654 ExitOnFailure(hr, "Failed to create the string dictionary.");
655
656 // If the self-dependent dependent exists, plan its removal. If we did not do this, we
657 // would prevent self-removal.
658 if (wzSelfDependent && DependencyDependentExists(pRegistration, wzSelfDependent))
659 {
660 hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER, wzSelfDependent, pRegistration->sczId);
661 ExitOnFailure(hr, "Failed to allocate registration action.");
662
663 hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, wzSelfDependent);
664 ExitOnFailure(hr, "Failed to add self-dependent to ignore dependents.");
665 }
666
667 // If we are not doing an upgrade, we check to see if there are still dependents on us and if so we skip planning.
668 // However, when being upgraded, we always execute our uninstall because a newer version of us is probably
669 // already on the machine and we need to clean up the stuff specific to this bundle.
670 if (BOOTSTRAPPER_RELATION_UPGRADE != relationType)
671 {
672 // If there were other dependencies to ignore, add them.
673 if (wzIgnoreDependencies && *wzIgnoreDependencies)
674 {
675 hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, wzIgnoreDependencies);
676 ExitOnFailure(hr, "Failed to add dependents ignored from command-line.");
677 }
678
679 // For addon or patch bundles, dependent related bundles should be ignored. This allows
680 // that addon or patch to be removed even though bundles it targets still are registered.
681 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
682 {
683 const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
684
685 if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType)
686 {
687 for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j)
688 {
689 const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j;
690
691 hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pProvider->sczKey);
692 ExitOnFailure(hr, "Failed to add dependent bundle provider key to ignore dependents.");
693 }
694 }
695 }
696
697 // If there are any (non-ignored and not-planned-to-be-removed) dependents left, uninstall.
698 hr = DepCheckDependents(pRegistration->hkRoot, pRegistration->sczProviderKey, 0, sdIgnoreDependents, &rgDependencies, &cDependencies);
699 if (E_FILENOTFOUND == hr)
700 {
701 hr = S_OK;
702 }
703 else if (SUCCEEDED(hr) && cDependencies)
704 {
705 // TODO: callback to the BA and let it have the option to ignore any of these dependents?
706
707 pPlan->fDisallowRemoval = TRUE; // ensure the registration stays
708 *pfContinuePlanning = FALSE; // skip the rest of planning.
709
710 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS, cDependencies);
711 }
712 ExitOnFailure(hr, "Failed to check for remaining dependents during planning.");
713 }
714 }
715 else
716 {
717 BOOL fAddonOrPatchBundle = (pRegistration->cAddonCodes || pRegistration->cPatchCodes);
718
719 // If the bundle is not cached or will not be cached after restart, ensure the bundle is cached.
720 if (!FileExistsAfterRestart(pRegistration->sczCacheExecutablePath, NULL))
721 {
722 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE;
723 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION;
724 }
725 else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action && !CacheBundleRunningFromCache()) // repairing but not not running from the cache.
726 {
727 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE;
728 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION;
729 }
730 else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action) // just repair, make sure the registration is "fixed up".
731 {
732 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION;
733 }
734
735 // Always update our estimated size registration when installing/modify/repair since things
736 // may have been added or removed or it just needs to be "fixed up".
737 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE;
738
739 // Always plan to write our provider key registration when installing/modify/repair to "fix it"
740 // if broken.
741 pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER;
742
743 // Register each dependent related bundle. The ensures that addons and patches are reference
744 // counted and stick around until the last targeted bundle is removed.
745 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
746 {
747 const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
748
749 if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType)
750 {
751 for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j)
752 {
753 const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j;
754
755 if (!DependencyDependentExists(pRegistration, pProvider->sczKey))
756 {
757 hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, pProvider->sczKey, pRelatedBundle->package.sczId);
758 ExitOnFailure(hr, "Failed to add registration action for dependent related bundle.");
759 }
760 }
761 }
762 }
763
764 // Only do the following if we decided there was a dependent self to register. If so and and an explicit parent was
765 // provided, register dependent self. Otherwise, if this bundle is not an addon or patch bundle then self-regisiter
766 // as our own dependent.
767 if (wzSelfDependent && (pRegistration->sczActiveParent || !fAddonOrPatchBundle))
768 {
769 if (!DependencyDependentExists(pRegistration, wzSelfDependent))
770 {
771 hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, wzSelfDependent, pRegistration->sczId);
772 ExitOnFailure(hr, "Failed to add registration action for self dependent.");
773 }
774 }
775 }
776
777LExit:
778 ReleaseDict(sdIgnoreDependents);
779 ReleaseDependencyArray(rgDependencies, cDependencies);
780
781 return hr;
782}
783
784extern "C" HRESULT PlanPassThroughBundle(
785 __in BURN_USER_EXPERIENCE* pUX,
786 __in BURN_PACKAGE* pPackage,
787 __in BURN_PLAN* pPlan,
788 __in BURN_LOGGING* pLog,
789 __in BURN_VARIABLES* pVariables,
790 __in BOOTSTRAPPER_DISPLAY display,
791 __in BOOTSTRAPPER_RELATION_TYPE relationType,
792 __inout HANDLE* phSyncpointEvent
793 )
794{
795 HRESULT hr = S_OK;
796 BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine.
797 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
798
799 // Plan passthrough package.
800 hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL);
801 ExitOnFailure(hr, "Failed to process passthrough package.");
802
803 // If we still have an open rollback boundary, complete it.
804 if (pRollbackBoundary)
805 {
806 hr = PlanRollbackBoundaryComplete(pPlan);
807 ExitOnFailure(hr, "Failed to plan rollback boundary for passthrough package.");
808 }
809
810 // Notice that the PlanCleanPackage() function is purposefully missing here. Passthrough packages
811 // are never cleaned up by the calling bundle (they delete themselves when appropriate) so we don't
812 // need to plan clean up.
813
814LExit:
815 return hr;
816}
817
818extern "C" HRESULT PlanUpdateBundle(
819 __in BURN_USER_EXPERIENCE* pUX,
820 __in BURN_PACKAGE* pPackage,
821 __in BURN_PLAN* pPlan,
822 __in BURN_LOGGING* pLog,
823 __in BURN_VARIABLES* pVariables,
824 __in BOOTSTRAPPER_DISPLAY display,
825 __in BOOTSTRAPPER_RELATION_TYPE relationType,
826 __inout HANDLE* phSyncpointEvent
827 )
828{
829 HRESULT hr = S_OK;
830 BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine.
831 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
832
833 // Plan update package.
834 hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL);
835 ExitOnFailure(hr, "Failed to process update package.");
836
837 // If we still have an open rollback boundary, complete it.
838 if (pRollbackBoundary)
839 {
840 hr = PlanRollbackBoundaryComplete(pPlan);
841 ExitOnFailure(hr, "Failed to plan rollback boundary for update package.");
842 }
843
844 // Plan clean up of update package.
845 hr = PlanCleanPackage(pPlan, pPackage);
846 ExitOnFailure(hr, "Failed to plan clean of update package.");
847
848LExit:
849 return hr;
850}
851
852static HRESULT ProcessPackage(
853 __in BOOL fBundlePerMachine,
854 __in BURN_PACKAGE* pCompatiblePackageParent,
855 __in BURN_USER_EXPERIENCE* pUX,
856 __in BURN_PLAN* pPlan,
857 __in BURN_PACKAGE* pPackage,
858 __in BURN_LOGGING* pLog,
859 __in BURN_VARIABLES* pVariables,
860 __in BOOTSTRAPPER_DISPLAY display,
861 __in BOOTSTRAPPER_RELATION_TYPE relationType,
862 __in_z_opt LPCWSTR wzLayoutDirectory,
863 __inout HANDLE* phSyncpointEvent,
864 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary,
865 __in_opt PLAN_NONPERMANENT_PACKAGE_INDICES* pNonpermanentPackageIndices
866 )
867{
868 HRESULT hr = S_OK;
869 BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary = NULL;
870 BOOL fPlanPackageBegan = FALSE;
871
872 // Remember the default requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it.
873 hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, !pPackage->fUninstallable, pPlan->action, pVariables, pPackage->sczInstallCondition, relationType, &pPackage->defaultRequested);
874 ExitOnFailure(hr, "Failed to set default package state.");
875
876 pPackage->requested = pPackage->defaultRequested;
877 fPlanPackageBegan = TRUE;
878
879 if (pCompatiblePackageParent)
880 {
881 AssertSz(BURN_PACKAGE_TYPE_MSI == pPackage->type, "Currently only MSI packages have compatible packages.");
882
883 hr = UserExperienceOnPlanCompatibleMsiPackageBegin(pUX, pCompatiblePackageParent->sczId, pPackage->sczId, pPackage->Msi.qwVersion, &pPackage->requested);
884 ExitOnRootFailure(hr, "BA aborted plan compatible MSI package begin.");
885 }
886 else
887 {
888 hr = UserExperienceOnPlanPackageBegin(pUX, pPackage->sczId, &pPackage->requested);
889 ExitOnRootFailure(hr, "BA aborted plan package begin.");
890 }
891
892 pEffectiveRollbackBoundary = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackage->pRollbackBoundaryBackward : pPackage->pRollbackBoundaryForward;
893 hr = ProcessPackageRollbackBoundary(pPlan, pEffectiveRollbackBoundary, ppRollbackBoundary);
894 ExitOnFailure(hr, "Failed to process package rollback boundary.");
895
896 // If the package is in a requested state, plan it.
897 if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested)
898 {
899 if (BOOTSTRAPPER_ACTION_LAYOUT == pPlan->action)
900 {
901 hr = PlanLayoutPackage(pPlan, pPackage, wzLayoutDirectory);
902 ExitOnFailure(hr, "Failed to plan layout package.");
903 }
904 else
905 {
906 if (pPackage->fUninstallable && pNonpermanentPackageIndices)
907 {
908 if (BURN_PLAN_INVALID_ACTION_INDEX == pNonpermanentPackageIndices->iBeforeRollbackFirstNonPermanentPackage)
909 {
910 pNonpermanentPackageIndices->iBeforeRollbackFirstNonPermanentPackage = pPlan->cRollbackActions;
911 }
912 }
913
914 hr = PlanExecutePackage(fBundlePerMachine, display, pUX, pPlan, pPackage, pLog, pVariables, phSyncpointEvent);
915 ExitOnFailure(hr, "Failed to plan execute package.");
916
917 if (pPackage->fUninstallable && pNonpermanentPackageIndices)
918 {
919 if (BURN_PLAN_INVALID_ACTION_INDEX == pNonpermanentPackageIndices->iAfterExecuteFirstNonPermanentPackage)
920 {
921 pNonpermanentPackageIndices->iAfterExecuteFirstNonPermanentPackage = pPlan->cExecuteActions - 1;
922 }
923
924 pNonpermanentPackageIndices->iAfterExecuteLastNonPermanentPackage = pPlan->cExecuteActions;
925 pNonpermanentPackageIndices->iAfterRollbackLastNonPermanentPackage = pPlan->cRollbackActions;
926 }
927 }
928 }
929 else if (BOOTSTRAPPER_ACTION_LAYOUT != pPlan->action)
930 {
931 // All packages that have cacheType set to always should be cached if the bundle is going to be present.
932 if (BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType && BOOTSTRAPPER_ACTION_INSTALL <= pPlan->action)
933 {
934 hr = PlanCachePackage(fBundlePerMachine, pUX, pPlan, pPackage, pVariables, phSyncpointEvent);
935 ExitOnFailure(hr, "Failed to plan cache package.");
936 }
937 else
938 {
939 // Make sure the package is properly ref-counted even if no plan is requested.
940 hr = PlanDependencyActions(fBundlePerMachine, pPlan, pPackage);
941 ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId);
942 }
943 }
944
945 // Add the checkpoint after each package and dependency registration action.
946 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback || BURN_DEPENDENCY_ACTION_NONE != pPackage->dependencyExecute)
947 {
948 hr = PlanExecuteCheckpoint(pPlan);
949 ExitOnFailure(hr, "Failed to append execute checkpoint.");
950 }
951
952LExit:
953 if (fPlanPackageBegan)
954 {
955 if (pCompatiblePackageParent)
956 {
957 UserExperienceOnPlanCompatibleMsiPackageComplete(pUX, pCompatiblePackageParent->sczId, pPackage->sczId, hr, pPackage->currentState, pPackage->requested, pPackage->execute, pPackage->rollback);
958 }
959 else
960 {
961 UserExperienceOnPlanPackageComplete(pUX, pPackage->sczId, hr, pPackage->currentState, pPackage->requested, pPackage->execute, pPackage->rollback);
962 }
963 }
964
965 return hr;
966}
967
968static HRESULT ProcessPackageRollbackBoundary(
969 __in BURN_PLAN* pPlan,
970 __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary,
971 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
972 )
973{
974 HRESULT hr = S_OK;
975
976 // If the package marks the start of a rollback boundary, start a new one.
977 if (pEffectiveRollbackBoundary)
978 {
979 // Complete previous rollback boundary.
980 if (*ppRollbackBoundary)
981 {
982 hr = PlanRollbackBoundaryComplete(pPlan);
983 ExitOnFailure(hr, "Failed to plan rollback boundary complete.");
984 }
985
986 // Start new rollback boundary.
987 hr = PlanRollbackBoundaryBegin(pPlan, pEffectiveRollbackBoundary);
988 ExitOnFailure(hr, "Failed to plan rollback boundary begin.");
989
990 *ppRollbackBoundary = pEffectiveRollbackBoundary;
991 }
992
993LExit:
994 return hr;
995}
996
997extern "C" HRESULT PlanLayoutPackage(
998 __in BURN_PLAN* pPlan,
999 __in BURN_PACKAGE* pPackage,
1000 __in_z_opt LPCWSTR wzLayoutDirectory
1001 )
1002{
1003 HRESULT hr = S_OK;
1004 BURN_CACHE_ACTION* pCacheAction = NULL;
1005 DWORD iPackageStartAction = 0;
1006
1007 hr = AppendCacheAction(pPlan, &pCacheAction);
1008 ExitOnFailure(hr, "Failed to append package start action.");
1009
1010 pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_START;
1011 pCacheAction->packageStart.pPackage = pPackage;
1012
1013 // Remember the index for the package start action (which is now the last in the cache
1014 // actions array) because the array may be resized later and move around in memory.
1015 iPackageStartAction = pPlan->cCacheActions - 1;
1016
1017 // If any of the package payloads are not cached, add them to the plan.
1018 for (DWORD i = 0; i < pPackage->cPayloads; ++i)
1019 {
1020 BURN_PACKAGE_PAYLOAD* pPackagePayload = &pPackage->rgPayloads[i];
1021
1022 // If doing layout and the package is in a container.
1023 if (wzLayoutDirectory && pPackagePayload->pPayload->pContainer)
1024 {
1025 // TODO: determine if a container already exists in the layout and pass appropriate value fPayloadCached (instead of always FALSE).
1026 hr = AppendLayoutContainerAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload->pContainer, FALSE, wzLayoutDirectory);
1027 ExitOnFailure(hr, "Failed to append layout container action.");
1028 }
1029 else
1030 {
1031 // TODO: determine if a payload already exists in the layout and pass appropriate value fPayloadCached (instead of always FALSE).
1032 hr = AppendCacheOrLayoutPayloadAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload, FALSE, wzLayoutDirectory);
1033 ExitOnFailure(hr, "Failed to append cache/layout payload action.");
1034 }
1035
1036 Assert(BURN_CACHE_ACTION_TYPE_PACKAGE_START == pPlan->rgCacheActions[iPackageStartAction].type);
1037 ++pPlan->rgCacheActions[iPackageStartAction].packageStart.cCachePayloads;
1038 pPlan->rgCacheActions[iPackageStartAction].packageStart.qwCachePayloadSizeTotal += pPackagePayload->pPayload->qwFileSize;
1039 }
1040
1041 // Create package stop action.
1042 hr = AppendCacheAction(pPlan, &pCacheAction);
1043 ExitOnFailure(hr, "Failed to append cache action.");
1044
1045 pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_STOP;
1046 pCacheAction->packageStop.pPackage = pPackage;
1047
1048 // Update the start action with the location of the complete action.
1049 pPlan->rgCacheActions[iPackageStartAction].packageStart.iPackageCompleteAction = pPlan->cCacheActions - 1;
1050
1051 ++pPlan->cOverallProgressTicksTotal;
1052
1053LExit:
1054 return hr;
1055}
1056
1057extern "C" HRESULT PlanCachePackage(
1058 __in BOOL fPerMachine,
1059 __in BURN_USER_EXPERIENCE* pUserExperience,
1060 __in BURN_PLAN* pPlan,
1061 __in BURN_PACKAGE* pPackage,
1062 __in BURN_VARIABLES* pVariables,
1063 __out HANDLE* phSyncpointEvent
1064 )
1065{
1066 HRESULT hr = S_OK;
1067 BOOL fBARequestedCache = FALSE;
1068
1069 // Calculate the execute actions because we need them to decide whether the package should be cached.
1070 hr = CalculateExecuteActions(pUserExperience, pPackage, pVariables, &fBARequestedCache);
1071 ExitOnFailure(hr, "Failed to calculate execute actions for package: %ls", pPackage->sczId);
1072
1073 if (fBARequestedCache || NeedsCache(pPlan, pPackage))
1074 {
1075 hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent);
1076 ExitOnFailure(hr, "Failed to plan cache package.");
1077
1078 if (pPackage->fPerMachine)
1079 {
1080 pPlan->fPerMachine = TRUE;
1081 }
1082 }
1083
1084 // Make sure the package is properly ref-counted.
1085 hr = PlanDependencyActions(fPerMachine, pPlan, pPackage);
1086 ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId);
1087
1088LExit:
1089 return hr;
1090}
1091
1092extern "C" HRESULT PlanExecutePackage(
1093 __in BOOL fPerMachine,
1094 __in BOOTSTRAPPER_DISPLAY display,
1095 __in BURN_USER_EXPERIENCE* pUserExperience,
1096 __in BURN_PLAN* pPlan,
1097 __in BURN_PACKAGE* pPackage,
1098 __in BURN_LOGGING* pLog,
1099 __in BURN_VARIABLES* pVariables,
1100 __inout HANDLE* phSyncpointEvent
1101 )
1102{
1103 HRESULT hr = S_OK;
1104 BOOL fBARequestedCache = FALSE;
1105
1106 hr = CalculateExecuteActions(pUserExperience, pPackage, pVariables, &fBARequestedCache);
1107 ExitOnFailure(hr, "Failed to calculate plan actions for package: %ls", pPackage->sczId);
1108
1109 // Calculate package states based on reference count and plan certain dependency actions prior to planning the package execute action.
1110 hr = DependencyPlanPackageBegin(fPerMachine, pPackage, pPlan);
1111 ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId);
1112
1113 if (fBARequestedCache || NeedsCache(pPlan, pPackage))
1114 {
1115 hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent);
1116 ExitOnFailure(hr, "Failed to plan cache package.");
1117 }
1118 else if (BURN_CACHE_STATE_COMPLETE != pPackage->cache && // if the package is not in the cache, disable any rollback that would require the package from the cache.
1119 (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->rollback || (BURN_PACKAGE_TYPE_EXE == pPackage->type && BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback))
1120 )
1121 {
1122 LogId(REPORT_STANDARD, MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE, pPackage->sczId, LoggingCacheStateToString(pPackage->cache), LoggingActionStateToString(pPackage->rollback));
1123 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1124 }
1125
1126 // Add the cache and install size to estimated size if it will be on the machine at the end of the install
1127 if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested ||
1128 (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_ABSENT < pPackage->requested) ||
1129 BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType
1130 )
1131 {
1132 // If the package will remain in the cache, add the package size to the estimated size
1133 if (BURN_CACHE_TYPE_YES <= pPackage->cacheType)
1134 {
1135 pPlan->qwEstimatedSize += pPackage->qwSize;
1136 }
1137
1138 // If the package will end up installed on the machine, add the install size to the estimated size.
1139 if (BOOTSTRAPPER_REQUEST_STATE_CACHE < pPackage->requested)
1140 {
1141 // MSP packages get cached automatically by windows installer with any embedded cabs, so include that in the size as well
1142 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
1143 {
1144 pPlan->qwEstimatedSize += pPackage->qwSize;
1145 }
1146
1147 pPlan->qwEstimatedSize += pPackage->qwInstallSize;
1148 }
1149 }
1150
1151 // Add execute actions.
1152 switch (pPackage->type)
1153 {
1154 case BURN_PACKAGE_TYPE_EXE:
1155 hr = ExeEnginePlanAddPackage(NULL, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire);
1156 break;
1157
1158 case BURN_PACKAGE_TYPE_MSI:
1159 hr = MsiEnginePlanAddPackage(display, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire);
1160 break;
1161
1162 case BURN_PACKAGE_TYPE_MSP:
1163 hr = MspEnginePlanAddPackage(display, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire);
1164 break;
1165
1166 case BURN_PACKAGE_TYPE_MSU:
1167 hr = MsuEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire);
1168 break;
1169
1170 default:
1171 hr = E_UNEXPECTED;
1172 ExitOnFailure(hr, "Invalid package type.");
1173 }
1174 ExitOnFailure(hr, "Failed to add plan actions for package: %ls", pPackage->sczId);
1175
1176 // Plan certain dependency actions after planning the package execute action.
1177 hr = DependencyPlanPackageComplete(pPackage, pPlan);
1178 ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId);
1179
1180 // If we are going to take any action on this package, add progress for it.
1181 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
1182 {
1183 LoggingIncrementPackageSequence();
1184
1185 ++pPlan->cExecutePackagesTotal;
1186 ++pPlan->cOverallProgressTicksTotal;
1187
1188 // If package is per-machine and is being executed, flag the plan to be per-machine as well.
1189 if (pPackage->fPerMachine)
1190 {
1191 pPlan->fPerMachine = TRUE;
1192 }
1193 }
1194
1195LExit:
1196 return hr;
1197}
1198
1199extern "C" HRESULT PlanRelatedBundlesBegin(
1200 __in BURN_USER_EXPERIENCE* pUserExperience,
1201 __in BURN_REGISTRATION* pRegistration,
1202 __in BOOTSTRAPPER_RELATION_TYPE relationType,
1203 __in BURN_PLAN* pPlan
1204 )
1205{
1206 HRESULT hr = S_OK;
1207 LPWSTR* rgsczAncestors = NULL;
1208 UINT cAncestors = 0;
1209 STRINGDICT_HANDLE sdAncestors = NULL;
1210
1211 if (pRegistration->sczAncestors)
1212 {
1213 hr = StrSplitAllocArray(&rgsczAncestors, &cAncestors, pRegistration->sczAncestors, L";");
1214 ExitOnFailure(hr, "Failed to create string array from ancestors.");
1215
1216 hr = DictCreateStringListFromArray(&sdAncestors, rgsczAncestors, cAncestors, DICT_FLAG_CASEINSENSITIVE);
1217 ExitOnFailure(hr, "Failed to create dictionary from ancestors array.");
1218 }
1219
1220 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
1221 {
1222 BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
1223 pRelatedBundle->package.defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1224 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1225
1226 // Do not execute the same bundle twice.
1227 if (sdAncestors)
1228 {
1229 hr = DictKeyExists(sdAncestors, pRelatedBundle->package.sczId);
1230 if (SUCCEEDED(hr))
1231 {
1232 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType));
1233 continue;
1234 }
1235 else if (E_NOTFOUND != hr)
1236 {
1237 ExitOnFailure(hr, "Failed to lookup the bundle ID in the ancestors dictionary.");
1238 }
1239 }
1240 else if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_RELATION_NONE != relationType)
1241 {
1242 // Avoid repair loops for older bundles that do not handle ancestors.
1243 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRelationTypeToString(relationType));
1244 continue;
1245 }
1246
1247 // Pass along any ancestors and ourself to prevent infinite loops.
1248 if (pRegistration->sczAncestors && *pRegistration->sczAncestors)
1249 {
1250 hr = StrAllocFormatted(&pRelatedBundle->package.Exe.sczAncestors, L"%ls;%ls", pRegistration->sczAncestors, pRegistration->sczId);
1251 ExitOnFailure(hr, "Failed to copy ancestors and self to related bundle ancestors.");
1252 }
1253 else
1254 {
1255 hr = StrAllocString(&pRelatedBundle->package.Exe.sczAncestors, pRegistration->sczId, 0);
1256 ExitOnFailure(hr, "Failed to copy self to related bundle ancestors.");
1257 }
1258
1259 switch (pRelatedBundle->relationType)
1260 {
1261 case BOOTSTRAPPER_RELATION_UPGRADE:
1262 if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < pPlan->action)
1263 {
1264 pRelatedBundle->package.requested = (pRegistration->qwVersion > pRelatedBundle->qwVersion) ? BOOTSTRAPPER_REQUEST_STATE_ABSENT : BOOTSTRAPPER_REQUEST_STATE_NONE;
1265 }
1266 break;
1267 case BOOTSTRAPPER_RELATION_PATCH: __fallthrough;
1268 case BOOTSTRAPPER_RELATION_ADDON:
1269 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1270 {
1271 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_ABSENT;
1272 }
1273 else if (BOOTSTRAPPER_ACTION_INSTALL == pPlan->action || BOOTSTRAPPER_ACTION_MODIFY == pPlan->action)
1274 {
1275 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1276 }
1277 else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action)
1278 {
1279 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_REPAIR;
1280 }
1281 break;
1282 case BOOTSTRAPPER_RELATION_DEPENDENT:
1283 // Automatically repair dependent bundles to restore missing
1284 // packages after uninstall unless we're being upgraded with the
1285 // assumption that upgrades are cumulative (as intended).
1286 if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1287 {
1288 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_REPAIR;
1289 }
1290 break;
1291 case BOOTSTRAPPER_RELATION_DETECT:
1292 break;
1293 default:
1294 hr = E_UNEXPECTED;
1295 ExitOnFailure(hr, "Unexpected relation type encountered during plan: %d", pRelatedBundle->relationType);
1296 break;
1297 }
1298
1299 pRelatedBundle->package.defaultRequested = pRelatedBundle->package.requested;
1300
1301 hr = UserExperienceOnPlanRelatedBundle(pUserExperience, pRelatedBundle->package.sczId, &pRelatedBundle->package.requested);
1302 ExitOnRootFailure(hr, "BA aborted plan related bundle.");
1303
1304 // Log when the BA changed the bundle state so the engine doesn't get blamed for planning the wrong thing.
1305 if (pRelatedBundle->package.requested != pRelatedBundle->package.defaultRequested)
1306 {
1307 LogId(REPORT_STANDARD, MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST, pRelatedBundle->package.sczId, LoggingRequestStateToString(pRelatedBundle->package.requested), LoggingRequestStateToString(pRelatedBundle->package.defaultRequested));
1308 }
1309
1310 // If uninstalling and the dependent related bundle may be executed, ignore its provider key to allow for downgrades with ref-counting.
1311 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested)
1312 {
1313 if (0 < pRelatedBundle->package.cDependencyProviders)
1314 {
1315 // Bundles only support a single provider key.
1316 const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders;
1317
1318 hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, pProvider->sczDisplayName);
1319 ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey);
1320 }
1321 }
1322 }
1323
1324LExit:
1325 ReleaseDict(sdAncestors);
1326 ReleaseStrArray(rgsczAncestors, cAncestors);
1327
1328 return hr;
1329}
1330
1331extern "C" HRESULT PlanRelatedBundlesComplete(
1332 __in BURN_REGISTRATION* pRegistration,
1333 __in BURN_PLAN* pPlan,
1334 __in BURN_LOGGING* pLog,
1335 __in BURN_VARIABLES* pVariables,
1336 __inout HANDLE* phSyncpointEvent,
1337 __in DWORD dwExecuteActionEarlyIndex
1338 )
1339{
1340 HRESULT hr = S_OK;
1341 LPWSTR sczIgnoreDependencies = NULL;
1342 STRINGDICT_HANDLE sdProviderKeys = NULL;
1343
1344 // Get the list of dependencies to ignore to pass to related bundles.
1345 hr = DependencyAllocIgnoreDependencies(pPlan, &sczIgnoreDependencies);
1346 ExitOnFailure(hr, "Failed to get the list of dependencies to ignore.");
1347
1348 hr = DictCreateStringList(&sdProviderKeys, pPlan->cExecuteActions, DICT_FLAG_CASEINSENSITIVE);
1349 ExitOnFailure(hr, "Failed to create dictionary for planned packages.");
1350
1351 BOOL fExecutingAnyPackage = FALSE;
1352
1353 for (DWORD i = 0; i < pPlan->cExecuteActions; ++i)
1354 {
1355 if (BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE == pPlan->rgExecuteActions[i].type && BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].exePackage.action)
1356 {
1357 fExecutingAnyPackage = TRUE;
1358
1359 BURN_PACKAGE* pPackage = pPlan->rgExecuteActions[i].packageProvider.pPackage;
1360 if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol)
1361 {
1362 if (0 < pPackage->cDependencyProviders)
1363 {
1364 // Bundles only support a single provider key.
1365 const BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders;
1366 DictAddKey(sdProviderKeys, pProvider->sczKey);
1367 }
1368 }
1369 }
1370 else
1371 {
1372 switch (pPlan->rgExecuteActions[i].type)
1373 {
1374 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
1375 fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msiPackage.action);
1376 break;
1377
1378 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
1379 fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].mspTarget.action);
1380 break;
1381
1382 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
1383 fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msuPackage.action);
1384 break;
1385 }
1386 }
1387 }
1388
1389 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
1390 {
1391 DWORD *pdwInsertIndex = NULL;
1392 BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
1393
1394 // Do not execute if a major upgrade to the related bundle is an embedded bundle (Provider keys are the same)
1395 if (0 < pRelatedBundle->package.cDependencyProviders)
1396 {
1397 // Bundles only support a single provider key.
1398 const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders;
1399 hr = DictKeyExists(sdProviderKeys, pProvider->sczKey);
1400 if (E_NOTFOUND != hr)
1401 {
1402 ExitOnFailure(hr, "Failed to check the dictionary for a related bundle provider key: \"%ls\".", pProvider->sczKey);
1403 // Key found, so there is an embedded bundle with the same provider key that will be executed. So this related bundle should not be added to the plan
1404 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), pProvider->sczKey);
1405 continue;
1406 }
1407 else
1408 {
1409 hr = S_OK;
1410 }
1411 }
1412
1413 // For an uninstall, there is no need to repair dependent bundles if no packages are executing.
1414 if (!fExecutingAnyPackage && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pRelatedBundle->package.requested && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1415 {
1416 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1417 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType));
1418 }
1419
1420 if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1421 {
1422 // Addon and patch bundles will be passed a list of dependencies to ignore for planning.
1423 hr = StrAllocString(&pRelatedBundle->package.Exe.sczIgnoreDependencies, sczIgnoreDependencies, 0);
1424 ExitOnFailure(hr, "Failed to copy the list of dependencies to ignore.");
1425
1426 // Uninstall addons and patches early in the chain, before other packages are uninstalled.
1427 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1428 {
1429 pdwInsertIndex = &dwExecuteActionEarlyIndex;
1430 }
1431 }
1432
1433 if (BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested)
1434 {
1435 hr = ExeEnginePlanCalculatePackage(&pRelatedBundle->package, NULL);
1436 ExitOnFailure(hr, "Failed to calcuate plan for related bundle: %ls", pRelatedBundle->package.sczId);
1437
1438 // Calculate package states based on reference count for addon and patch related bundles.
1439 if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1440 {
1441 hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan);
1442 ExitOnFailure(hr, "Failed to begin plan dependency actions to package: %ls", pRelatedBundle->package.sczId);
1443
1444 // If uninstalling a related bundle, make sure the bundle is uninstalled after removing registration.
1445 if (pdwInsertIndex && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1446 {
1447 ++(*pdwInsertIndex);
1448 }
1449 }
1450
1451 hr = ExeEnginePlanAddPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan, pLog, pVariables, *phSyncpointEvent, FALSE);
1452 ExitOnFailure(hr, "Failed to add to plan related bundle: %ls", pRelatedBundle->package.sczId);
1453
1454 // Calculate package states based on reference count for addon and patch related bundles.
1455 if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1456 {
1457 hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan);
1458 ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId);
1459 }
1460
1461 // If we are going to take any action on this package, add progress for it.
1462 if (BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.execute || BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.rollback)
1463 {
1464 LoggingIncrementPackageSequence();
1465
1466 ++pPlan->cExecutePackagesTotal;
1467 ++pPlan->cOverallProgressTicksTotal;
1468 }
1469
1470 // If package is per-machine and is being executed, flag the plan to be per-machine as well.
1471 if (pRelatedBundle->package.fPerMachine)
1472 {
1473 pPlan->fPerMachine = TRUE;
1474 }
1475 }
1476 else if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1477 {
1478 // Make sure the package is properly ref-counted even if no plan is requested.
1479 hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan);
1480 ExitOnFailure(hr, "Failed to begin plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId);
1481
1482 hr = DependencyPlanPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan);
1483 ExitOnFailure(hr, "Failed to plan related bundle package provider actions.");
1484
1485 hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan);
1486 ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId);
1487 }
1488 }
1489
1490LExit:
1491 ReleaseDict(sdProviderKeys);
1492 ReleaseStr(sczIgnoreDependencies);
1493
1494 return hr;
1495}
1496
1497extern "C" HRESULT PlanFinalizeActions(
1498 __in BURN_PLAN* pPlan
1499 )
1500{
1501 HRESULT hr = S_OK;
1502
1503 hr = RemoveUnnecessaryActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions);
1504 ExitOnFailure(hr, "Failed to remove unnecessary execute actions.");
1505
1506 hr = RemoveUnnecessaryActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions);
1507 ExitOnFailure(hr, "Failed to remove unnecessary execute actions.");
1508
1509 hr = FinalizeSlipstreamPatchActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions);
1510 ExitOnFailure(hr, "Failed to finalize slipstream execute actions.");
1511
1512 hr = FinalizeSlipstreamPatchActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions);
1513 ExitOnFailure(hr, "Failed to finalize slipstream rollback actions.");
1514
1515LExit:
1516 return hr;
1517}
1518
1519extern "C" HRESULT PlanCleanPackage(
1520 __in BURN_PLAN* pPlan,
1521 __in BURN_PACKAGE* pPackage
1522 )
1523{
1524 HRESULT hr = S_OK;
1525 BOOL fPlanCleanPackage = FALSE;
1526 BURN_CLEAN_ACTION* pCleanAction = NULL;
1527
1528 // The following is a complex set of logic that determines when a package should be cleaned
1529 // from the cache. Start by noting that we only clean if the package is being acquired or
1530 // already cached and the package is not supposed to always be cached.
1531 if ((pPackage->fAcquire || BURN_CACHE_STATE_PARTIAL == pPackage->cache || BURN_CACHE_STATE_COMPLETE == pPackage->cache) &&
1532 (BURN_CACHE_TYPE_ALWAYS > pPackage->cacheType || BOOTSTRAPPER_ACTION_INSTALL > pPlan->action))
1533 {
1534 // The following are all different reasons why the package should be cleaned from the cache.
1535 // The else-ifs are used to make the conditions easier to see (rather than have them combined
1536 // in one huge condition).
1537 if (BURN_CACHE_TYPE_YES > pPackage->cacheType) // easy, package is not supposed to stay cached.
1538 {
1539 fPlanCleanPackage = TRUE;
1540 }
1541 else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested ||
1542 BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed and
1543 BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) // actually being removed.
1544 {
1545 fPlanCleanPackage = TRUE;
1546 }
1547 else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested ||
1548 BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed but
1549 BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is do nothing and
1550 !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and
1551 BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed.
1552 {
1553 fPlanCleanPackage = TRUE;
1554 }
1555 else if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && // uninstalling and
1556 BOOTSTRAPPER_REQUEST_STATE_NONE == pPackage->requested && // requested do nothing (aka: default) and
1557 BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is still do nothing and
1558 !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and
1559 BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed.
1560 {
1561 fPlanCleanPackage = TRUE;
1562 }
1563 }
1564
1565 if (fPlanCleanPackage)
1566 {
1567 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgCleanActions), pPlan->cCleanActions + 1, sizeof(BURN_CLEAN_ACTION), 5);
1568 ExitOnFailure(hr, "Failed to grow plan's array of clean actions.");
1569
1570 pCleanAction = pPlan->rgCleanActions + pPlan->cCleanActions;
1571 ++pPlan->cCleanActions;
1572
1573 pCleanAction->pPackage = pPackage;
1574
1575 pPackage->fUncache = TRUE;
1576 }
1577
1578LExit:
1579 return hr;
1580}
1581
1582extern "C" HRESULT PlanExecuteCacheSyncAndRollback(
1583 __in BURN_PLAN* pPlan,
1584 __in BURN_PACKAGE* pPackage,
1585 __in HANDLE hCacheEvent,
1586 __in BOOL fPlanPackageCacheRollback
1587 )
1588{
1589 HRESULT hr = S_OK;
1590 BURN_EXECUTE_ACTION* pAction = NULL;
1591
1592 hr = PlanAppendExecuteAction(pPlan, &pAction);
1593 ExitOnFailure(hr, "Failed to append wait action for caching.");
1594
1595 pAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT;
1596 pAction->syncpoint.hEvent = hCacheEvent;
1597
1598 if (fPlanPackageCacheRollback)
1599 {
1600 hr = PlanAppendRollbackAction(pPlan, &pAction);
1601 ExitOnFailure(hr, "Failed to append rollback action.");
1602
1603 pAction->type = BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE;
1604 pAction->uncachePackage.pPackage = pPackage;
1605
1606 hr = PlanExecuteCheckpoint(pPlan);
1607 ExitOnFailure(hr, "Failed to append execute checkpoint for cache rollback.");
1608 }
1609
1610LExit:
1611 return hr;
1612}
1613
1614extern "C" HRESULT PlanExecuteCheckpoint(
1615 __in BURN_PLAN* pPlan
1616 )
1617{
1618 HRESULT hr = S_OK;
1619 BURN_EXECUTE_ACTION* pAction = NULL;
1620 DWORD dwCheckpointId = GetNextCheckpointId();
1621
1622 // execute checkpoint
1623 hr = PlanAppendExecuteAction(pPlan, &pAction);
1624 ExitOnFailure(hr, "Failed to append execute action.");
1625
1626 pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT;
1627 pAction->checkpoint.dwId = dwCheckpointId;
1628
1629 // rollback checkpoint
1630 hr = PlanAppendRollbackAction(pPlan, &pAction);
1631 ExitOnFailure(hr, "Failed to append rollback action.");
1632
1633 pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT;
1634 pAction->checkpoint.dwId = dwCheckpointId;
1635
1636LExit:
1637 return hr;
1638}
1639
1640extern "C" HRESULT PlanInsertExecuteAction(
1641 __in DWORD dwIndex,
1642 __in BURN_PLAN* pPlan,
1643 __out BURN_EXECUTE_ACTION** ppExecuteAction
1644 )
1645{
1646 HRESULT hr = S_OK;
1647
1648 hr = MemInsertIntoArray((void**)&pPlan->rgExecuteActions, dwIndex, 1, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1649 ExitOnFailure(hr, "Failed to grow plan's array of execute actions.");
1650
1651 *ppExecuteAction = pPlan->rgExecuteActions + dwIndex;
1652 ++pPlan->cExecuteActions;
1653
1654LExit:
1655 return hr;
1656}
1657
1658extern "C" HRESULT PlanInsertRollbackAction(
1659 __in DWORD dwIndex,
1660 __in BURN_PLAN* pPlan,
1661 __out BURN_EXECUTE_ACTION** ppRollbackAction
1662 )
1663{
1664 HRESULT hr = S_OK;
1665
1666 hr = MemInsertIntoArray((void**)&pPlan->rgRollbackActions, dwIndex, 1, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1667 ExitOnFailure(hr, "Failed to grow plan's array of rollback actions.");
1668
1669 *ppRollbackAction = pPlan->rgRollbackActions + dwIndex;
1670 ++pPlan->cRollbackActions;
1671
1672LExit:
1673 return hr;
1674}
1675
1676extern "C" HRESULT PlanAppendExecuteAction(
1677 __in BURN_PLAN* pPlan,
1678 __out BURN_EXECUTE_ACTION** ppExecuteAction
1679 )
1680{
1681 HRESULT hr = S_OK;
1682
1683 hr = MemEnsureArraySize((void**)&pPlan->rgExecuteActions, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1684 ExitOnFailure(hr, "Failed to grow plan's array of execute actions.");
1685
1686 *ppExecuteAction = pPlan->rgExecuteActions + pPlan->cExecuteActions;
1687 ++pPlan->cExecuteActions;
1688
1689LExit:
1690 return hr;
1691}
1692
1693extern "C" HRESULT PlanAppendRollbackAction(
1694 __in BURN_PLAN* pPlan,
1695 __out BURN_EXECUTE_ACTION** ppRollbackAction
1696 )
1697{
1698 HRESULT hr = S_OK;
1699
1700 hr = MemEnsureArraySize((void**)&pPlan->rgRollbackActions, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1701 ExitOnFailure(hr, "Failed to grow plan's array of rollback actions.");
1702
1703 *ppRollbackAction = pPlan->rgRollbackActions + pPlan->cRollbackActions;
1704 ++pPlan->cRollbackActions;
1705
1706LExit:
1707 return hr;
1708}
1709
1710extern "C" HRESULT PlanKeepRegistration(
1711 __in BURN_PLAN* pPlan,
1712 __in DWORD iAfterExecutePackageAction,
1713 __in DWORD iBeforeRollbackPackageAction
1714 )
1715{
1716 HRESULT hr = S_OK;
1717 BURN_EXECUTE_ACTION* pAction = NULL;
1718
1719 if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterExecutePackageAction)
1720 {
1721 hr = PlanInsertExecuteAction(iAfterExecutePackageAction, pPlan, &pAction);
1722 ExitOnFailure(hr, "Failed to insert keep registration execute action.");
1723
1724 pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION;
1725 pAction->registration.fKeep = TRUE;
1726 }
1727
1728 if (BURN_PLAN_INVALID_ACTION_INDEX != iBeforeRollbackPackageAction)
1729 {
1730 hr = PlanInsertRollbackAction(iBeforeRollbackPackageAction, pPlan, &pAction);
1731 ExitOnFailure(hr, "Failed to insert keep registration rollback action.");
1732
1733 pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION;
1734 pAction->registration.fKeep = FALSE;
1735 }
1736
1737LExit:
1738 return hr;
1739}
1740
1741extern "C" HRESULT PlanRemoveRegistration(
1742 __in BURN_PLAN* pPlan,
1743 __in DWORD iAfterExecutePackageAction,
1744 __in DWORD iAfterRollbackPackageAction
1745 )
1746{
1747 HRESULT hr = S_OK;
1748 BURN_EXECUTE_ACTION* pAction = NULL;
1749
1750 if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterExecutePackageAction)
1751 {
1752 hr = PlanInsertExecuteAction(iAfterExecutePackageAction, pPlan, &pAction);
1753 ExitOnFailure(hr, "Failed to insert remove registration execute action.");
1754
1755 pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION;
1756 pAction->registration.fKeep = FALSE;
1757 }
1758
1759 if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterRollbackPackageAction)
1760 {
1761 hr = PlanInsertRollbackAction(iAfterRollbackPackageAction, pPlan, &pAction);
1762 ExitOnFailure(hr, "Failed to insert remove registration rollback action.");
1763
1764 pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION;
1765 pAction->registration.fKeep = TRUE;
1766 }
1767
1768LExit:
1769 return hr;
1770}
1771
1772extern "C" HRESULT PlanRollbackBoundaryBegin(
1773 __in BURN_PLAN* pPlan,
1774 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
1775 )
1776{
1777 HRESULT hr = S_OK;
1778 BURN_EXECUTE_ACTION* pExecuteAction = NULL;
1779
1780 // Add begin rollback boundary to execute plan.
1781 hr = PlanAppendExecuteAction(pPlan, &pExecuteAction);
1782 ExitOnFailure(hr, "Failed to append rollback boundary begin action.");
1783
1784 pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY;
1785 pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary;
1786
1787 // Add begin rollback boundary to rollback plan.
1788 hr = PlanAppendRollbackAction(pPlan, &pExecuteAction);
1789 ExitOnFailure(hr, "Failed to append rollback boundary begin action.");
1790
1791 pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY;
1792 pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary;
1793
1794LExit:
1795 return hr;
1796}
1797
1798extern "C" HRESULT PlanRollbackBoundaryComplete(
1799 __in BURN_PLAN* pPlan
1800 )
1801{
1802 HRESULT hr = S_OK;
1803 BURN_EXECUTE_ACTION* pExecuteAction = NULL;
1804 DWORD dwCheckpointId = 0;
1805
1806 // Add checkpoints.
1807 dwCheckpointId = GetNextCheckpointId();
1808
1809 hr = PlanAppendExecuteAction(pPlan, &pExecuteAction);
1810 ExitOnFailure(hr, "Failed to append execute action.");
1811
1812 pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT;
1813 pExecuteAction->checkpoint.dwId = dwCheckpointId;
1814
1815 hr = PlanAppendRollbackAction(pPlan, &pExecuteAction);
1816 ExitOnFailure(hr, "Failed to append rollback action.");
1817
1818 pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT;
1819 pExecuteAction->checkpoint.dwId = dwCheckpointId;
1820
1821LExit:
1822 return hr;
1823}
1824
1825/*******************************************************************
1826 PlanSetResumeCommand - Initializes resume command string
1827
1828*******************************************************************/
1829extern "C" HRESULT PlanSetResumeCommand(
1830 __in BURN_REGISTRATION* pRegistration,
1831 __in BOOTSTRAPPER_ACTION action,
1832 __in BOOTSTRAPPER_COMMAND* pCommand,
1833 __in BURN_LOGGING* pLog
1834 )
1835{
1836 HRESULT hr = S_OK;
1837
1838 // build the resume command-line.
1839 hr = CoreRecreateCommandLine(&pRegistration->sczResumeCommandLine, action, pCommand->display, pCommand->restart, pCommand->relationType, pCommand->fPassthrough, pRegistration->sczActiveParent, pRegistration->sczAncestors, pLog->sczPath, pCommand->wzCommandLine);
1840 ExitOnFailure(hr, "Failed to recreate resume command-line.");
1841
1842LExit:
1843 return hr;
1844}
1845
1846
1847// internal function definitions
1848
1849static void UninitializeRegistrationAction(
1850 __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction
1851 )
1852{
1853 ReleaseStr(pAction->sczDependentProviderKey);
1854 ReleaseStr(pAction->sczBundleId);
1855 memset(pAction, 0, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION));
1856}
1857
1858static void UninitializeCacheAction(
1859 __in BURN_CACHE_ACTION* pCacheAction
1860 )
1861{
1862 switch (pCacheAction->type)
1863 {
1864 case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT:
1865 ReleaseHandle(pCacheAction->syncpoint.hEvent);
1866 break;
1867
1868 case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE:
1869 ReleaseStr(pCacheAction->bundleLayout.sczExecutableName);
1870 ReleaseStr(pCacheAction->bundleLayout.sczLayoutDirectory);
1871 ReleaseStr(pCacheAction->bundleLayout.sczUnverifiedPath);
1872 break;
1873
1874 case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER:
1875 ReleaseStr(pCacheAction->resolveContainer.sczUnverifiedPath);
1876 break;
1877
1878 case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER:
1879 ReleaseStr(pCacheAction->extractContainer.sczContainerUnverifiedPath);
1880 ReleaseMem(pCacheAction->extractContainer.rgPayloads);
1881 break;
1882
1883 case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD:
1884 ReleaseStr(pCacheAction->resolvePayload.sczUnverifiedPath);
1885 break;
1886
1887 case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD:
1888 ReleaseStr(pCacheAction->cachePayload.sczUnverifiedPath);
1889 break;
1890 }
1891}
1892
1893static void ResetPlannedPackageState(
1894 __in BURN_PACKAGE* pPackage
1895 )
1896{
1897 // Reset package state that is a result of planning.
1898 pPackage->expected = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
1899 pPackage->defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1900 pPackage->requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1901 pPackage->fAcquire = FALSE;
1902 pPackage->fUncache = FALSE;
1903 pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
1904 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1905 pPackage->providerExecute = BURN_DEPENDENCY_ACTION_NONE;
1906 pPackage->providerRollback = BURN_DEPENDENCY_ACTION_NONE;
1907 pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE;
1908 pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE;
1909 pPackage->fDependencyManagerWasHere = FALSE;
1910
1911 if (BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.rgFeatures)
1912 {
1913 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
1914 {
1915 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
1916
1917 pFeature->execute = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1918 pFeature->rollback = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1919 }
1920 }
1921 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.rgTargetProducts)
1922 {
1923 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
1924 {
1925 BURN_MSPTARGETPRODUCT* pTargetProduct = &pPackage->Msp.rgTargetProducts[i];
1926
1927 pTargetProduct->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
1928 pTargetProduct->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1929 }
1930 }
1931}
1932
1933static HRESULT GetActionDefaultRequestState(
1934 __in BOOTSTRAPPER_ACTION action,
1935 __in BOOL fPermanent,
1936 __in BOOTSTRAPPER_PACKAGE_STATE currentState,
1937 __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
1938 )
1939{
1940 HRESULT hr = S_OK;
1941
1942 switch (action)
1943 {
1944 case BOOTSTRAPPER_ACTION_CACHE:
1945 switch (currentState)
1946 {
1947 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
1948 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1949 break;
1950
1951 case BOOTSTRAPPER_PACKAGE_STATE_CACHED:
1952 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
1953 break;
1954
1955 default:
1956 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE;
1957 break;
1958 }
1959 break;
1960
1961 case BOOTSTRAPPER_ACTION_INSTALL: __fallthrough;
1962 case BOOTSTRAPPER_ACTION_UPDATE_REPLACE: __fallthrough;
1963 case BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED:
1964 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1965 break;
1966
1967 case BOOTSTRAPPER_ACTION_REPAIR:
1968 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR;
1969 break;
1970
1971 case BOOTSTRAPPER_ACTION_UNINSTALL:
1972 *pRequestState = fPermanent ? BOOTSTRAPPER_REQUEST_STATE_NONE : BOOTSTRAPPER_REQUEST_STATE_ABSENT;
1973 break;
1974
1975 case BOOTSTRAPPER_ACTION_MODIFY:
1976 switch (currentState)
1977 {
1978 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
1979 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT;
1980 break;
1981
1982 case BOOTSTRAPPER_PACKAGE_STATE_CACHED:
1983 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE;
1984 break;
1985
1986 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
1987 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1988 break;
1989
1990 default:
1991 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
1992 break;
1993 }
1994 break;
1995
1996 default:
1997 hr = E_INVALIDARG;
1998 ExitOnRootFailure(hr, "Invalid action state.");
1999 }
2000
2001LExit:
2002 return hr;
2003}
2004
2005static HRESULT AddRegistrationAction(
2006 __in BURN_PLAN* pPlan,
2007 __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type,
2008 __in_z LPCWSTR wzDependentProviderKey,
2009 __in_z LPCWSTR wzOwnerBundleId
2010 )
2011{
2012 HRESULT hr = S_OK;
2013 BURN_DEPENDENT_REGISTRATION_ACTION_TYPE rollbackType = (BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER == type) ? BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER : BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER;
2014 BURN_DEPENDENT_REGISTRATION_ACTION* pAction = NULL;
2015
2016 // Create forward registration action.
2017 hr = MemEnsureArraySize((void**)&pPlan->rgRegistrationActions, pPlan->cRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5);
2018 ExitOnFailure(hr, "Failed to grow plan's array of registration actions.");
2019
2020 pAction = pPlan->rgRegistrationActions + pPlan->cRegistrationActions;
2021 ++pPlan->cRegistrationActions;
2022
2023 pAction->type = type;
2024
2025 hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0);
2026 ExitOnFailure(hr, "Failed to copy owner bundle to registration action.");
2027
2028 hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0);
2029 ExitOnFailure(hr, "Failed to copy dependent provider key to registration action.");
2030
2031 // Create rollback registration action.
2032 hr = MemEnsureArraySize((void**)&pPlan->rgRollbackRegistrationActions, pPlan->cRollbackRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5);
2033 ExitOnFailure(hr, "Failed to grow plan's array of rollback registration actions.");
2034
2035 pAction = pPlan->rgRollbackRegistrationActions + pPlan->cRollbackRegistrationActions;
2036 ++pPlan->cRollbackRegistrationActions;
2037
2038 pAction->type = rollbackType;
2039
2040 hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0);
2041 ExitOnFailure(hr, "Failed to copy owner bundle to registration action.");
2042
2043 hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0);
2044 ExitOnFailure(hr, "Failed to copy dependent provider key to rollback registration action.");
2045
2046LExit:
2047 return hr;
2048}
2049
2050static HRESULT AddCachePackage(
2051 __in BURN_PLAN* pPlan,
2052 __in BURN_PACKAGE* pPackage,
2053 __out HANDLE* phSyncpointEvent
2054 )
2055{
2056 HRESULT hr = S_OK;
2057
2058 // If this is an MSI package with slipstream MSPs, ensure the MSPs are cached first.
2059 if (BURN_PACKAGE_TYPE_MSI == pPackage->type && 0 < pPackage->Msi.cSlipstreamMspPackages)
2060 {
2061 hr = AddCacheSlipstreamMsps(pPlan, pPackage);
2062 ExitOnFailure(hr, "Failed to plan slipstream patches for package.");
2063 }
2064
2065 hr = AddCachePackageHelper(pPlan, pPackage, phSyncpointEvent);
2066 ExitOnFailure(hr, "Failed to plan cache package.");
2067
2068LExit:
2069 return hr;
2070}
2071
2072static HRESULT AddCachePackageHelper(
2073 __in BURN_PLAN* pPlan,
2074 __in BURN_PACKAGE* pPackage,
2075 __out HANDLE* phSyncpointEvent
2076 )
2077{
2078 AssertSz(pPackage->sczCacheId && *pPackage->sczCacheId, "AddCachePackageHelper() expects the package to have a cache id.");
2079
2080 HRESULT hr = S_OK;
2081 BURN_CACHE_ACTION* pCacheAction = NULL;
2082 DWORD dwCheckpoint = 0;
2083 DWORD iPackageStartAction = 0;
2084
2085 BOOL fPlanned = AlreadyPlannedCachePackage(pPlan, pPackage->sczId, phSyncpointEvent);
2086 if (fPlanned)
2087 {
2088 ExitFunction();
2089 }
2090
2091 // Cache checkpoints happen before the package is cached because downloading packages'
2092 // payloads will not roll themselves back the way installation packages rollback on
2093 // failure automatically.
2094 dwCheckpoint = GetNextCheckpointId();
2095
2096 hr = AppendCacheAction(pPlan, &pCacheAction);
2097 ExitOnFailure(hr, "Failed to append package start action.");
2098
2099 pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT;
2100 pCacheAction->checkpoint.dwId = dwCheckpoint;
2101
2102 // Only plan the cache rollback if the package is also going to be uninstalled;
2103 // otherwise, future operations like repair will not be able to locate the cached package.
2104 BOOL fPlanCacheRollback = (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->rollback);
2105
2106 if (fPlanCacheRollback)
2107 {
2108 hr = AppendRollbackCacheAction(pPlan, &pCacheAction);
2109 ExitOnFailure(hr, "Failed to append rollback cache action.");
2110
2111 pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT;
2112 pCacheAction->checkpoint.dwId = dwCheckpoint;
2113 }
2114
2115 // Plan the package start.
2116 hr = AppendCacheAction(pPlan, &pCacheAction);
2117 ExitOnFailure(hr, "Failed to append package start action.");
2118
2119 pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_START;
2120 pCacheAction->packageStart.pPackage = pPackage;
2121
2122 // Remember the index for the package start action (which is now the last in the cache
2123 // actions array) because we have to update this action after processing all the payloads
2124 // and the array may be resized later which would move a pointer around in memory.
2125 iPackageStartAction = pPlan->cCacheActions - 1;
2126
2127 if (fPlanCacheRollback)
2128 {
2129 // Create a package cache rollback action.
2130 hr = AppendRollbackCacheAction(pPlan, &pCacheAction);
2131 ExitOnFailure(hr, "Failed to append rollback cache action.");
2132
2133 pCacheAction->type = BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE;
2134 pCacheAction->rollbackPackage.pPackage = pPackage;
2135 }
2136
2137 // Add all the payload cache operations to the plan for this package.
2138 for (DWORD i = 0; i < pPackage->cPayloads; ++i)
2139 {
2140 BURN_PACKAGE_PAYLOAD* pPackagePayload = &pPackage->rgPayloads[i];
2141
2142 hr = AppendCacheOrLayoutPayloadAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload, pPackagePayload->fCached, NULL);
2143 ExitOnFailure(hr, "Failed to append payload cache action.");
2144
2145 Assert(BURN_CACHE_ACTION_TYPE_PACKAGE_START == pPlan->rgCacheActions[iPackageStartAction].type);
2146 ++pPlan->rgCacheActions[iPackageStartAction].packageStart.cCachePayloads;
2147 pPlan->rgCacheActions[iPackageStartAction].packageStart.qwCachePayloadSizeTotal += pPackagePayload->pPayload->qwFileSize;
2148 }
2149
2150 // Create package stop action.
2151 hr = AppendCacheAction(pPlan, &pCacheAction);
2152 ExitOnFailure(hr, "Failed to append cache action.");
2153
2154 pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_STOP;
2155 pCacheAction->packageStop.pPackage = pPackage;
2156
2157 // Update the start action with the location of the complete action.
2158 pPlan->rgCacheActions[iPackageStartAction].packageStart.iPackageCompleteAction = pPlan->cCacheActions - 1;
2159
2160 // Create syncpoint action.
2161 hr = AppendCacheAction(pPlan, &pCacheAction);
2162 ExitOnFailure(hr, "Failed to append cache action.");
2163
2164 pCacheAction->type = BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT;
2165 pCacheAction->syncpoint.hEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
2166 ExitOnNullWithLastError(pCacheAction->syncpoint.hEvent, hr, "Failed to create syncpoint event.");
2167
2168 *phSyncpointEvent = pCacheAction->syncpoint.hEvent;
2169
2170 ++pPlan->cOverallProgressTicksTotal;
2171
2172 // If the package was not already fully cached then note that we planned the cache here. Otherwise, we only
2173 // did cache operations to verify the cache is valid so we did not plan the acquisition of the package.
2174 pPackage->fAcquire = (BURN_CACHE_STATE_COMPLETE != pPackage->cache);
2175
2176LExit:
2177 return hr;
2178}
2179
2180static HRESULT AddCacheSlipstreamMsps(
2181 __in BURN_PLAN* pPlan,
2182 __in BURN_PACKAGE* pPackage
2183 )
2184{
2185 HRESULT hr = S_OK;
2186 HANDLE hIgnored = NULL;
2187
2188 AssertSz(BURN_PACKAGE_TYPE_MSI == pPackage->type, "Only MSI packages can have slipstream patches.");
2189
2190 for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
2191 {
2192 BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i];
2193 AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches.");
2194
2195 hr = AddCachePackageHelper(pPlan, pMspPackage, &hIgnored);
2196 ExitOnFailure(hr, "Failed to plan slipstream MSP: %ls", pMspPackage->sczId);
2197 }
2198
2199LExit:
2200 return hr;
2201}
2202
2203static BOOL AlreadyPlannedCachePackage(
2204 __in BURN_PLAN* pPlan,
2205 __in_z LPCWSTR wzPackageId,
2206 __out HANDLE* phSyncpointEvent
2207 )
2208{
2209 BOOL fPlanned = FALSE;
2210
2211 for (DWORD iCacheAction = 0; iCacheAction < pPlan->cCacheActions; ++iCacheAction)
2212 {
2213 BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + iCacheAction;
2214
2215 if (BURN_CACHE_ACTION_TYPE_PACKAGE_STOP == pCacheAction->type)
2216 {
2217 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pCacheAction->packageStop.pPackage->sczId, -1, wzPackageId, -1))
2218 {
2219 if (iCacheAction + 1 < pPlan->cCacheActions && BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT == pPlan->rgCacheActions[iCacheAction + 1].type)
2220 {
2221 *phSyncpointEvent = pPlan->rgCacheActions[iCacheAction + 1].syncpoint.hEvent;
2222 }
2223
2224 fPlanned = TRUE;
2225 break;
2226 }
2227 }
2228 }
2229
2230 return fPlanned;
2231}
2232
2233static DWORD GetNextCheckpointId()
2234{
2235 static DWORD dwCounter = 0;
2236 return ++dwCounter;
2237}
2238
2239static HRESULT AppendCacheAction(
2240 __in BURN_PLAN* pPlan,
2241 __out BURN_CACHE_ACTION** ppCacheAction
2242 )
2243{
2244 HRESULT hr = S_OK;
2245
2246 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgCacheActions), pPlan->cCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5);
2247 ExitOnFailure(hr, "Failed to grow plan's array of cache actions.");
2248
2249 *ppCacheAction = pPlan->rgCacheActions + pPlan->cCacheActions;
2250 ++pPlan->cCacheActions;
2251
2252LExit:
2253 return hr;
2254}
2255
2256static HRESULT AppendRollbackCacheAction(
2257 __in BURN_PLAN* pPlan,
2258 __out BURN_CACHE_ACTION** ppCacheAction
2259 )
2260{
2261 HRESULT hr = S_OK;
2262
2263 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgRollbackCacheActions), pPlan->cRollbackCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5);
2264 ExitOnFailure(hr, "Failed to grow plan's array of rollback cache actions.");
2265
2266 *ppCacheAction = pPlan->rgRollbackCacheActions + pPlan->cRollbackCacheActions;
2267 ++pPlan->cRollbackCacheActions;
2268
2269LExit:
2270 return hr;
2271}
2272
2273static HRESULT AppendLayoutContainerAction(
2274 __in BURN_PLAN* pPlan,
2275 __in_opt BURN_PACKAGE* pPackage,
2276 __in DWORD iPackageStartAction,
2277 __in BURN_CONTAINER* pContainer,
2278 __in BOOL fContainerCached,
2279 __in_z LPCWSTR wzLayoutDirectory
2280 )
2281{
2282 HRESULT hr = S_OK;
2283 BURN_CACHE_ACTION* pAcquireAction = NULL;
2284 DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX;
2285 LPWSTR sczContainerWorkingPath = NULL;
2286 BURN_CACHE_ACTION* pCacheAction = NULL;
2287 BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL;
2288
2289 // No need to do anything if the container is already cached or is attached to the bundle (since the
2290 // bundle itself will already have a layout action).
2291 if (fContainerCached || pContainer->fAttached)
2292 {
2293 ExitFunction();
2294 }
2295
2296 // Ensure the container is being acquired. If it is, then some earlier package already planned the layout of this container so
2297 // don't do it again. Otherwise, plan away!
2298 if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pContainer, 0, iPackageStartAction, NULL, NULL))
2299 {
2300 hr = AddAcquireContainer(pPlan, pContainer, &pAcquireAction, &iAcquireAction);
2301 ExitOnFailure(hr, "Failed to append acquire container action for layout to plan.");
2302
2303 Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pAcquireAction->type);
2304
2305 // Create the layout container action.
2306 hr = StrAllocString(&sczContainerWorkingPath, pAcquireAction->resolveContainer.sczUnverifiedPath, 0);
2307 ExitOnFailure(hr, "Failed to copy container working path for layout.");
2308
2309 hr = AppendCacheAction(pPlan, &pCacheAction);
2310 ExitOnFailure(hr, "Failed to append cache action to cache payload.");
2311
2312 hr = CreateContainerProgress(pPlan, pContainer, &pContainerProgress);
2313 ExitOnFailure(hr, "Failed to create container progress.");
2314
2315 hr = StrAllocString(&pCacheAction->layoutContainer.sczLayoutDirectory, wzLayoutDirectory, 0);
2316 ExitOnFailure(hr, "Failed to copy layout directory into plan.");
2317
2318 pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER;
2319 pCacheAction->layoutContainer.pPackage = pPackage;
2320 pCacheAction->layoutContainer.pContainer = pContainer;
2321 pCacheAction->layoutContainer.iProgress = pContainerProgress->iIndex;
2322 pCacheAction->layoutContainer.fMove = TRUE;
2323 pCacheAction->layoutContainer.iTryAgainAction = iAcquireAction;
2324 pCacheAction->layoutContainer.sczUnverifiedPath = sczContainerWorkingPath;
2325 sczContainerWorkingPath = NULL;
2326 }
2327
2328LExit:
2329 ReleaseNullStr(sczContainerWorkingPath);
2330
2331 return hr;
2332}
2333
2334static HRESULT AppendCacheOrLayoutPayloadAction(
2335 __in BURN_PLAN* pPlan,
2336 __in_opt BURN_PACKAGE* pPackage,
2337 __in DWORD iPackageStartAction,
2338 __in BURN_PAYLOAD* pPayload,
2339 __in BOOL fPayloadCached,
2340 __in_z_opt LPCWSTR wzLayoutDirectory
2341 )
2342{
2343 HRESULT hr = S_OK;
2344 LPWSTR sczPayloadWorkingPath = NULL;
2345 BURN_CACHE_ACTION* pCacheAction = NULL;
2346 DWORD iTryAgainAction = BURN_PLAN_INVALID_ACTION_INDEX;
2347 BURN_CACHE_PAYLOAD_PROGRESS* pPayloadProgress = NULL;
2348
2349 hr = CacheCalculatePayloadWorkingPath(pPlan->wzBundleId, pPayload, &sczPayloadWorkingPath);
2350 ExitOnFailure(hr, "Failed to calculate unverified path for payload.");
2351
2352 // If the payload is in a container, ensure the container is being acquired
2353 // then add this payload to the list of payloads to extract already in the plan.
2354 if (pPayload->pContainer)
2355 {
2356 BURN_CACHE_ACTION* pPreviousPackageExtractAction = NULL;
2357 BURN_CACHE_ACTION* pThisPackageExtractAction = NULL;
2358
2359 // If the payload is not already cached, then add it to the first extract container action in the plan. Extracting
2360 // all the needed payloads from the container in a single pass is the most efficient way to extract files from
2361 // containers. If there is not an extract container action before our package, that is okay because we'll create
2362 // an extract container action for our package in a second anyway.
2363 if (!fPayloadCached)
2364 {
2365 if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pPayload->pContainer, 0, iPackageStartAction, &pPreviousPackageExtractAction, NULL))
2366 {
2367 hr = AddExtractPayload(pPreviousPackageExtractAction, pPackage, pPayload, sczPayloadWorkingPath);
2368 ExitOnFailure(hr, "Failed to add extract payload action to previous package.");
2369 }
2370 }
2371
2372 // If there is already an extract container action after our package start action then try to find an acquire action
2373 // that is matched with it. If there is an acquire action then that is our "try again" action, otherwise we'll use the existing
2374 // extract action as the "try again" action.
2375 if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pPayload->pContainer, iPackageStartAction, BURN_PLAN_INVALID_ACTION_INDEX, &pThisPackageExtractAction, &iTryAgainAction))
2376 {
2377 DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX;
2378 if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pPayload->pContainer, iPackageStartAction, iTryAgainAction, NULL, &iAcquireAction))
2379 {
2380 iTryAgainAction = iAcquireAction;
2381 }
2382 }
2383 else // did not find an extract container action for our package.
2384 {
2385 // Ensure there is an extract action (and maybe an acquire action) for every package that has payloads. The
2386 // acquire and extract action will be skipped if the payload is already cached or was added to a previous
2387 // package's extract action above.
2388 //
2389 // These actions always exist (even when they are likely to be skipped) so that "try again" will not
2390 // jump so far back in the plan that you end up extracting payloads for other packages. With these actions
2391 // "try again" will only retry the extraction for payloads in this package.
2392 hr = CreateContainerAcquireAndExtractAction(pPlan, pPayload->pContainer, iPackageStartAction, pPreviousPackageExtractAction ? TRUE : fPayloadCached, &pThisPackageExtractAction, &iTryAgainAction);
2393 ExitOnFailure(hr, "Failed to create container extract action.");
2394 }
2395 ExitOnFailure(hr, "Failed while searching for package's container extract action.");
2396
2397 // We *always* add the payload to this package's extract action even though the extract action
2398 // is probably being skipped until retry if there was a previous package extract action.
2399 hr = AddExtractPayload(pThisPackageExtractAction, pPackage, pPayload, sczPayloadWorkingPath);
2400 ExitOnFailure(hr, "Failed to add extract payload to current package.");
2401 }
2402 else // add a payload acquire action to the plan.
2403 {
2404 // Try to find an existing acquire action for this payload. If one is not found,
2405 // we'll create it. At the same time we will change any cache/layout payload actions
2406 // that would "MOVE" the file to "COPY" so that our new cache/layout action below
2407 // can do the move.
2408 pCacheAction = ProcessSharedPayload(pPlan, pPayload);
2409 if (!pCacheAction)
2410 {
2411 hr = AppendCacheAction(pPlan, &pCacheAction);
2412 ExitOnFailure(hr, "Failed to append cache action to acquire payload.");
2413
2414 pCacheAction->type = BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD;
2415 pCacheAction->fSkipUntilRetried = fPayloadCached;
2416 pCacheAction->resolvePayload.pPackage = pPackage;
2417 pCacheAction->resolvePayload.pPayload = pPayload;
2418 hr = StrAllocString(&pCacheAction->resolvePayload.sczUnverifiedPath, sczPayloadWorkingPath, 0);
2419 ExitOnFailure(hr, "Failed to copy unverified path for payload to acquire.");
2420 }
2421
2422 iTryAgainAction = static_cast<DWORD>(pCacheAction - pPlan->rgCacheActions);
2423 pCacheAction = NULL;
2424 }
2425
2426 Assert(BURN_PLAN_INVALID_ACTION_INDEX != iTryAgainAction);
2427 Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pPlan->rgCacheActions[iTryAgainAction].type ||
2428 BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pPlan->rgCacheActions[iTryAgainAction].type ||
2429 BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD == pPlan->rgCacheActions[iTryAgainAction].type);
2430
2431 hr = AppendCacheAction(pPlan, &pCacheAction);
2432 ExitOnFailure(hr, "Failed to append cache action to cache payload.");
2433
2434 hr = CreatePayloadProgress(pPlan, pPayload, &pPayloadProgress);
2435 ExitOnFailure(hr, "Failed to create payload progress.");
2436
2437 if (!wzLayoutDirectory)
2438 {
2439 pCacheAction->type = BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD;
2440 pCacheAction->cachePayload.pPackage = pPackage;
2441 pCacheAction->cachePayload.pPayload = pPayload;
2442 pCacheAction->cachePayload.iProgress = pPayloadProgress->iIndex;
2443 pCacheAction->cachePayload.fMove = TRUE;
2444 pCacheAction->cachePayload.iTryAgainAction = iTryAgainAction;
2445 pCacheAction->cachePayload.sczUnverifiedPath = sczPayloadWorkingPath;
2446 sczPayloadWorkingPath = NULL;
2447 }
2448 else
2449 {
2450 hr = StrAllocString(&pCacheAction->layoutPayload.sczLayoutDirectory, wzLayoutDirectory, 0);
2451 ExitOnFailure(hr, "Failed to copy layout directory into plan.");
2452
2453 pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD;
2454 pCacheAction->layoutPayload.pPackage = pPackage;
2455 pCacheAction->layoutPayload.pPayload = pPayload;
2456 pCacheAction->layoutPayload.iProgress = pPayloadProgress->iIndex;
2457 pCacheAction->layoutPayload.fMove = TRUE;
2458 pCacheAction->layoutPayload.iTryAgainAction = iTryAgainAction;
2459 pCacheAction->layoutPayload.sczUnverifiedPath = sczPayloadWorkingPath;
2460 sczPayloadWorkingPath = NULL;
2461 }
2462
2463 pCacheAction = NULL;
2464
2465LExit:
2466 ReleaseStr(sczPayloadWorkingPath);
2467
2468 return hr;
2469}
2470
2471static BOOL FindContainerCacheAction(
2472 __in BURN_CACHE_ACTION_TYPE type,
2473 __in BURN_PLAN* pPlan,
2474 __in BURN_CONTAINER* pContainer,
2475 __in DWORD iSearchStart,
2476 __in DWORD iSearchEnd,
2477 __out_opt BURN_CACHE_ACTION** ppCacheAction,
2478 __out_opt DWORD* piCacheAction
2479 )
2480{
2481 BOOL fFound = FALSE; // assume we won't find what we are looking for.
2482
2483 Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == type || BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == type);
2484
2485 iSearchStart = (BURN_PLAN_INVALID_ACTION_INDEX == iSearchStart) ? 0 : iSearchStart;
2486 iSearchEnd = (BURN_PLAN_INVALID_ACTION_INDEX == iSearchEnd) ? pPlan->cCacheActions : iSearchEnd;
2487
2488 for (DWORD iSearch = iSearchStart; iSearch < iSearchEnd; ++iSearch)
2489 {
2490 BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + iSearch;
2491 if (pCacheAction->type == type &&
2492 ((BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pCacheAction->type && pCacheAction->resolveContainer.pContainer == pContainer) ||
2493 (BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pCacheAction->type && pCacheAction->extractContainer.pContainer == pContainer)))
2494 {
2495 if (ppCacheAction)
2496 {
2497 *ppCacheAction = pCacheAction;
2498 }
2499
2500 if (piCacheAction)
2501 {
2502 *piCacheAction = iSearch;
2503 }
2504
2505 fFound = TRUE;
2506 break;
2507 }
2508 }
2509
2510 return fFound;
2511}
2512
2513static HRESULT CreateContainerAcquireAndExtractAction(
2514 __in BURN_PLAN* pPlan,
2515 __in BURN_CONTAINER* pContainer,
2516 __in DWORD iPackageStartAction,
2517 __in BOOL fPayloadCached,
2518 __out BURN_CACHE_ACTION** ppContainerExtractAction,
2519 __out DWORD* piContainerTryAgainAction
2520 )
2521{
2522 HRESULT hr = S_OK;
2523 DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX;
2524 BURN_CACHE_ACTION* pContainerExtractAction = NULL;
2525 DWORD iExtractAction = BURN_PLAN_INVALID_ACTION_INDEX;
2526 DWORD iTryAgainAction = BURN_PLAN_INVALID_ACTION_INDEX;
2527 LPWSTR sczContainerWorkingPath = NULL;
2528
2529 // If the container is actually attached to the executable then we will not need an acquire
2530 // container action.
2531 if (!pContainer->fActuallyAttached)
2532 {
2533 BURN_CACHE_ACTION* pAcquireContainerAction = NULL;
2534
2535 // If there is no plan to acquire the container then add acquire action since we
2536 // can't extract stuff out of a container until we acquire the container.
2537 if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pContainer, iPackageStartAction, BURN_PLAN_INVALID_ACTION_INDEX, &pAcquireContainerAction, &iAcquireAction))
2538 {
2539 hr = AddAcquireContainer(pPlan, pContainer, &pAcquireContainerAction, &iAcquireAction);
2540 ExitOnFailure(hr, "Failed to append acquire container action to plan.");
2541
2542 pAcquireContainerAction->fSkipUntilRetried = TRUE; // we'll start by assuming the acquire is not necessary and the fPayloadCached below will set us straight if wrong.
2543 }
2544
2545 Assert(BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction);
2546 Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pAcquireContainerAction->type);
2547 Assert(pContainer == pAcquireContainerAction->resolveContainer.pContainer);
2548 }
2549
2550 Assert((pContainer->fActuallyAttached && BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction) ||
2551 (!pContainer->fActuallyAttached && BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction));
2552
2553 // If we do not find an action for extracting payloads from this container, create it now.
2554 if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pContainer, (BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction) ? iPackageStartAction : iAcquireAction, BURN_PLAN_INVALID_ACTION_INDEX, &pContainerExtractAction, &iExtractAction))
2555 {
2556 // Attached containers that are actually attached use the executable path for their working path.
2557 if (pContainer->fActuallyAttached)
2558 {
2559 Assert(BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction);
2560
2561 hr = PathForCurrentProcess(&sczContainerWorkingPath, NULL);
2562 ExitOnFailure(hr, "Failed to get path for executing module as attached container working path.");
2563 }
2564 else // use the acquired working path as the location of the container.
2565 {
2566 Assert(BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction);
2567
2568 hr = StrAllocString(&sczContainerWorkingPath, pPlan->rgCacheActions[iAcquireAction].resolveContainer.sczUnverifiedPath, 0);
2569 ExitOnFailure(hr, "Failed to copy container unverified path for cache action to extract container.");
2570 }
2571
2572 hr = AppendCacheAction(pPlan, &pContainerExtractAction);
2573 ExitOnFailure(hr, "Failed to append cache action to extract payloads from container.");
2574
2575 iExtractAction = pPlan->cCacheActions - 1;
2576
2577 pContainerExtractAction->type = BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER;
2578 pContainerExtractAction->fSkipUntilRetried = pContainer->fActuallyAttached; // assume we can skip the extract engine when the container is already attached and the fPayloadCached below will set us straight if wrong.
2579 pContainerExtractAction->extractContainer.pContainer = pContainer;
2580 pContainerExtractAction->extractContainer.iSkipUntilAcquiredByAction = iAcquireAction;
2581 pContainerExtractAction->extractContainer.sczContainerUnverifiedPath = sczContainerWorkingPath;
2582 sczContainerWorkingPath = NULL;
2583 }
2584
2585 Assert(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pContainerExtractAction->type);
2586 Assert(BURN_PLAN_INVALID_ACTION_INDEX != iExtractAction);
2587
2588 // If there is an acquire action, that is our try again action. Otherwise, we'll use the extract action.
2589 iTryAgainAction = (BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction) ? iAcquireAction : iExtractAction;
2590
2591 // If the try again action thinks it can be skipped but the payload is not cached,
2592 // ensure the action will not be skipped.
2593 BURN_CACHE_ACTION* pTryAgainAction = pPlan->rgCacheActions + iTryAgainAction;
2594 Assert((BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pTryAgainAction->type && pContainer == pTryAgainAction->resolveContainer.pContainer) ||
2595 (BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pTryAgainAction->type && pContainer == pTryAgainAction->extractContainer.pContainer));
2596 if (pTryAgainAction->fSkipUntilRetried && !fPayloadCached)
2597 {
2598 pTryAgainAction->fSkipUntilRetried = FALSE;
2599 }
2600
2601 *ppContainerExtractAction = pContainerExtractAction;
2602 *piContainerTryAgainAction = iTryAgainAction;
2603
2604LExit:
2605 ReleaseStr(sczContainerWorkingPath);
2606
2607 return hr;
2608}
2609
2610static HRESULT AddAcquireContainer(
2611 __in BURN_PLAN* pPlan,
2612 __in BURN_CONTAINER* pContainer,
2613 __out_opt BURN_CACHE_ACTION** ppCacheAction,
2614 __out_opt DWORD* piCacheAction
2615 )
2616{
2617 HRESULT hr = S_OK;
2618 LPWSTR sczContainerWorkingPath = NULL;
2619 BURN_CACHE_ACTION* pAcquireContainerAction = NULL;
2620 BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL;
2621
2622 hr = CacheCalculateContainerWorkingPath(pPlan->wzBundleId, pContainer, &sczContainerWorkingPath);
2623 ExitOnFailure(hr, "Failed to calculate unverified path for container.");
2624
2625 hr = AppendCacheAction(pPlan, &pAcquireContainerAction);
2626 ExitOnFailure(hr, "Failed to append acquire container action to plan.");
2627
2628 hr = CreateContainerProgress(pPlan, pContainer, &pContainerProgress);
2629 ExitOnFailure(hr, "Failed to create container progress.");
2630
2631 pAcquireContainerAction->type = BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER;
2632 pAcquireContainerAction->resolveContainer.pContainer = pContainer;
2633 pAcquireContainerAction->resolveContainer.iProgress = pContainerProgress->iIndex;
2634 pAcquireContainerAction->resolveContainer.sczUnverifiedPath = sczContainerWorkingPath;
2635 sczContainerWorkingPath = NULL;
2636
2637 if (ppCacheAction)
2638 {
2639 *ppCacheAction = pAcquireContainerAction;
2640 }
2641
2642 if (piCacheAction)
2643 {
2644 *piCacheAction = pPlan->cCacheActions - 1;
2645 }
2646
2647LExit:
2648 ReleaseStr(sczContainerWorkingPath);
2649
2650 return hr;
2651}
2652
2653static HRESULT AddExtractPayload(
2654 __in BURN_CACHE_ACTION* pCacheAction,
2655 __in_opt BURN_PACKAGE* pPackage,
2656 __in BURN_PAYLOAD* pPayload,
2657 __in_z LPCWSTR wzPayloadWorkingPath
2658 )
2659{
2660 HRESULT hr = S_OK;
2661
2662 Assert(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pCacheAction->type);
2663
2664 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pCacheAction->extractContainer.rgPayloads), pCacheAction->extractContainer.cPayloads + 1, sizeof(BURN_EXTRACT_PAYLOAD), 5);
2665 ExitOnFailure(hr, "Failed to grow list of payloads to extract from container.");
2666
2667 BURN_EXTRACT_PAYLOAD* pExtractPayload = pCacheAction->extractContainer.rgPayloads + pCacheAction->extractContainer.cPayloads;
2668 pExtractPayload->pPackage = pPackage;
2669 pExtractPayload->pPayload = pPayload;
2670 hr = StrAllocString(&pExtractPayload->sczUnverifiedPath, wzPayloadWorkingPath, 0);
2671 ExitOnFailure(hr, "Failed to copy unverified path for payload to extract.");
2672 ++pCacheAction->extractContainer.cPayloads;
2673
2674LExit:
2675 return hr;
2676}
2677
2678static BURN_CACHE_ACTION* ProcessSharedPayload(
2679 __in BURN_PLAN* pPlan,
2680 __in BURN_PAYLOAD* pPayload
2681 )
2682{
2683 BURN_CACHE_ACTION* pAcquireAction = NULL;
2684#ifdef DEBUG
2685 DWORD cMove = 0;
2686#endif
2687
2688 for (DWORD i = 0; i < pPlan->cCacheActions; ++i)
2689 {
2690 BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i;
2691
2692 if (BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD == pCacheAction->type &&
2693 pCacheAction->resolvePayload.pPayload == pPayload)
2694 {
2695 AssertSz(!pAcquireAction, "There should be at most one acquire cache action per payload.");
2696 pAcquireAction = pCacheAction;
2697 }
2698 else if (BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD == pCacheAction->type &&
2699 pCacheAction->cachePayload.pPayload == pPayload &&
2700 pCacheAction->cachePayload.fMove)
2701 {
2702 // Since we found a shared payload, change its operation from MOVE to COPY.
2703 pCacheAction->cachePayload.fMove = FALSE;
2704
2705 AssertSz(1 == ++cMove, "Shared payload should be moved once and only once.");
2706#ifndef DEBUG
2707 break;
2708#endif
2709 }
2710 else if (BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD == pCacheAction->type &&
2711 pCacheAction->layoutPayload.pPayload == pPayload &&
2712 pCacheAction->layoutPayload.fMove)
2713 {
2714 // Since we found a shared payload, change its operation from MOVE to COPY if necessary
2715 pCacheAction->layoutPayload.fMove = FALSE;
2716
2717 AssertSz(1 == ++cMove, "Shared payload should be moved once and only once.");
2718#ifndef DEBUG
2719 break;
2720#endif
2721 }
2722 }
2723
2724 return pAcquireAction;
2725}
2726
2727static HRESULT RemoveUnnecessaryActions(
2728 __in BOOL fExecute,
2729 __in BURN_EXECUTE_ACTION* rgActions,
2730 __in DWORD cActions
2731 )
2732{
2733 HRESULT hr = S_OK;
2734 LPCSTR szExecuteOrRollback = fExecute ? "execute" : "rollback";
2735
2736 for (DWORD i = 0; i < cActions; ++i)
2737 {
2738 BURN_EXECUTE_ACTION* pAction = rgActions + i;
2739
2740 // If this MSP targets a package in the chain, check the target's execute state
2741 // to see if this patch should be skipped.
2742 if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type && pAction->mspTarget.pChainedTargetPackage)
2743 {
2744 BOOTSTRAPPER_ACTION_STATE chainedTargetPackageAction = fExecute ? pAction->mspTarget.pChainedTargetPackage->execute : pAction->mspTarget.pChainedTargetPackage->rollback;
2745 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == chainedTargetPackageAction)
2746 {
2747 LogId(REPORT_STANDARD, MSG_PLAN_SKIP_PATCH_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback);
2748 pAction->fDeleted = TRUE;
2749 }
2750 else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < chainedTargetPackageAction && pAction->mspTarget.fSlipstream && BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pAction->mspTarget.action)
2751 {
2752 // If the slipstream target is being installed or upgraded (not uninstalled or repaired) then we will slipstream so skip
2753 // this action to install the patch standalone. Also, if the slipstream target is being repaired and the patch is being
2754 // repaired, skip this operation since it will be redundant.
2755 //
2756 // The primary goal here is to ensure that a slipstream patch that is yet not installed is installed even if the MSI
2757 // is already on the machine. The slipstream must be installed standalone if the MSI is being repaired.
2758 if (BOOTSTRAPPER_ACTION_STATE_REPAIR != chainedTargetPackageAction || BOOTSTRAPPER_ACTION_STATE_REPAIR == pAction->mspTarget.action)
2759 {
2760 LogId(REPORT_STANDARD, MSG_PLAN_SKIP_SLIPSTREAM_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback);
2761 pAction->fDeleted = TRUE;
2762 }
2763 }
2764 }
2765 }
2766
2767 return hr;
2768}
2769
2770static HRESULT FinalizeSlipstreamPatchActions(
2771 __in BOOL fExecute,
2772 __in BURN_EXECUTE_ACTION* rgActions,
2773 __in DWORD cActions
2774 )
2775{
2776 HRESULT hr = S_OK;
2777
2778 for (DWORD i = 0; i < cActions; ++i)
2779 {
2780 BURN_EXECUTE_ACTION* pAction = rgActions + i;
2781
2782 // If this MSI package contains slipstream patches store the slipstream actions.
2783 if (BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE == pAction->type && pAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages)
2784 {
2785 BURN_PACKAGE* pPackage = pAction->msiPackage.pPackage;
2786
2787 // By default all slipstream actions will be initialized to "no action" (aka: 0).
2788 pAction->msiPackage.rgSlipstreamPatches = (BOOTSTRAPPER_ACTION_STATE*)MemAlloc(sizeof(BOOTSTRAPPER_ACTION_STATE) * pPackage->Msi.cSlipstreamMspPackages, TRUE);
2789 ExitOnNull(pAction->msiPackage.rgSlipstreamPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch actions.");
2790
2791 // If we are uninstalling or repairing the MSI, we must ignore all the slipstream patches because they cannot
2792 // be applied right now.
2793 if (BOOTSTRAPPER_ACTION_STATE_REPAIR != pAction->msiPackage.action && BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pAction->msiPackage.action)
2794 {
2795 for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j)
2796 {
2797 BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[j];
2798 AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches.");
2799
2800 pAction->msiPackage.rgSlipstreamPatches[j] = fExecute ? pMspPackage->execute : pMspPackage->rollback;
2801 for (DWORD k = 0; k < pMspPackage->Msp.cTargetProductCodes; ++k)
2802 {
2803 BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + k;
2804 if (pPackage == pTargetProduct->pChainedTargetPackage)
2805 {
2806 pAction->msiPackage.rgSlipstreamPatches[j] = fExecute ? pTargetProduct->execute : pTargetProduct->rollback;
2807 break;
2808 }
2809 }
2810 }
2811 }
2812 }
2813 }
2814
2815LExit:
2816 return hr;
2817}
2818
2819static HRESULT PlanDependencyActions(
2820 __in BOOL fBundlePerMachine,
2821 __in BURN_PLAN* pPlan,
2822 __in BURN_PACKAGE* pPackage
2823 )
2824{
2825 HRESULT hr = S_OK;
2826
2827 hr = DependencyPlanPackageBegin(fBundlePerMachine, pPackage, pPlan);
2828 ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId);
2829
2830 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
2831 ExitOnFailure(hr, "Failed to plan package dependency actions.");
2832
2833 hr = DependencyPlanPackageComplete(pPackage, pPlan);
2834 ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId);
2835
2836LExit:
2837 return hr;
2838}
2839
2840static HRESULT CalculateExecuteActions(
2841 __in BURN_USER_EXPERIENCE* pUserExperience,
2842 __in BURN_PACKAGE* pPackage,
2843 __in BURN_VARIABLES* pVariables,
2844 __out_opt BOOL* pfBARequestedCache
2845 )
2846{
2847 HRESULT hr = S_OK;
2848
2849 // Calculate execute actions.
2850 switch (pPackage->type)
2851 {
2852 case BURN_PACKAGE_TYPE_EXE:
2853 hr = ExeEnginePlanCalculatePackage(pPackage, pfBARequestedCache);
2854 break;
2855
2856 case BURN_PACKAGE_TYPE_MSI:
2857 hr = MsiEnginePlanCalculatePackage(pPackage, pVariables, pUserExperience, pfBARequestedCache);
2858 break;
2859
2860 case BURN_PACKAGE_TYPE_MSP:
2861 hr = MspEnginePlanCalculatePackage(pPackage, pUserExperience, pfBARequestedCache);
2862 break;
2863
2864 case BURN_PACKAGE_TYPE_MSU:
2865 hr = MsuEnginePlanCalculatePackage(pPackage, pfBARequestedCache);
2866 break;
2867
2868 default:
2869 hr = E_UNEXPECTED;
2870 ExitOnFailure(hr, "Invalid package type.");
2871 }
2872
2873LExit:
2874 return hr;
2875}
2876
2877static BOOL NeedsCache(
2878 __in BURN_PLAN* pPlan,
2879 __in BURN_PACKAGE* pPackage
2880 )
2881{
2882 // All packages that have cacheType set to always should be cached if the bundle is going to be present.
2883 if (BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType && BOOTSTRAPPER_ACTION_INSTALL <= pPlan->action)
2884 {
2885 return TRUE;
2886 }
2887 else if (BURN_PACKAGE_TYPE_EXE == pPackage->type) // Exe packages require the package for all operations (even uninstall).
2888 {
2889 return BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute;
2890 }
2891 else // The other package types can uninstall without the original package.
2892 {
2893 return BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->execute;
2894 }
2895}
2896
2897static HRESULT CreateContainerProgress(
2898 __in BURN_PLAN* pPlan,
2899 __in BURN_CONTAINER* pContainer,
2900 __out BURN_CACHE_CONTAINER_PROGRESS** ppContainerProgress
2901 )
2902{
2903 HRESULT hr = S_OK;
2904 BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL;
2905
2906 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgContainerProgress), pPlan->cContainerProgress + 1, sizeof(BURN_CACHE_CONTAINER_PROGRESS), 5);
2907 ExitOnFailure(hr, "Failed to grow container progress list.");
2908
2909 if (!pPlan->shContainerProgress)
2910 {
2911 hr = DictCreateWithEmbeddedKey(&pPlan->shContainerProgress, 5, reinterpret_cast<void **>(&pPlan->rgContainerProgress), offsetof(BURN_CACHE_CONTAINER_PROGRESS, wzId), DICT_FLAG_NONE);
2912 ExitOnFailure(hr, "Failed to create container progress dictionary.");
2913 }
2914
2915 hr = DictGetValue(pPlan->shContainerProgress, pContainer->sczId, reinterpret_cast<void **>(&pContainerProgress));
2916 if (E_NOTFOUND == hr)
2917 {
2918 pContainerProgress = &pPlan->rgContainerProgress[pPlan->cContainerProgress];
2919 pContainerProgress->iIndex = pPlan->cContainerProgress;
2920 pContainerProgress->pContainer = pContainer;
2921 pContainerProgress->wzId = pContainer->sczId;
2922
2923 hr = DictAddValue(pPlan->shContainerProgress, pContainerProgress);
2924 ExitOnFailure(hr, "Failed to add \"%ls\" to the container progress dictionary.", pContainerProgress->wzId);
2925
2926 ++pPlan->cContainerProgress;
2927 pPlan->qwCacheSizeTotal += pContainer->qwFileSize;
2928 }
2929 ExitOnFailure(hr, "Failed to retrieve \"%ls\" from the container progress dictionary.", pContainer->sczId);
2930
2931 *ppContainerProgress = pContainerProgress;
2932
2933LExit:
2934 return hr;
2935}
2936
2937static HRESULT CreatePayloadProgress(
2938 __in BURN_PLAN* pPlan,
2939 __in BURN_PAYLOAD* pPayload,
2940 __out BURN_CACHE_PAYLOAD_PROGRESS** ppPayloadProgress
2941 )
2942{
2943 HRESULT hr = S_OK;
2944 BURN_CACHE_PAYLOAD_PROGRESS* pPayloadProgress = NULL;
2945
2946 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgPayloadProgress), pPlan->cPayloadProgress + 1, sizeof(BURN_CACHE_PAYLOAD_PROGRESS), 5);
2947 ExitOnFailure(hr, "Failed to grow payload progress list.");
2948
2949 if (!pPlan->shPayloadProgress)
2950 {
2951 hr = DictCreateWithEmbeddedKey(&pPlan->shPayloadProgress, 5, reinterpret_cast<void **>(&pPlan->rgPayloadProgress), offsetof(BURN_CACHE_PAYLOAD_PROGRESS, wzId), DICT_FLAG_NONE);
2952 ExitOnFailure(hr, "Failed to create payload progress dictionary.");
2953 }
2954
2955 hr = DictGetValue(pPlan->shPayloadProgress, pPayload->sczKey, reinterpret_cast<void **>(&pPayloadProgress));
2956 if (E_NOTFOUND == hr)
2957 {
2958 pPayloadProgress = &pPlan->rgPayloadProgress[pPlan->cPayloadProgress];
2959 pPayloadProgress->iIndex = pPlan->cPayloadProgress;
2960 pPayloadProgress->pPayload = pPayload;
2961 pPayloadProgress->wzId = pPayload->sczKey;
2962
2963 hr = DictAddValue(pPlan->shPayloadProgress, pPayloadProgress);
2964 ExitOnFailure(hr, "Failed to add \"%ls\" to the payload progress dictionary.", pPayloadProgress->wzId);
2965
2966 ++pPlan->cPayloadProgress;
2967 pPlan->qwCacheSizeTotal += pPayload->qwFileSize;
2968 }
2969 ExitOnFailure(hr, "Failed to retrieve \"%ls\" from the payload progress dictionary.", pPayload->sczKey);
2970
2971 *ppPayloadProgress = pPayloadProgress;
2972
2973LExit:
2974 return hr;
2975}
2976
2977
2978#ifdef DEBUG
2979
2980static void CacheActionLog(
2981 __in DWORD iAction,
2982 __in BURN_CACHE_ACTION* pAction,
2983 __in BOOL fRollback
2984 )
2985{
2986 LPCWSTR wzBase = fRollback ? L" Rollback cache" : L" Cache";
2987 switch (pAction->type)
2988 {
2989 case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER:
2990 LogStringLine(REPORT_STANDARD, "%ls action[%u]: ACQUIRE_CONTAINER id: %ls, source path: %ls, working path: %ls, skip until retried: %hs", wzBase, iAction, pAction->resolveContainer.pContainer->sczId, pAction->resolveContainer.pContainer->sczSourcePath, pAction->resolveContainer.sczUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried));
2991 break;
2992
2993 case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD:
2994 LogStringLine(REPORT_STANDARD, "%ls action[%u]: ACQUIRE_PAYLOAD package id: %ls, payload id: %ls, source path: %ls, working path: %ls, skip until retried: %hs", wzBase, iAction, pAction->resolvePayload.pPackage ? pAction->resolvePayload.pPackage->sczId : L"", pAction->resolvePayload.pPayload->sczKey, pAction->resolvePayload.pPayload->sczSourcePath, pAction->resolvePayload.sczUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried));
2995 break;
2996
2997 case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD:
2998 LogStringLine(REPORT_STANDARD, "%ls action[%u]: CACHE_PAYLOAD package id: %ls, payload id: %ls, working path: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->cachePayload.pPackage->sczId, pAction->cachePayload.pPayload->sczKey, pAction->cachePayload.sczUnverifiedPath, pAction->cachePayload.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->cachePayload.iTryAgainAction);
2999 break;
3000
3001 case BURN_CACHE_ACTION_TYPE_CHECKPOINT:
3002 LogStringLine(REPORT_STANDARD, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId);
3003 break;
3004
3005 case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER:
3006 LogStringLine(REPORT_STANDARD, "%ls action[%u]: EXTRACT_CONTAINER id: %ls, working path: %ls, skip until retried: %hs, skip until acquired by action: %u", wzBase, iAction, pAction->extractContainer.pContainer->sczId, pAction->extractContainer.sczContainerUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried), pAction->extractContainer.iSkipUntilAcquiredByAction);
3007 for (DWORD j = 0; j < pAction->extractContainer.cPayloads; j++)
3008 {
3009 LogStringLine(REPORT_STANDARD, " extract package id: %ls, payload id: %ls, working path: %ls", pAction->extractContainer.rgPayloads[j].pPackage->sczId, pAction->extractContainer.rgPayloads[j].pPayload->sczKey, pAction->extractContainer.rgPayloads[j].sczUnverifiedPath);
3010 }
3011 break;
3012
3013 case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE:
3014 LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_BUNDLE working path: %ls, layout directory: %ls, exe name: %ls, skip until retried: %hs", wzBase, iAction, pAction->bundleLayout.sczUnverifiedPath, pAction->bundleLayout.sczLayoutDirectory, pAction->bundleLayout.sczExecutableName, LoggingBoolToString(pAction->fSkipUntilRetried));
3015 break;
3016
3017 case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER:
3018 LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_CONTAINER package id: %ls, container id: %ls, working path: %ls, layout directory: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->layoutContainer.pPackage ? pAction->layoutContainer.pPackage->sczId : L"", pAction->layoutContainer.pContainer->sczId, pAction->layoutContainer.sczUnverifiedPath, pAction->layoutContainer.sczLayoutDirectory, pAction->layoutContainer.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->layoutContainer.iTryAgainAction);
3019 break;
3020
3021 case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD:
3022 LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_PAYLOAD package id: %ls, payload id: %ls, working path: %ls, layout directory: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->layoutPayload.pPackage ? pAction->layoutPayload.pPackage->sczId : L"", pAction->layoutPayload.pPayload->sczKey, pAction->layoutPayload.sczUnverifiedPath, pAction->layoutPayload.sczLayoutDirectory, pAction->layoutPayload.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->layoutPayload.iTryAgainAction);
3023 break;
3024
3025 case BURN_CACHE_ACTION_TYPE_PACKAGE_START:
3026 LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_START id: %ls, plan index for skip: %u, payloads to cache: %u, bytes to cache: %llu, skip until retried: %hs", wzBase, iAction, pAction->packageStart.pPackage->sczId, pAction->packageStart.iPackageCompleteAction, pAction->packageStart.cCachePayloads, pAction->packageStart.qwCachePayloadSizeTotal, LoggingBoolToString(pAction->fSkipUntilRetried));
3027 break;
3028
3029 case BURN_CACHE_ACTION_TYPE_PACKAGE_STOP:
3030 LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_STOP id: %ls, skip until retried: %hs", wzBase, iAction, pAction->packageStop.pPackage->sczId, LoggingBoolToString(pAction->fSkipUntilRetried));
3031 break;
3032
3033 case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE:
3034 LogStringLine(REPORT_STANDARD, "%ls action[%u]: ROLLBACK_PACKAGE id: %ls, skip until retried: %hs", wzBase, iAction, pAction->rollbackPackage.pPackage->sczId, LoggingBoolToString(pAction->fSkipUntilRetried));
3035 break;
3036
3037 case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT:
3038 LogStringLine(REPORT_STANDARD, "%ls action[%u]: SIGNAL_SYNCPOINT event handle: 0x%x, skip until retried: %hs", wzBase, iAction, pAction->syncpoint.hEvent, LoggingBoolToString(pAction->fSkipUntilRetried));
3039 break;
3040
3041 case BURN_CACHE_ACTION_TYPE_TRANSACTION_BOUNDARY:
3042 LogStringLine(REPORT_STANDARD, "%ls action[%u]: TRANSACTION_BOUNDARY id: %ls, event handle: 0x%x, vital: %ls, transaction: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.hEvent, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no", pAction->rollbackBoundary.pRollbackBoundary->fTransaction ? L"yes" : L"no");
3043 break;
3044
3045 default:
3046 AssertSz(FALSE, "Unknown cache action type.");
3047 break;
3048 }
3049}
3050
3051static void ExecuteActionLog(
3052 __in DWORD iAction,
3053 __in BURN_EXECUTE_ACTION* pAction,
3054 __in BOOL fRollback
3055 )
3056{
3057 LPCWSTR wzBase = fRollback ? L" Rollback" : L" Execute";
3058 switch (pAction->type)
3059 {
3060 case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT:
3061 LogStringLine(REPORT_STANDARD, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId);
3062 break;
3063
3064 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER:
3065 LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_PROVIDER package id: %ls, action: %u", wzBase, iAction, pAction->packageProvider.pPackage->sczId, pAction->packageProvider.action);
3066 break;
3067
3068 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
3069 LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_DEPENDENCY package id: %ls, bundle provider key: %ls, action: %u", wzBase, iAction, pAction->packageDependency.pPackage->sczId, pAction->packageDependency.sczBundleProviderKey, pAction->packageDependency.action);
3070 break;
3071
3072 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
3073 LogStringLine(REPORT_STANDARD, "%ls action[%u]: EXE_PACKAGE package id: %ls, action: %hs, ignore dependencies: %ls", wzBase, iAction, pAction->exePackage.pPackage->sczId, LoggingActionStateToString(pAction->exePackage.action), pAction->exePackage.sczIgnoreDependencies);
3074 break;
3075
3076 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
3077 LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, ui level: %u, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), pAction->msiPackage.uiLevel, pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes);
3078 for (DWORD j = 0; j < pAction->msiPackage.cPatches; ++j)
3079 {
3080 LogStringLine(REPORT_STANDARD, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->msiPackage.rgOrderedPatches->dwOrder, pAction->msiPackage.rgOrderedPatches[j].dwOrder, pAction->msiPackage.rgOrderedPatches[j].pPackage->sczId);
3081 }
3082 break;
3083
3084 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
3085 LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %ls, ui level: %u, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, pAction->mspTarget.fPerMachineTarget ? L"yes" : L"no", pAction->mspTarget.uiLevel, pAction->mspTarget.sczLogPath);
3086 for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j)
3087 {
3088 LogStringLine(REPORT_STANDARD, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->mspTarget.rgOrderedPatches[j].dwOrder, pAction->mspTarget.rgOrderedPatches[j].pPackage->sczId);
3089 }
3090 break;
3091
3092 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
3093 LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSU_PACKAGE package id: %ls, action: %hs, log path: %ls", wzBase, iAction, pAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pAction->msuPackage.action), pAction->msuPackage.sczLogPath);
3094 break;
3095
3096 case BURN_EXECUTE_ACTION_TYPE_REGISTRATION:
3097 LogStringLine(REPORT_STANDARD, "%ls action[%u]: REGISTRATION keep: %ls", wzBase, iAction, pAction->registration.fKeep ? L"yes" : L"no");
3098 break;
3099
3100 case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY:
3101 LogStringLine(REPORT_STANDARD, "%ls action[%u]: ROLLBACK_BOUNDARY id: %ls, vital: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no");
3102 break;
3103
3104 case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT:
3105 LogStringLine(REPORT_STANDARD, "%ls action[%u]: WAIT_SYNCPOINT event handle: 0x%x", wzBase, iAction, pAction->syncpoint.hEvent);
3106 break;
3107
3108 case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE:
3109 LogStringLine(REPORT_STANDARD, "%ls action[%u]: UNCACHE_PACKAGE id: %ls", wzBase, iAction, pAction->uncachePackage.pPackage->sczId);
3110 break;
3111
3112 case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE:
3113 LogStringLine(REPORT_STANDARD, "%ls action[%u]: COMPATIBLE_PACKAGE reference id: %ls, installed ProductCode: %ls", wzBase, iAction, pAction->compatiblePackage.pReferencePackage->sczId, pAction->compatiblePackage.sczInstalledProductCode);
3114 break;
3115
3116 default:
3117 AssertSz(FALSE, "Unknown execute action type.");
3118 break;
3119 }
3120}
3121
3122extern "C" void PlanDump(
3123 __in BURN_PLAN* pPlan
3124 )
3125{
3126 LogStringLine(REPORT_STANDARD, "--- Begin plan dump ---");
3127
3128 LogStringLine(REPORT_STANDARD, "Plan action: %hs", LoggingBurnActionToString(pPlan->action));
3129 LogStringLine(REPORT_STANDARD, " per-machine: %hs", LoggingTrueFalseToString(pPlan->fPerMachine));
3130 LogStringLine(REPORT_STANDARD, " keep registration by default: %hs", LoggingTrueFalseToString(pPlan->fKeepRegistrationDefault));
3131 LogStringLine(REPORT_STANDARD, " estimated size: %llu", pPlan->qwEstimatedSize);
3132
3133 LogStringLine(REPORT_STANDARD, "Plan cache size: %llu", pPlan->qwCacheSizeTotal);
3134 for (DWORD i = 0; i < pPlan->cCacheActions; ++i)
3135 {
3136 CacheActionLog(i, pPlan->rgCacheActions + i, FALSE);
3137 }
3138
3139 for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i)
3140 {
3141 CacheActionLog(i, pPlan->rgRollbackCacheActions + i, TRUE);
3142 }
3143
3144 LogStringLine(REPORT_STANDARD, "Plan execute package count: %u", pPlan->cExecutePackagesTotal);
3145 LogStringLine(REPORT_STANDARD, " overall progress ticks: %u", pPlan->cOverallProgressTicksTotal);
3146 for (DWORD i = 0; i < pPlan->cExecuteActions; ++i)
3147 {
3148 ExecuteActionLog(i, pPlan->rgExecuteActions + i, FALSE);
3149 }
3150
3151 for (DWORD i = 0; i < pPlan->cRollbackActions; ++i)
3152 {
3153 ExecuteActionLog(i, pPlan->rgRollbackActions + i, TRUE);
3154 }
3155
3156 for (DWORD i = 0; i < pPlan->cCleanActions; ++i)
3157 {
3158 LogStringLine(REPORT_STANDARD, " Clean action[%u]: CLEAN_PACKAGE package id: %ls", i, pPlan->rgCleanActions[i].pPackage->sczId);
3159 }
3160
3161 for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i)
3162 {
3163 LogStringLine(REPORT_STANDARD, " Dependency action[%u]: PLANNED_PROVIDER key: %ls, name: %ls", i, pPlan->rgPlannedProviders[i].sczKey, pPlan->rgPlannedProviders[i].sczName);
3164 }
3165
3166 LogStringLine(REPORT_STANDARD, "--- End plan dump ---");
3167}
3168
3169#endif