summaryrefslogtreecommitdiff
path: root/src/burn/engine/plan.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/burn/engine/plan.cpp')
-rw-r--r--src/burn/engine/plan.cpp2699
1 files changed, 2699 insertions, 0 deletions
diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp
new file mode 100644
index 00000000..9a4aa5f1
--- /dev/null
+++ b/src/burn/engine/plan.cpp
@@ -0,0 +1,2699 @@
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#define PlanDumpLevel REPORT_DEBUG
6
7// internal struct definitions
8
9
10// internal function definitions
11
12static void UninitializeRegistrationAction(
13 __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction
14 );
15static void UninitializeCacheAction(
16 __in BURN_CACHE_ACTION* pCacheAction
17 );
18static void ResetPlannedContainerState(
19 __in BURN_CONTAINER* pContainer
20 );
21static void ResetPlannedPayloadsState(
22 __in BURN_PAYLOADS* pPayloads
23 );
24static void ResetPlannedPayloadGroupState(
25 __in BURN_PAYLOAD_GROUP* pPayloadGroup
26 );
27static void ResetPlannedPackageState(
28 __in BURN_PACKAGE* pPackage
29 );
30static void ResetPlannedRollbackBoundaryState(
31 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
32 );
33static HRESULT PlanPackagesHelper(
34 __in BURN_PACKAGE* rgPackages,
35 __in DWORD cPackages,
36 __in BOOL fPlanCleanPackages,
37 __in BURN_USER_EXPERIENCE* pUX,
38 __in BURN_PLAN* pPlan,
39 __in BURN_LOGGING* pLog,
40 __in BURN_VARIABLES* pVariables,
41 __in BOOTSTRAPPER_DISPLAY display,
42 __in BOOTSTRAPPER_RELATION_TYPE relationType
43 );
44static HRESULT InitializePackage(
45 __in BURN_PLAN* pPlan,
46 __in BURN_USER_EXPERIENCE* pUX,
47 __in BURN_VARIABLES* pVariables,
48 __in BURN_PACKAGE* pPackage,
49 __in BOOTSTRAPPER_RELATION_TYPE relationType
50 );
51static HRESULT ProcessPackage(
52 __in BOOL fBundlePerMachine,
53 __in BURN_USER_EXPERIENCE* pUX,
54 __in BURN_PLAN* pPlan,
55 __in BURN_PACKAGE* pPackage,
56 __in BURN_LOGGING* pLog,
57 __in BURN_VARIABLES* pVariables,
58 __in BOOTSTRAPPER_DISPLAY display,
59 __inout HANDLE* phSyncpointEvent,
60 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
61 );
62static HRESULT ProcessPackageRollbackBoundary(
63 __in BURN_PLAN* pPlan,
64 __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary,
65 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
66 );
67static HRESULT GetActionDefaultRequestState(
68 __in BOOTSTRAPPER_ACTION action,
69 __in BOOL fPermanent,
70 __in BOOTSTRAPPER_PACKAGE_STATE currentState,
71 __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
72 );
73static HRESULT AddRegistrationAction(
74 __in BURN_PLAN* pPlan,
75 __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type,
76 __in_z LPCWSTR wzDependentProviderKey,
77 __in_z LPCWSTR wzOwnerBundleId
78 );
79static HRESULT AddCachePackage(
80 __in BURN_PLAN* pPlan,
81 __in BURN_PACKAGE* pPackage,
82 __out HANDLE* phSyncpointEvent
83 );
84static HRESULT AddCachePackageHelper(
85 __in BURN_PLAN* pPlan,
86 __in BURN_PACKAGE* pPackage,
87 __out HANDLE* phSyncpointEvent
88 );
89static HRESULT AddCacheSlipstreamMsps(
90 __in BURN_PLAN* pPlan,
91 __in BURN_PACKAGE* pPackage
92 );
93static BOOL AlreadyPlannedCachePackage(
94 __in BURN_PLAN* pPlan,
95 __in_z LPCWSTR wzPackageId,
96 __out HANDLE* phSyncpointEvent
97 );
98static DWORD GetNextCheckpointId(
99 __in BURN_PLAN* pPlan
100 );
101static HRESULT AppendCacheAction(
102 __in BURN_PLAN* pPlan,
103 __out BURN_CACHE_ACTION** ppCacheAction
104 );
105static HRESULT AppendRollbackCacheAction(
106 __in BURN_PLAN* pPlan,
107 __out BURN_CACHE_ACTION** ppCacheAction
108 );
109static HRESULT ProcessPayloadGroup(
110 __in BURN_PLAN* pPlan,
111 __in BURN_PAYLOAD_GROUP* pPayloadGroup
112 );
113static void RemoveUnnecessaryActions(
114 __in BOOL fExecute,
115 __in BURN_EXECUTE_ACTION* rgActions,
116 __in DWORD cActions
117 );
118static void FinalizePatchActions(
119 __in BOOL fExecute,
120 __in BURN_EXECUTE_ACTION* rgActions,
121 __in DWORD cActions
122 );
123static void CalculateExpectedRegistrationStates(
124 __in BURN_PACKAGE* rgPackages,
125 __in DWORD cPackages
126 );
127static HRESULT PlanDependencyActions(
128 __in BOOL fBundlePerMachine,
129 __in BURN_PLAN* pPlan,
130 __in BURN_PACKAGE* pPackage
131 );
132static HRESULT CalculateExecuteActions(
133 __in BURN_PACKAGE* pPackage,
134 __in_opt BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary
135 );
136static BOOL NeedsCache(
137 __in BURN_PACKAGE* pPackage,
138 __in BOOL fExecute
139 );
140static BOOL ForceCache(
141 __in BURN_PLAN* pPlan,
142 __in BURN_PACKAGE* pPackage
143 );
144
145// function definitions
146
147extern "C" void PlanReset(
148 __in BURN_PLAN* pPlan,
149 __in BURN_CONTAINERS* pContainers,
150 __in BURN_PACKAGES* pPackages,
151 __in BURN_PAYLOAD_GROUP* pLayoutPayloads
152 )
153{
154 ReleaseNullStr(pPlan->sczLayoutDirectory);
155 PackageUninitialize(&pPlan->forwardCompatibleBundle);
156
157 if (pPlan->rgRegistrationActions)
158 {
159 for (DWORD i = 0; i < pPlan->cRegistrationActions; ++i)
160 {
161 UninitializeRegistrationAction(&pPlan->rgRegistrationActions[i]);
162 }
163 MemFree(pPlan->rgRegistrationActions);
164 }
165
166 if (pPlan->rgRollbackRegistrationActions)
167 {
168 for (DWORD i = 0; i < pPlan->cRollbackRegistrationActions; ++i)
169 {
170 UninitializeRegistrationAction(&pPlan->rgRollbackRegistrationActions[i]);
171 }
172 MemFree(pPlan->rgRollbackRegistrationActions);
173 }
174
175 if (pPlan->rgCacheActions)
176 {
177 for (DWORD i = 0; i < pPlan->cCacheActions; ++i)
178 {
179 UninitializeCacheAction(&pPlan->rgCacheActions[i]);
180 }
181 MemFree(pPlan->rgCacheActions);
182 }
183
184 if (pPlan->rgExecuteActions)
185 {
186 for (DWORD i = 0; i < pPlan->cExecuteActions; ++i)
187 {
188 PlanUninitializeExecuteAction(&pPlan->rgExecuteActions[i]);
189 }
190 MemFree(pPlan->rgExecuteActions);
191 }
192
193 if (pPlan->rgRollbackActions)
194 {
195 for (DWORD i = 0; i < pPlan->cRollbackActions; ++i)
196 {
197 PlanUninitializeExecuteAction(&pPlan->rgRollbackActions[i]);
198 }
199 MemFree(pPlan->rgRollbackActions);
200 }
201
202 if (pPlan->rgCleanActions)
203 {
204 // Nothing needs to be freed inside clean actions today.
205 MemFree(pPlan->rgCleanActions);
206 }
207
208 if (pPlan->rgPlannedProviders)
209 {
210 ReleaseDependencyArray(pPlan->rgPlannedProviders, pPlan->cPlannedProviders);
211 }
212
213 if (pPlan->rgContainerProgress)
214 {
215 MemFree(pPlan->rgContainerProgress);
216 }
217
218 if (pPlan->shContainerProgress)
219 {
220 ReleaseDict(pPlan->shContainerProgress);
221 }
222
223 if (pPlan->rgPayloadProgress)
224 {
225 MemFree(pPlan->rgPayloadProgress);
226 }
227
228 if (pPlan->shPayloadProgress)
229 {
230 ReleaseDict(pPlan->shPayloadProgress);
231 }
232
233 if (pPlan->pPayloads)
234 {
235 ResetPlannedPayloadsState(pPlan->pPayloads);
236 }
237
238 memset(pPlan, 0, sizeof(BURN_PLAN));
239
240 if (pContainers->rgContainers)
241 {
242 for (DWORD i = 0; i < pContainers->cContainers; ++i)
243 {
244 ResetPlannedContainerState(&pContainers->rgContainers[i]);
245 }
246 }
247
248 // Reset the planned actions for each package.
249 if (pPackages->rgPackages)
250 {
251 for (DWORD i = 0; i < pPackages->cPackages; ++i)
252 {
253 ResetPlannedPackageState(&pPackages->rgPackages[i]);
254 }
255 }
256
257 ResetPlannedPayloadGroupState(pLayoutPayloads);
258
259 // Reset the planned state for each rollback boundary.
260 if (pPackages->rgRollbackBoundaries)
261 {
262 for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i)
263 {
264 ResetPlannedRollbackBoundaryState(&pPackages->rgRollbackBoundaries[i]);
265 }
266 }
267}
268
269extern "C" void PlanUninitializeExecuteAction(
270 __in BURN_EXECUTE_ACTION* pExecuteAction
271 )
272{
273 switch (pExecuteAction->type)
274 {
275 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
276 ReleaseStr(pExecuteAction->exePackage.sczIgnoreDependencies);
277 ReleaseStr(pExecuteAction->exePackage.sczAncestors);
278 break;
279
280 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
281 ReleaseStr(pExecuteAction->msiPackage.sczLogPath);
282 ReleaseMem(pExecuteAction->msiPackage.rgFeatures);
283 break;
284
285 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
286 ReleaseStr(pExecuteAction->mspTarget.sczTargetProductCode);
287 ReleaseStr(pExecuteAction->mspTarget.sczLogPath);
288 ReleaseMem(pExecuteAction->mspTarget.rgOrderedPatches);
289 break;
290
291 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
292 ReleaseStr(pExecuteAction->msuPackage.sczLogPath);
293 break;
294
295 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
296 ReleaseStr(pExecuteAction->packageDependency.sczBundleProviderKey);
297 break;
298 }
299}
300
301extern "C" HRESULT PlanSetVariables(
302 __in BOOTSTRAPPER_ACTION action,
303 __in BURN_VARIABLES* pVariables
304 )
305{
306 HRESULT hr = S_OK;
307
308 hr = VariableSetNumeric(pVariables, BURN_BUNDLE_ACTION, action, TRUE);
309 ExitOnFailure(hr, "Failed to set the bundle action built-in variable.");
310
311LExit:
312 return hr;
313}
314
315extern "C" HRESULT PlanDefaultPackageRequestState(
316 __in BURN_PACKAGE_TYPE packageType,
317 __in BOOTSTRAPPER_PACKAGE_STATE currentState,
318 __in BOOL fPermanent,
319 __in BOOTSTRAPPER_ACTION action,
320 __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition,
321 __in BOOTSTRAPPER_RELATION_TYPE relationType,
322 __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
323 )
324{
325 HRESULT hr = S_OK;
326 BOOTSTRAPPER_REQUEST_STATE defaultRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
327
328 // If doing layout, then always default to requesting the package be cached.
329 if (BOOTSTRAPPER_ACTION_LAYOUT == action)
330 {
331 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE;
332 }
333 else if (BOOTSTRAPPER_RELATION_PATCH == relationType && BURN_PACKAGE_TYPE_MSP == packageType)
334 {
335 // For patch related bundles, only install a patch if currently absent during install, modify, or repair.
336 if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == currentState && BOOTSTRAPPER_ACTION_INSTALL <= action)
337 {
338 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
339 }
340 else
341 {
342 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
343 }
344 }
345 else if (BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == currentState && BOOTSTRAPPER_ACTION_UNINSTALL != action)
346 {
347 // Superseded means the package is on the machine but not active, so only uninstall operations are allowed.
348 // All other operations do nothing.
349 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
350 }
351 else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == currentState && !(BOOTSTRAPPER_ACTION_UNINSTALL == action && BURN_PACKAGE_TYPE_MSP == packageType))
352 {
353 // Obsolete means the package is not on the machine and should not be installed, *except* patches can be obsolete
354 // and present so allow them to be removed during uninstall. Everyone else, gets nothing.
355 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
356 }
357 else // pick the best option for the action state and install condition.
358 {
359 hr = GetActionDefaultRequestState(action, fPermanent, currentState, &defaultRequestState);
360 ExitOnFailure(hr, "Failed to get default request state for action.");
361
362 // If we're doing an install, use the install condition
363 // to determine whether to use the default request state or make the package absent.
364 if (BOOTSTRAPPER_ACTION_UNINSTALL != action && BOOTSTRAPPER_PACKAGE_CONDITION_FALSE == installCondition)
365 {
366 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT;
367 }
368 else // just set the package to the default request state.
369 {
370 *pRequestState = defaultRequestState;
371 }
372 }
373
374LExit:
375 return hr;
376}
377
378extern "C" HRESULT PlanLayoutBundle(
379 __in BURN_PLAN* pPlan,
380 __in_z LPCWSTR wzExecutableName,
381 __in DWORD64 qwBundleSize,
382 __in BURN_VARIABLES* pVariables,
383 __in BURN_PAYLOAD_GROUP* pLayoutPayloads
384 )
385{
386 HRESULT hr = S_OK;
387 BURN_CACHE_ACTION* pCacheAction = NULL;
388 LPWSTR sczExecutablePath = NULL;
389
390 // Get the layout directory.
391 hr = VariableGetString(pVariables, BURN_BUNDLE_LAYOUT_DIRECTORY, &pPlan->sczLayoutDirectory);
392 if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory.
393 {
394 hr = VariableGetString(pVariables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, &pPlan->sczLayoutDirectory);
395 if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory.
396 {
397 hr = PathForCurrentProcess(&sczExecutablePath, NULL);
398 ExitOnFailure(hr, "Failed to get path for current executing process as layout directory.");
399
400 hr = PathGetDirectory(sczExecutablePath, &pPlan->sczLayoutDirectory);
401 ExitOnFailure(hr, "Failed to get executing process as layout directory.");
402 }
403 }
404 ExitOnFailure(hr, "Failed to get bundle layout directory property.");
405
406 hr = PathBackslashTerminate(&pPlan->sczLayoutDirectory);
407 ExitOnFailure(hr, "Failed to ensure layout directory is backslash terminated.");
408
409 hr = ProcessPayloadGroup(pPlan, pLayoutPayloads);
410 ExitOnFailure(hr, "Failed to process payload group for bundle.");
411
412 // Plan the layout of the bundle engine itself.
413 hr = AppendCacheAction(pPlan, &pCacheAction);
414 ExitOnFailure(hr, "Failed to append bundle start action.");
415
416 pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE;
417
418 hr = StrAllocString(&pCacheAction->bundleLayout.sczExecutableName, wzExecutableName, 0);
419 ExitOnFailure(hr, "Failed to to copy executable name for bundle.");
420
421 hr = CacheCalculateBundleLayoutWorkingPath(pPlan->wzBundleId, &pCacheAction->bundleLayout.sczUnverifiedPath);
422 ExitOnFailure(hr, "Failed to calculate bundle layout working path.");
423
424 pCacheAction->bundleLayout.qwBundleSize = qwBundleSize;
425 pCacheAction->bundleLayout.pPayloadGroup = pLayoutPayloads;
426
427 // Acquire + Verify + Finalize
428 pPlan->qwCacheSizeTotal += 3 * qwBundleSize;
429
430 ++pPlan->cOverallProgressTicksTotal;
431
432LExit:
433 ReleaseStr(sczExecutablePath);
434
435 return hr;
436}
437
438extern "C" HRESULT PlanForwardCompatibleBundles(
439 __in BURN_USER_EXPERIENCE* pUX,
440 __in BOOTSTRAPPER_COMMAND* pCommand,
441 __in BURN_PLAN* pPlan,
442 __in BURN_REGISTRATION* pRegistration,
443 __in BOOTSTRAPPER_ACTION action
444 )
445{
446 HRESULT hr = S_OK;
447 BOOL fRecommendIgnore = TRUE;
448 BOOL fIgnoreBundle = FALSE;
449
450 if (!pRegistration->fForwardCompatibleBundleExists)
451 {
452 ExitFunction();
453 }
454
455 // Only change the recommendation if an active parent was provided.
456 if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent)
457 {
458 // On install, recommend running the forward compatible bundle because there is an active parent. This
459 // will essentially register the parent with the forward compatible bundle.
460 if (BOOTSTRAPPER_ACTION_INSTALL == action)
461 {
462 fRecommendIgnore = FALSE;
463 }
464 else if (BOOTSTRAPPER_ACTION_UNINSTALL == action ||
465 BOOTSTRAPPER_ACTION_MODIFY == action ||
466 BOOTSTRAPPER_ACTION_REPAIR == action)
467 {
468 // When modifying the bundle, only recommend running the forward compatible bundle if the parent
469 // is already registered as a dependent of the provider key.
470 if (pRegistration->fParentRegisteredAsDependent)
471 {
472 fRecommendIgnore = FALSE;
473 }
474 }
475 }
476
477 for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle)
478 {
479 BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle;
480 if (!pRelatedBundle->fForwardCompatible)
481 {
482 continue;
483 }
484
485 fIgnoreBundle = fRecommendIgnore;
486
487 hr = UserExperienceOnPlanForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, &fIgnoreBundle);
488 ExitOnRootFailure(hr, "BA aborted plan forward compatible bundle.");
489
490 if (!fIgnoreBundle)
491 {
492 hr = PseudoBundleInitializePassthrough(&pPlan->forwardCompatibleBundle, pCommand, NULL, pRegistration->sczActiveParent, pRegistration->sczAncestors, &pRelatedBundle->package);
493 ExitOnFailure(hr, "Failed to initialize pass through bundle.");
494
495 pPlan->fEnabledForwardCompatibleBundle = TRUE;
496 break;
497 }
498 }
499
500LExit:
501 return hr;
502}
503
504extern "C" HRESULT PlanPackages(
505 __in BURN_USER_EXPERIENCE* pUX,
506 __in BURN_PACKAGES* pPackages,
507 __in BURN_PLAN* pPlan,
508 __in BURN_LOGGING* pLog,
509 __in BURN_VARIABLES* pVariables,
510 __in BOOTSTRAPPER_DISPLAY display,
511 __in BOOTSTRAPPER_RELATION_TYPE relationType
512 )
513{
514 HRESULT hr = S_OK;
515
516 hr = PlanPackagesHelper(pPackages->rgPackages, pPackages->cPackages, TRUE, pUX, pPlan, pLog, pVariables, display, relationType);
517
518 return hr;
519}
520
521extern "C" HRESULT PlanRegistration(
522 __in BURN_PLAN* pPlan,
523 __in BURN_REGISTRATION* pRegistration,
524 __in BOOTSTRAPPER_RESUME_TYPE /*resumeType*/,
525 __in BOOTSTRAPPER_RELATION_TYPE relationType,
526 __inout BOOL* pfContinuePlanning
527 )
528{
529 HRESULT hr = S_OK;
530 STRINGDICT_HANDLE sdBundleDependents = NULL;
531 STRINGDICT_HANDLE sdIgnoreDependents = NULL;
532
533 pPlan->fCanAffectMachineState = TRUE; // register the bundle since we're modifying machine state.
534 pPlan->fDisallowRemoval = FALSE; // by default the bundle can be planned to be removed
535 pPlan->fIgnoreAllDependents = pRegistration->fIgnoreAllDependents;
536
537 // Ensure the bundle is cached if not running from the cache.
538 if (!CacheBundleRunningFromCache())
539 {
540 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE;
541 }
542
543 // Always write registration since things may have changed or it just needs to be "fixed up".
544 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION;
545
546 // Always update our estimated size registration when installing/modify/repair since things
547 // may have been added or removed or it just needs to be "fixed up".
548 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE;
549
550 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
551 {
552 // If our provider key was detected and it points to our current bundle then we can
553 // unregister the bundle dependency.
554 if (pRegistration->sczDetectedProviderKeyBundleId &&
555 CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczId, -1, pRegistration->sczDetectedProviderKeyBundleId, -1))
556 {
557 pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER;
558 }
559 else // log that another bundle already owned our registration, hopefully this only happens when a newer version
560 { // of a bundle installed and is in the process of upgrading us.
561 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL, pRegistration->sczProviderKey, pRegistration->sczDetectedProviderKeyBundleId);
562 }
563
564 // Create the dictionary of dependents that should be ignored.
565 hr = DictCreateStringList(&sdIgnoreDependents, 5, DICT_FLAG_CASEINSENSITIVE);
566 ExitOnFailure(hr, "Failed to create the string dictionary.");
567
568 // If the self-dependent dependent exists, plan its removal. If we did not do this, we
569 // would prevent self-removal.
570 if (pRegistration->fSelfRegisteredAsDependent)
571 {
572 hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER, pRegistration->wzSelfDependent, pRegistration->sczId);
573 ExitOnFailure(hr, "Failed to allocate registration action.");
574
575 hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pRegistration->wzSelfDependent);
576 ExitOnFailure(hr, "Failed to add self-dependent to ignore dependents.");
577 }
578
579 if (!pPlan->fIgnoreAllDependents)
580 {
581 // If we are not doing an upgrade, we check to see if there are still dependents on us and if so we skip planning.
582 // However, when being upgraded, we always execute our uninstall because a newer version of us is probably
583 // already on the machine and we need to clean up the stuff specific to this bundle.
584 if (BOOTSTRAPPER_RELATION_UPGRADE != relationType)
585 {
586 // If there were other dependencies to ignore, add them.
587 for (DWORD iDependency = 0; iDependency < pRegistration->cIgnoredDependencies; ++iDependency)
588 {
589 DEPENDENCY* pDependency = pRegistration->rgIgnoredDependencies + iDependency;
590
591 hr = DictKeyExists(sdIgnoreDependents, pDependency->sczKey);
592 if (E_NOTFOUND != hr)
593 {
594 ExitOnFailure(hr, "Failed to check the dictionary of ignored dependents.");
595 }
596 else
597 {
598 hr = DictAddKey(sdIgnoreDependents, pDependency->sczKey);
599 ExitOnFailure(hr, "Failed to add dependent key to ignored dependents.");
600 }
601 }
602
603 // For addon or patch bundles, dependent related bundles should be ignored. This allows
604 // that addon or patch to be removed even though bundles it targets still are registered.
605 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
606 {
607 const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
608
609 if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType)
610 {
611 for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j)
612 {
613 const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j;
614
615 hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pProvider->sczKey);
616 ExitOnFailure(hr, "Failed to add dependent bundle provider key to ignore dependents.");
617 }
618 }
619 }
620
621 // If there are any (non-ignored and not-planned-to-be-removed) dependents left, skip planning.
622 for (DWORD iDependent = 0; iDependent < pRegistration->cDependents; ++iDependent)
623 {
624 DEPENDENCY* pDependent = pRegistration->rgDependents + iDependent;
625
626 hr = DictKeyExists(sdIgnoreDependents, pDependent->sczKey);
627 if (E_NOTFOUND == hr)
628 {
629 hr = S_OK;
630
631 // TODO: callback to the BA and let it have the option to ignore this dependent?
632 if (!pPlan->fDisallowRemoval)
633 {
634 pPlan->fDisallowRemoval = TRUE; // ensure the registration stays
635 *pfContinuePlanning = FALSE; // skip the rest of planning.
636
637 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS);
638 }
639
640 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_DEPENDENT, pDependent->sczKey, LoggingStringOrUnknownIfNull(pDependent->sczName));
641 }
642 ExitOnFailure(hr, "Failed to check for remaining dependents during planning.");
643 }
644 }
645 }
646 }
647 else
648 {
649 BOOL fAddonOrPatchBundle = (pRegistration->cAddonCodes || pRegistration->cPatchCodes);
650
651 // Always plan to write our provider key registration when installing/modify/repair to "fix it"
652 // if broken.
653 pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER;
654
655 // Create the dictionary of bundle dependents.
656 hr = DictCreateStringList(&sdBundleDependents, 5, DICT_FLAG_CASEINSENSITIVE);
657 ExitOnFailure(hr, "Failed to create the string dictionary.");
658
659 for (DWORD iDependent = 0; iDependent < pRegistration->cDependents; ++iDependent)
660 {
661 DEPENDENCY* pDependent = pRegistration->rgDependents + iDependent;
662
663 hr = DictKeyExists(sdBundleDependents, pDependent->sczKey);
664 if (E_NOTFOUND == hr)
665 {
666 hr = DictAddKey(sdBundleDependents, pDependent->sczKey);
667 ExitOnFailure(hr, "Failed to add dependent key to bundle dependents.");
668 }
669 ExitOnFailure(hr, "Failed to check the dictionary of bundle dependents.");
670 }
671
672 // Register each dependent related bundle. The ensures that addons and patches are reference
673 // counted and stick around until the last targeted bundle is removed.
674 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
675 {
676 const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
677
678 if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType)
679 {
680 for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j)
681 {
682 const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j;
683
684 hr = DictKeyExists(sdBundleDependents, pProvider->sczKey);
685 if (E_NOTFOUND == hr)
686 {
687 hr = DictAddKey(sdBundleDependents, pProvider->sczKey);
688 ExitOnFailure(hr, "Failed to add new dependent key to bundle dependents.");
689
690 hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, pProvider->sczKey, pRelatedBundle->package.sczId);
691 ExitOnFailure(hr, "Failed to add registration action for dependent related bundle.");
692 }
693 ExitOnFailure(hr, "Failed to check the dictionary of bundle dependents.");
694 }
695 }
696 }
697
698 // Only do the following if we decided there was a dependent self to register. If so and and an explicit parent was
699 // provided, register dependent self. Otherwise, if this bundle is not an addon or patch bundle then self-regisiter
700 // as our own dependent.
701 if (pRegistration->wzSelfDependent && !pRegistration->fSelfRegisteredAsDependent && (pRegistration->sczActiveParent || !fAddonOrPatchBundle))
702 {
703 hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, pRegistration->wzSelfDependent, pRegistration->sczId);
704 ExitOnFailure(hr, "Failed to add registration action for self dependent.");
705 }
706 }
707
708LExit:
709 ReleaseDict(sdBundleDependents);
710 ReleaseDict(sdIgnoreDependents);
711
712 return hr;
713}
714
715extern "C" HRESULT PlanPassThroughBundle(
716 __in BURN_USER_EXPERIENCE* pUX,
717 __in BURN_PACKAGE* pPackage,
718 __in BURN_PLAN* pPlan,
719 __in BURN_LOGGING* pLog,
720 __in BURN_VARIABLES* pVariables,
721 __in BOOTSTRAPPER_DISPLAY display,
722 __in BOOTSTRAPPER_RELATION_TYPE relationType
723 )
724{
725 HRESULT hr = S_OK;
726
727 // Plan passthrough package.
728 // Passthrough packages are never cleaned up by the calling bundle (they delete themselves when appropriate)
729 // so we don't need to plan clean up.
730 hr = PlanPackagesHelper(pPackage, 1, FALSE, pUX, pPlan, pLog, pVariables, display, relationType);
731 ExitOnFailure(hr, "Failed to process passthrough package.");
732
733LExit:
734 return hr;
735}
736
737extern "C" HRESULT PlanUpdateBundle(
738 __in BURN_USER_EXPERIENCE* pUX,
739 __in BURN_PACKAGE* pPackage,
740 __in BURN_PLAN* pPlan,
741 __in BURN_LOGGING* pLog,
742 __in BURN_VARIABLES* pVariables,
743 __in BOOTSTRAPPER_DISPLAY display,
744 __in BOOTSTRAPPER_RELATION_TYPE relationType
745 )
746{
747 HRESULT hr = S_OK;
748
749 // Plan update package.
750 hr = PlanPackagesHelper(pPackage, 1, TRUE, pUX, pPlan, pLog, pVariables, display, relationType);
751 ExitOnFailure(hr, "Failed to process update package.");
752
753LExit:
754 return hr;
755}
756
757static HRESULT PlanPackagesHelper(
758 __in BURN_PACKAGE* rgPackages,
759 __in DWORD cPackages,
760 __in BOOL fPlanCleanPackages,
761 __in BURN_USER_EXPERIENCE* pUX,
762 __in BURN_PLAN* pPlan,
763 __in BURN_LOGGING* pLog,
764 __in BURN_VARIABLES* pVariables,
765 __in BOOTSTRAPPER_DISPLAY display,
766 __in BOOTSTRAPPER_RELATION_TYPE relationType
767 )
768{
769 HRESULT hr = S_OK;
770 BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine.
771 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
772 HANDLE hSyncpointEvent = NULL;
773
774 // Initialize the packages.
775 for (DWORD i = 0; i < cPackages; ++i)
776 {
777 DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i;
778 BURN_PACKAGE* pPackage = rgPackages + iPackage;
779
780 hr = InitializePackage(pPlan, pUX, pVariables, pPackage, relationType);
781 ExitOnFailure(hr, "Failed to initialize package.");
782 }
783
784 // Initialize the patch targets after all packages, since they could rely on the requested state of packages that are after the patch's package in the chain.
785 for (DWORD i = 0; i < cPackages; ++i)
786 {
787 DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i;
788 BURN_PACKAGE* pPackage = rgPackages + iPackage;
789
790 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
791 {
792 hr = MspEnginePlanInitializePackage(pPackage, pUX);
793 ExitOnFailure(hr, "Failed to initialize plan package: %ls", pPackage->sczId);
794 }
795 }
796
797 // Plan the packages.
798 for (DWORD i = 0; i < cPackages; ++i)
799 {
800 DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i;
801 BURN_PACKAGE* pPackage = rgPackages + iPackage;
802
803 hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, &hSyncpointEvent, &pRollbackBoundary);
804 ExitOnFailure(hr, "Failed to process package.");
805 }
806
807 // If we still have an open rollback boundary, complete it.
808 if (pRollbackBoundary)
809 {
810 hr = PlanRollbackBoundaryComplete(pPlan);
811 ExitOnFailure(hr, "Failed to plan final rollback boundary complete.");
812
813 pRollbackBoundary = NULL;
814 }
815
816 if (fPlanCleanPackages)
817 {
818 // Plan clean up of packages.
819 for (DWORD i = 0; i < cPackages; ++i)
820 {
821 DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i;
822 BURN_PACKAGE* pPackage = rgPackages + iPackage;
823
824 hr = PlanCleanPackage(pPlan, pPackage);
825 ExitOnFailure(hr, "Failed to plan clean package.");
826 }
827 }
828
829 // Remove unnecessary actions.
830 hr = PlanFinalizeActions(pPlan);
831 ExitOnFailure(hr, "Failed to remove unnecessary actions from plan.");
832
833 CalculateExpectedRegistrationStates(rgPackages, cPackages);
834
835 // Let the BA know the actions that were planned.
836 for (DWORD i = 0; i < cPackages; ++i)
837 {
838 DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i;
839 BURN_PACKAGE* pPackage = rgPackages + iPackage;
840
841 UserExperienceOnPlannedPackage(pUX, pPackage->sczId, pPackage->execute, pPackage->rollback, pPackage->fPlannedCache, pPackage->fPlannedUncache);
842 }
843
844LExit:
845 return hr;
846}
847
848static HRESULT InitializePackage(
849 __in BURN_PLAN* pPlan,
850 __in BURN_USER_EXPERIENCE* pUX,
851 __in BURN_VARIABLES* pVariables,
852 __in BURN_PACKAGE* pPackage,
853 __in BOOTSTRAPPER_RELATION_TYPE relationType
854 )
855{
856 HRESULT hr = S_OK;
857 BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition = BOOTSTRAPPER_PACKAGE_CONDITION_DEFAULT;
858 BOOL fInstallCondition = FALSE;
859 BOOL fBeginCalled = FALSE;
860
861 if (pPackage->fCanAffectRegistration)
862 {
863 pPackage->expectedCacheRegistrationState = pPackage->cacheRegistrationState;
864 pPackage->expectedInstallRegistrationState = pPackage->installRegistrationState;
865 }
866
867 if (pPackage->sczInstallCondition && *pPackage->sczInstallCondition)
868 {
869 hr = ConditionEvaluate(pVariables, pPackage->sczInstallCondition, &fInstallCondition);
870 ExitOnFailure(hr, "Failed to evaluate install condition.");
871
872 installCondition = fInstallCondition ? BOOTSTRAPPER_PACKAGE_CONDITION_TRUE : BOOTSTRAPPER_PACKAGE_CONDITION_FALSE;
873 }
874
875 // Remember the default requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it.
876 hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, !pPackage->fUninstallable, pPlan->action, installCondition, relationType, &pPackage->defaultRequested);
877 ExitOnFailure(hr, "Failed to set default package state.");
878
879 pPackage->requested = pPackage->defaultRequested;
880 fBeginCalled = TRUE;
881
882 hr = UserExperienceOnPlanPackageBegin(pUX, pPackage->sczId, pPackage->currentState, pPackage->fCached, installCondition, &pPackage->requested, &pPackage->cacheType);
883 ExitOnRootFailure(hr, "BA aborted plan package begin.");
884
885 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
886 {
887 hr = MsiEnginePlanInitializePackage(pPackage, pVariables, pUX);
888 ExitOnFailure(hr, "Failed to initialize plan package: %ls", pPackage->sczId);
889 }
890
891LExit:
892 if (fBeginCalled)
893 {
894 UserExperienceOnPlanPackageComplete(pUX, pPackage->sczId, hr, pPackage->requested);
895 }
896
897 return hr;
898}
899
900static HRESULT ProcessPackage(
901 __in BOOL fBundlePerMachine,
902 __in BURN_USER_EXPERIENCE* pUX,
903 __in BURN_PLAN* pPlan,
904 __in BURN_PACKAGE* pPackage,
905 __in BURN_LOGGING* pLog,
906 __in BURN_VARIABLES* pVariables,
907 __in BOOTSTRAPPER_DISPLAY display,
908 __inout HANDLE* phSyncpointEvent,
909 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
910 )
911{
912 HRESULT hr = S_OK;
913 BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary = NULL;
914
915 pEffectiveRollbackBoundary = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackage->pRollbackBoundaryBackward : pPackage->pRollbackBoundaryForward;
916 hr = ProcessPackageRollbackBoundary(pPlan, pEffectiveRollbackBoundary, ppRollbackBoundary);
917 ExitOnFailure(hr, "Failed to process package rollback boundary.");
918
919 if (BOOTSTRAPPER_ACTION_LAYOUT == pPlan->action)
920 {
921 if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested)
922 {
923 hr = PlanLayoutPackage(pPlan, pPackage);
924 ExitOnFailure(hr, "Failed to plan layout package.");
925 }
926 }
927 else
928 {
929 if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested)
930 {
931 // If the package is in a requested state, plan it.
932 hr = PlanExecutePackage(fBundlePerMachine, display, pUX, pPlan, pPackage, pLog, pVariables, phSyncpointEvent);
933 ExitOnFailure(hr, "Failed to plan execute package.");
934 }
935 else
936 {
937 if (ForceCache(pPlan, pPackage))
938 {
939 hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent);
940 ExitOnFailure(hr, "Failed to plan cache package.");
941
942 if (pPackage->fPerMachine)
943 {
944 pPlan->fPerMachine = TRUE;
945 }
946 }
947
948 // Make sure the package is properly ref-counted even if no plan is requested.
949 hr = PlanDependencyActions(fBundlePerMachine, pPlan, pPackage);
950 ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId);
951 }
952 }
953
954 // Add the checkpoint after each package and dependency registration action.
955 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback || BURN_DEPENDENCY_ACTION_NONE != pPackage->dependencyExecute)
956 {
957 hr = PlanExecuteCheckpoint(pPlan);
958 ExitOnFailure(hr, "Failed to append execute checkpoint.");
959 }
960
961LExit:
962 return hr;
963}
964
965static HRESULT ProcessPackageRollbackBoundary(
966 __in BURN_PLAN* pPlan,
967 __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary,
968 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
969 )
970{
971 HRESULT hr = S_OK;
972
973 // If the package marks the start of a rollback boundary, start a new one.
974 if (pEffectiveRollbackBoundary)
975 {
976 // Complete previous rollback boundary.
977 if (*ppRollbackBoundary)
978 {
979 hr = PlanRollbackBoundaryComplete(pPlan);
980 ExitOnFailure(hr, "Failed to plan rollback boundary complete.");
981 }
982
983 // Start new rollback boundary.
984 hr = PlanRollbackBoundaryBegin(pPlan, pEffectiveRollbackBoundary);
985 ExitOnFailure(hr, "Failed to plan rollback boundary begin.");
986
987 *ppRollbackBoundary = pEffectiveRollbackBoundary;
988 }
989
990LExit:
991 return hr;
992}
993
994extern "C" HRESULT PlanLayoutContainer(
995 __in BURN_PLAN* pPlan,
996 __in BURN_CONTAINER* pContainer
997 )
998{
999 HRESULT hr = S_OK;
1000 BURN_CACHE_ACTION* pCacheAction = NULL;
1001
1002 Assert(!pContainer->fPlanned);
1003 pContainer->fPlanned = TRUE;
1004
1005 if (pPlan->sczLayoutDirectory)
1006 {
1007 if (!pContainer->fAttached)
1008 {
1009 hr = AppendCacheAction(pPlan, &pCacheAction);
1010 ExitOnFailure(hr, "Failed to append package start action.");
1011
1012 pCacheAction->type = BURN_CACHE_ACTION_TYPE_CONTAINER;
1013 pCacheAction->container.pContainer = pContainer;
1014
1015 // Acquire + Verify + Finalize
1016 pPlan->qwCacheSizeTotal += 3 * pContainer->qwFileSize;
1017 }
1018 }
1019 else
1020 {
1021 if (!pContainer->fActuallyAttached)
1022 {
1023 // Acquire
1024 pPlan->qwCacheSizeTotal += pContainer->qwFileSize;
1025 }
1026 }
1027
1028 if (!pContainer->sczUnverifiedPath)
1029 {
1030 if (pContainer->fActuallyAttached)
1031 {
1032 hr = PathForCurrentProcess(&pContainer->sczUnverifiedPath, NULL);
1033 ExitOnFailure(hr, "Failed to get path for executing module as attached container working path.");
1034 }
1035 else
1036 {
1037 hr = CacheCalculateContainerWorkingPath(pPlan->wzBundleId, pContainer, &pContainer->sczUnverifiedPath);
1038 ExitOnFailure(hr, "Failed to calculate unverified path for container.");
1039 }
1040 }
1041
1042LExit:
1043 return hr;
1044}
1045
1046extern "C" HRESULT PlanLayoutPackage(
1047 __in BURN_PLAN* pPlan,
1048 __in BURN_PACKAGE* pPackage
1049 )
1050{
1051 HRESULT hr = S_OK;
1052 BURN_CACHE_ACTION* pCacheAction = NULL;
1053
1054 hr = ProcessPayloadGroup(pPlan, &pPackage->payloads);
1055 ExitOnFailure(hr, "Failed to process payload group for package: %ls.", pPackage->sczId);
1056
1057 hr = AppendCacheAction(pPlan, &pCacheAction);
1058 ExitOnFailure(hr, "Failed to append package start action.");
1059
1060 pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE;
1061 pCacheAction->package.pPackage = pPackage;
1062
1063 ++pPlan->cOverallProgressTicksTotal;
1064
1065LExit:
1066 return hr;
1067}
1068
1069extern "C" HRESULT PlanExecutePackage(
1070 __in BOOL fPerMachine,
1071 __in BOOTSTRAPPER_DISPLAY display,
1072 __in BURN_USER_EXPERIENCE* pUserExperience,
1073 __in BURN_PLAN* pPlan,
1074 __in BURN_PACKAGE* pPackage,
1075 __in BURN_LOGGING* pLog,
1076 __in BURN_VARIABLES* pVariables,
1077 __inout HANDLE* phSyncpointEvent
1078 )
1079{
1080 HRESULT hr = S_OK;
1081 BOOL fRequestedCache = BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested || ForceCache(pPlan, pPackage);
1082
1083 hr = CalculateExecuteActions(pPackage, pPlan->pActiveRollbackBoundary);
1084 ExitOnFailure(hr, "Failed to calculate plan actions for package: %ls", pPackage->sczId);
1085
1086 // Calculate package states based on reference count and plan certain dependency actions prior to planning the package execute action.
1087 hr = DependencyPlanPackageBegin(fPerMachine, pPackage, pPlan);
1088 ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId);
1089
1090 if (fRequestedCache || NeedsCache(pPackage, TRUE))
1091 {
1092 hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent);
1093 ExitOnFailure(hr, "Failed to plan cache package.");
1094 }
1095 else if (!pPackage->fCached && NeedsCache(pPackage, FALSE))
1096 {
1097 // TODO: this decision should be made during apply instead of plan based on whether the package is actually cached.
1098 // If the package is not in the cache, disable any rollback that would require the package from the cache.
1099 LogId(REPORT_STANDARD, MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE, pPackage->sczId, LoggingActionStateToString(pPackage->rollback));
1100 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1101 }
1102
1103 // Add the cache and install size to estimated size if it will be on the machine at the end of the install
1104 if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested ||
1105 fRequestedCache ||
1106 (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_ABSENT < pPackage->requested)
1107 )
1108 {
1109 // If the package will remain in the cache, add the package size to the estimated size
1110 if (BOOTSTRAPPER_CACHE_TYPE_REMOVE < pPackage->cacheType)
1111 {
1112 pPlan->qwEstimatedSize += pPackage->qwSize;
1113 }
1114
1115 // If the package will end up installed on the machine, add the install size to the estimated size.
1116 if (BOOTSTRAPPER_REQUEST_STATE_CACHE < pPackage->requested)
1117 {
1118 // MSP packages get cached automatically by windows installer with any embedded cabs, so include that in the size as well
1119 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
1120 {
1121 pPlan->qwEstimatedSize += pPackage->qwSize;
1122 }
1123
1124 pPlan->qwEstimatedSize += pPackage->qwInstallSize;
1125 }
1126 }
1127
1128 // Add execute actions.
1129 switch (pPackage->type)
1130 {
1131 case BURN_PACKAGE_TYPE_EXE:
1132 hr = ExeEnginePlanAddPackage(NULL, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent);
1133 break;
1134
1135 case BURN_PACKAGE_TYPE_MSI:
1136 hr = MsiEnginePlanAddPackage(display, pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent);
1137 break;
1138
1139 case BURN_PACKAGE_TYPE_MSP:
1140 hr = MspEnginePlanAddPackage(display, pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent);
1141 break;
1142
1143 case BURN_PACKAGE_TYPE_MSU:
1144 hr = MsuEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables, *phSyncpointEvent);
1145 break;
1146
1147 default:
1148 hr = E_UNEXPECTED;
1149 ExitOnFailure(hr, "Invalid package type.");
1150 }
1151 ExitOnFailure(hr, "Failed to add plan actions for package: %ls", pPackage->sczId);
1152
1153 // Plan certain dependency actions after planning the package execute action.
1154 hr = DependencyPlanPackageComplete(pPackage, pPlan);
1155 ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId);
1156
1157 // If we are going to take any action on this package, add progress for it.
1158 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
1159 {
1160 LoggingIncrementPackageSequence();
1161
1162 ++pPlan->cExecutePackagesTotal;
1163 ++pPlan->cOverallProgressTicksTotal;
1164
1165 // If package is per-machine and is being executed, flag the plan to be per-machine as well.
1166 if (pPackage->fPerMachine)
1167 {
1168 pPlan->fPerMachine = TRUE;
1169 }
1170 }
1171
1172LExit:
1173 return hr;
1174}
1175
1176extern "C" HRESULT PlanDefaultRelatedBundleRequestState(
1177 __in BOOTSTRAPPER_RELATION_TYPE commandRelationType,
1178 __in BOOTSTRAPPER_RELATION_TYPE relatedBundleRelationType,
1179 __in BOOTSTRAPPER_ACTION action,
1180 __in VERUTIL_VERSION* pRegistrationVersion,
1181 __in VERUTIL_VERSION* pRelatedBundleVersion,
1182 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestState
1183 )
1184{
1185 HRESULT hr = S_OK;
1186 int nCompareResult = 0;
1187
1188 // Never touch related bundles during Cache.
1189 if (BOOTSTRAPPER_ACTION_CACHE == action)
1190 {
1191 ExitFunction1(*pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE);
1192 }
1193
1194 switch (relatedBundleRelationType)
1195 {
1196 case BOOTSTRAPPER_RELATION_UPGRADE:
1197 if (BOOTSTRAPPER_RELATION_UPGRADE != commandRelationType && BOOTSTRAPPER_ACTION_UNINSTALL < action)
1198 {
1199 hr = VerCompareParsedVersions(pRegistrationVersion, pRelatedBundleVersion, &nCompareResult);
1200 ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistrationVersion ? pRegistrationVersion->sczVersion : NULL, pRelatedBundleVersion ? pRelatedBundleVersion->sczVersion : NULL);
1201
1202 *pRequestState = (nCompareResult < 0) ? BOOTSTRAPPER_REQUEST_STATE_NONE : BOOTSTRAPPER_REQUEST_STATE_ABSENT;
1203 }
1204 break;
1205 case BOOTSTRAPPER_RELATION_PATCH: __fallthrough;
1206 case BOOTSTRAPPER_RELATION_ADDON:
1207 if (BOOTSTRAPPER_ACTION_UNINSTALL == action)
1208 {
1209 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT;
1210 }
1211 else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action)
1212 {
1213 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1214 }
1215 else if (BOOTSTRAPPER_ACTION_REPAIR == action)
1216 {
1217 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR;
1218 }
1219 break;
1220 case BOOTSTRAPPER_RELATION_DEPENDENT:
1221 // Automatically repair dependent bundles to restore missing
1222 // packages after uninstall unless we're being upgraded with the
1223 // assumption that upgrades are cumulative (as intended).
1224 if (BOOTSTRAPPER_RELATION_UPGRADE != commandRelationType && BOOTSTRAPPER_ACTION_UNINSTALL == action)
1225 {
1226 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR;
1227 }
1228 break;
1229 case BOOTSTRAPPER_RELATION_DETECT:
1230 break;
1231 default:
1232 hr = E_UNEXPECTED;
1233 ExitOnFailure(hr, "Unexpected relation type encountered during plan: %d", relatedBundleRelationType);
1234 break;
1235 }
1236
1237LExit:
1238 return hr;
1239}
1240
1241extern "C" HRESULT PlanRelatedBundlesBegin(
1242 __in BURN_USER_EXPERIENCE* pUserExperience,
1243 __in BURN_REGISTRATION* pRegistration,
1244 __in BOOTSTRAPPER_RELATION_TYPE relationType,
1245 __in BURN_PLAN* pPlan
1246 )
1247{
1248 HRESULT hr = S_OK;
1249 LPWSTR* rgsczAncestors = NULL;
1250 UINT cAncestors = 0;
1251 STRINGDICT_HANDLE sdAncestors = NULL;
1252
1253 if (pRegistration->sczAncestors)
1254 {
1255 hr = StrSplitAllocArray(&rgsczAncestors, &cAncestors, pRegistration->sczAncestors, L";");
1256 ExitOnFailure(hr, "Failed to create string array from ancestors.");
1257
1258 hr = DictCreateStringListFromArray(&sdAncestors, rgsczAncestors, cAncestors, DICT_FLAG_CASEINSENSITIVE);
1259 ExitOnFailure(hr, "Failed to create dictionary from ancestors array.");
1260 }
1261
1262 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
1263 {
1264 BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
1265
1266 if (!pRelatedBundle->fPlannable)
1267 {
1268 continue;
1269 }
1270
1271 pRelatedBundle->package.defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1272 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1273
1274 // Do not execute the same bundle twice.
1275 if (sdAncestors)
1276 {
1277 hr = DictKeyExists(sdAncestors, pRelatedBundle->package.sczId);
1278 if (SUCCEEDED(hr))
1279 {
1280 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType));
1281 continue;
1282 }
1283 else if (E_NOTFOUND != hr)
1284 {
1285 ExitOnFailure(hr, "Failed to lookup the bundle ID in the ancestors dictionary.");
1286 }
1287 }
1288 else if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_RELATION_NONE != relationType)
1289 {
1290 // Avoid repair loops for older bundles that do not handle ancestors.
1291 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRelationTypeToString(relationType));
1292 continue;
1293 }
1294
1295 // Pass along any ancestors and ourself to prevent infinite loops.
1296 pRelatedBundle->package.Exe.wzAncestors = pRegistration->sczBundlePackageAncestors;
1297
1298 hr = PlanDefaultRelatedBundleRequestState(relationType, pRelatedBundle->relationType, pPlan->action, pRegistration->pVersion, pRelatedBundle->pVersion, &pRelatedBundle->package.requested);
1299 ExitOnFailure(hr, "Failed to get default request state for related bundle.");
1300
1301 pRelatedBundle->package.defaultRequested = pRelatedBundle->package.requested;
1302
1303 hr = UserExperienceOnPlanRelatedBundle(pUserExperience, pRelatedBundle->package.sczId, &pRelatedBundle->package.requested);
1304 ExitOnRootFailure(hr, "BA aborted plan related bundle.");
1305
1306 // Log when the BA changed the bundle state so the engine doesn't get blamed for planning the wrong thing.
1307 if (pRelatedBundle->package.requested != pRelatedBundle->package.defaultRequested)
1308 {
1309 LogId(REPORT_STANDARD, MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST, pRelatedBundle->package.sczId, LoggingRequestStateToString(pRelatedBundle->package.requested), LoggingRequestStateToString(pRelatedBundle->package.defaultRequested));
1310 }
1311
1312 // If uninstalling and the dependent related bundle may be executed, ignore its provider key to allow for downgrades with ref-counting.
1313 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested)
1314 {
1315 if (0 < pRelatedBundle->package.cDependencyProviders)
1316 {
1317 // Bundles only support a single provider key.
1318 const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders;
1319
1320 hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, pProvider->sczDisplayName);
1321 ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey);
1322 }
1323 }
1324 }
1325
1326LExit:
1327 ReleaseDict(sdAncestors);
1328 ReleaseStrArray(rgsczAncestors, cAncestors);
1329
1330 return hr;
1331}
1332
1333extern "C" HRESULT PlanRelatedBundlesComplete(
1334 __in BURN_REGISTRATION* pRegistration,
1335 __in BURN_PLAN* pPlan,
1336 __in BURN_LOGGING* pLog,
1337 __in BURN_VARIABLES* pVariables,
1338 __in DWORD dwExecuteActionEarlyIndex
1339 )
1340{
1341 HRESULT hr = S_OK;
1342 LPWSTR sczIgnoreDependencies = NULL;
1343 STRINGDICT_HANDLE sdProviderKeys = NULL;
1344
1345 // Get the list of dependencies to ignore to pass to related bundles.
1346 hr = DependencyAllocIgnoreDependencies(pPlan, &sczIgnoreDependencies);
1347 ExitOnFailure(hr, "Failed to get the list of dependencies to ignore.");
1348
1349 hr = DictCreateStringList(&sdProviderKeys, pPlan->cExecuteActions, DICT_FLAG_CASEINSENSITIVE);
1350 ExitOnFailure(hr, "Failed to create dictionary for planned packages.");
1351
1352 BOOL fExecutingAnyPackage = FALSE;
1353
1354 for (DWORD i = 0; i < pPlan->cExecuteActions; ++i)
1355 {
1356 if (BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE == pPlan->rgExecuteActions[i].type && BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].exePackage.action)
1357 {
1358 fExecutingAnyPackage = TRUE;
1359
1360 BURN_PACKAGE* pPackage = pPlan->rgExecuteActions[i].packageProvider.pPackage;
1361 if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol)
1362 {
1363 if (0 < pPackage->cDependencyProviders)
1364 {
1365 // Bundles only support a single provider key.
1366 const BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders;
1367 DictAddKey(sdProviderKeys, pProvider->sczKey);
1368 }
1369 }
1370 }
1371 else
1372 {
1373 switch (pPlan->rgExecuteActions[i].type)
1374 {
1375 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
1376 fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msiPackage.action);
1377 break;
1378
1379 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
1380 fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].mspTarget.action);
1381 break;
1382
1383 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
1384 fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msuPackage.action);
1385 break;
1386 }
1387 }
1388 }
1389
1390 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
1391 {
1392 DWORD *pdwInsertIndex = NULL;
1393 BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
1394
1395 if (!pRelatedBundle->fPlannable)
1396 {
1397 continue;
1398 }
1399
1400 // Do not execute if a major upgrade to the related bundle is an embedded bundle (Provider keys are the same)
1401 if (0 < pRelatedBundle->package.cDependencyProviders)
1402 {
1403 // Bundles only support a single provider key.
1404 const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders;
1405 hr = DictKeyExists(sdProviderKeys, pProvider->sczKey);
1406 if (E_NOTFOUND != hr)
1407 {
1408 ExitOnFailure(hr, "Failed to check the dictionary for a related bundle provider key: \"%ls\".", pProvider->sczKey);
1409 // 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
1410 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), pProvider->sczKey);
1411 continue;
1412 }
1413 else
1414 {
1415 hr = S_OK;
1416 }
1417 }
1418
1419 // For an uninstall, there is no need to repair dependent bundles if no packages are executing.
1420 if (!fExecutingAnyPackage && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pRelatedBundle->package.requested && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1421 {
1422 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1423 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType));
1424 }
1425
1426 if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1427 {
1428 // Addon and patch bundles will be passed a list of dependencies to ignore for planning.
1429 hr = StrAllocString(&pRelatedBundle->package.Exe.sczIgnoreDependencies, sczIgnoreDependencies, 0);
1430 ExitOnFailure(hr, "Failed to copy the list of dependencies to ignore.");
1431
1432 // Uninstall addons and patches early in the chain, before other packages are uninstalled.
1433 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1434 {
1435 pdwInsertIndex = &dwExecuteActionEarlyIndex;
1436 }
1437 }
1438
1439 if (BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested)
1440 {
1441 hr = ExeEnginePlanCalculatePackage(&pRelatedBundle->package);
1442 ExitOnFailure(hr, "Failed to calcuate plan for related bundle: %ls", pRelatedBundle->package.sczId);
1443
1444 // Calculate package states based on reference count for addon and patch related bundles.
1445 if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1446 {
1447 hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan);
1448 ExitOnFailure(hr, "Failed to begin plan dependency actions to package: %ls", pRelatedBundle->package.sczId);
1449
1450 // If uninstalling a related bundle, make sure the bundle is uninstalled after removing registration.
1451 if (pdwInsertIndex && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1452 {
1453 ++(*pdwInsertIndex);
1454 }
1455 }
1456
1457 hr = ExeEnginePlanAddPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan, pLog, pVariables, NULL);
1458 ExitOnFailure(hr, "Failed to add to plan related bundle: %ls", pRelatedBundle->package.sczId);
1459
1460 // Calculate package states based on reference count for addon and patch related bundles.
1461 if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1462 {
1463 hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan);
1464 ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId);
1465 }
1466
1467 // If we are going to take any action on this package, add progress for it.
1468 if (BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.execute || BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.rollback)
1469 {
1470 LoggingIncrementPackageSequence();
1471
1472 ++pPlan->cExecutePackagesTotal;
1473 ++pPlan->cOverallProgressTicksTotal;
1474 }
1475
1476 // If package is per-machine and is being executed, flag the plan to be per-machine as well.
1477 if (pRelatedBundle->package.fPerMachine)
1478 {
1479 pPlan->fPerMachine = TRUE;
1480 }
1481 }
1482 else if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1483 {
1484 // Make sure the package is properly ref-counted even if no plan is requested.
1485 hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan);
1486 ExitOnFailure(hr, "Failed to begin plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId);
1487
1488 hr = DependencyPlanPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan);
1489 ExitOnFailure(hr, "Failed to plan related bundle package provider actions.");
1490
1491 hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan);
1492 ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId);
1493 }
1494 }
1495
1496LExit:
1497 ReleaseDict(sdProviderKeys);
1498 ReleaseStr(sczIgnoreDependencies);
1499
1500 return hr;
1501}
1502
1503extern "C" HRESULT PlanFinalizeActions(
1504 __in BURN_PLAN* pPlan
1505 )
1506{
1507 HRESULT hr = S_OK;
1508
1509 FinalizePatchActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions);
1510
1511 FinalizePatchActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions);
1512
1513 RemoveUnnecessaryActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions);
1514
1515 RemoveUnnecessaryActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions);
1516
1517 return hr;
1518}
1519
1520extern "C" HRESULT PlanCleanPackage(
1521 __in BURN_PLAN* pPlan,
1522 __in BURN_PACKAGE* pPackage
1523 )
1524{
1525 HRESULT hr = S_OK;
1526 BOOL fPlanCleanPackage = FALSE;
1527 BURN_CLEAN_ACTION* pCleanAction = NULL;
1528
1529 // The following is a complex set of logic that determines when a package should be cleaned from the cache.
1530 if (BOOTSTRAPPER_CACHE_TYPE_FORCE > pPackage->cacheType || BOOTSTRAPPER_ACTION_CACHE > pPlan->action)
1531 {
1532 // The following are all different reasons why the package should be cleaned from the cache.
1533 // The else-ifs are used to make the conditions easier to see (rather than have them combined
1534 // in one huge condition).
1535 if (BOOTSTRAPPER_CACHE_TYPE_KEEP > pPackage->cacheType) // easy, package is not supposed to stay cached.
1536 {
1537 fPlanCleanPackage = TRUE;
1538 }
1539 else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested ||
1540 BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed and
1541 BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) // actually being removed.
1542 {
1543 fPlanCleanPackage = TRUE;
1544 }
1545 else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested ||
1546 BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed but
1547 BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is do nothing and
1548 !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and
1549 BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed.
1550 {
1551 fPlanCleanPackage = TRUE;
1552 }
1553 else if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && // uninstalling and
1554 BOOTSTRAPPER_REQUEST_STATE_NONE == pPackage->requested && // requested do nothing (aka: default) and
1555 BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is still do nothing and
1556 !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and
1557 BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed.
1558 {
1559 fPlanCleanPackage = TRUE;
1560 }
1561 }
1562
1563 if (fPlanCleanPackage)
1564 {
1565 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgCleanActions), pPlan->cCleanActions + 1, sizeof(BURN_CLEAN_ACTION), 5);
1566 ExitOnFailure(hr, "Failed to grow plan's array of clean actions.");
1567
1568 pCleanAction = pPlan->rgCleanActions + pPlan->cCleanActions;
1569 ++pPlan->cCleanActions;
1570
1571 pCleanAction->pPackage = pPackage;
1572
1573 pPackage->fPlannedUncache = TRUE;
1574
1575 if (pPackage->fCanAffectRegistration)
1576 {
1577 pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
1578 }
1579 }
1580
1581LExit:
1582 return hr;
1583}
1584
1585extern "C" HRESULT PlanExecuteCacheSyncAndRollback(
1586 __in BURN_PLAN* pPlan,
1587 __in BURN_PACKAGE* pPackage,
1588 __in HANDLE hCacheEvent
1589 )
1590{
1591 HRESULT hr = S_OK;
1592 BURN_EXECUTE_ACTION* pAction = NULL;
1593
1594 hr = PlanAppendExecuteAction(pPlan, &pAction);
1595 ExitOnFailure(hr, "Failed to append wait action for caching.");
1596
1597 pAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT;
1598 pAction->syncpoint.hEvent = hCacheEvent;
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
1609LExit:
1610 return hr;
1611}
1612
1613extern "C" HRESULT PlanExecuteCheckpoint(
1614 __in BURN_PLAN* pPlan
1615 )
1616{
1617 HRESULT hr = S_OK;
1618 BURN_EXECUTE_ACTION* pAction = NULL;
1619 DWORD dwCheckpointId = GetNextCheckpointId(pPlan);
1620
1621 // execute checkpoint
1622 hr = PlanAppendExecuteAction(pPlan, &pAction);
1623 ExitOnFailure(hr, "Failed to append execute action.");
1624
1625 pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT;
1626 pAction->checkpoint.dwId = dwCheckpointId;
1627 pAction->checkpoint.pActiveRollbackBoundary = pPlan->pActiveRollbackBoundary;
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 pAction->checkpoint.pActiveRollbackBoundary = pPlan->pActiveRollbackBoundary;
1636
1637LExit:
1638 return hr;
1639}
1640
1641extern "C" HRESULT PlanInsertExecuteAction(
1642 __in DWORD dwIndex,
1643 __in BURN_PLAN* pPlan,
1644 __out BURN_EXECUTE_ACTION** ppExecuteAction
1645 )
1646{
1647 HRESULT hr = S_OK;
1648
1649 hr = MemInsertIntoArray((void**)&pPlan->rgExecuteActions, dwIndex, 1, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1650 ExitOnFailure(hr, "Failed to grow plan's array of execute actions.");
1651
1652 *ppExecuteAction = pPlan->rgExecuteActions + dwIndex;
1653 ++pPlan->cExecuteActions;
1654
1655LExit:
1656 return hr;
1657}
1658
1659extern "C" HRESULT PlanInsertRollbackAction(
1660 __in DWORD dwIndex,
1661 __in BURN_PLAN* pPlan,
1662 __out BURN_EXECUTE_ACTION** ppRollbackAction
1663 )
1664{
1665 HRESULT hr = S_OK;
1666
1667 hr = MemInsertIntoArray((void**)&pPlan->rgRollbackActions, dwIndex, 1, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1668 ExitOnFailure(hr, "Failed to grow plan's array of rollback actions.");
1669
1670 *ppRollbackAction = pPlan->rgRollbackActions + dwIndex;
1671 ++pPlan->cRollbackActions;
1672
1673LExit:
1674 return hr;
1675}
1676
1677extern "C" HRESULT PlanAppendExecuteAction(
1678 __in BURN_PLAN* pPlan,
1679 __out BURN_EXECUTE_ACTION** ppExecuteAction
1680 )
1681{
1682 HRESULT hr = S_OK;
1683
1684 hr = MemEnsureArraySize((void**)&pPlan->rgExecuteActions, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1685 ExitOnFailure(hr, "Failed to grow plan's array of execute actions.");
1686
1687 *ppExecuteAction = pPlan->rgExecuteActions + pPlan->cExecuteActions;
1688 ++pPlan->cExecuteActions;
1689
1690LExit:
1691 return hr;
1692}
1693
1694extern "C" HRESULT PlanAppendRollbackAction(
1695 __in BURN_PLAN* pPlan,
1696 __out BURN_EXECUTE_ACTION** ppRollbackAction
1697 )
1698{
1699 HRESULT hr = S_OK;
1700
1701 hr = MemEnsureArraySize((void**)&pPlan->rgRollbackActions, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1702 ExitOnFailure(hr, "Failed to grow plan's array of rollback actions.");
1703
1704 *ppRollbackAction = pPlan->rgRollbackActions + pPlan->cRollbackActions;
1705 ++pPlan->cRollbackActions;
1706
1707LExit:
1708 return hr;
1709}
1710
1711extern "C" HRESULT PlanRollbackBoundaryBegin(
1712 __in BURN_PLAN* pPlan,
1713 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
1714 )
1715{
1716 HRESULT hr = S_OK;
1717 BURN_EXECUTE_ACTION* pExecuteAction = NULL;
1718
1719 AssertSz(!pPlan->pActiveRollbackBoundary, "PlanRollbackBoundaryBegin called without completing previous RollbackBoundary");
1720 pPlan->pActiveRollbackBoundary = pRollbackBoundary;
1721
1722 // Add begin rollback boundary to execute plan.
1723 hr = PlanAppendExecuteAction(pPlan, &pExecuteAction);
1724 ExitOnFailure(hr, "Failed to append rollback boundary begin action.");
1725
1726 pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY;
1727 pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary;
1728
1729 // Add begin rollback boundary to rollback plan.
1730 hr = PlanAppendRollbackAction(pPlan, &pExecuteAction);
1731 ExitOnFailure(hr, "Failed to append rollback boundary begin action.");
1732
1733 pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY;
1734 pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary;
1735
1736 // Add begin MSI transaction to execute plan.
1737 if (pRollbackBoundary->fTransaction)
1738 {
1739 hr = PlanExecuteCheckpoint(pPlan);
1740 ExitOnFailure(hr, "Failed to append checkpoint before MSI transaction begin action.");
1741
1742 hr = PlanAppendExecuteAction(pPlan, &pExecuteAction);
1743 ExitOnFailure(hr, "Failed to append MSI transaction begin action.");
1744
1745 pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION;
1746 pExecuteAction->msiTransaction.pRollbackBoundary = pRollbackBoundary;
1747 }
1748
1749LExit:
1750 return hr;
1751}
1752
1753extern "C" HRESULT PlanRollbackBoundaryComplete(
1754 __in BURN_PLAN* pPlan
1755 )
1756{
1757 HRESULT hr = S_OK;
1758 BURN_EXECUTE_ACTION* pExecuteAction = NULL;
1759 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = pPlan->pActiveRollbackBoundary;
1760
1761 AssertSz(pRollbackBoundary, "PlanRollbackBoundaryComplete called without an active RollbackBoundary");
1762
1763 if (pRollbackBoundary && pRollbackBoundary->fTransaction)
1764 {
1765 // Add commit MSI transaction to execute plan.
1766 hr = PlanAppendExecuteAction(pPlan, &pExecuteAction);
1767 ExitOnFailure(hr, "Failed to append MSI transaction commit action.");
1768
1769 pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION;
1770 pExecuteAction->msiTransaction.pRollbackBoundary = pRollbackBoundary;
1771 }
1772
1773 pPlan->pActiveRollbackBoundary = NULL;
1774
1775 // Add checkpoints.
1776 hr = PlanExecuteCheckpoint(pPlan);
1777
1778LExit:
1779 return hr;
1780}
1781
1782/*******************************************************************
1783 PlanSetResumeCommand - Initializes resume command string
1784
1785*******************************************************************/
1786extern "C" HRESULT PlanSetResumeCommand(
1787 __in BURN_REGISTRATION* pRegistration,
1788 __in BOOTSTRAPPER_ACTION action,
1789 __in BOOTSTRAPPER_COMMAND* pCommand,
1790 __in BURN_LOGGING* pLog
1791 )
1792{
1793 HRESULT hr = S_OK;
1794
1795 // build the resume command-line.
1796 hr = CoreRecreateCommandLine(&pRegistration->sczResumeCommandLine, action, pCommand->display, pCommand->restart, pCommand->relationType, pCommand->fPassthrough, pRegistration->sczActiveParent, pRegistration->sczAncestors, pLog->sczPath, pCommand->wzCommandLine);
1797 ExitOnFailure(hr, "Failed to recreate resume command-line.");
1798
1799LExit:
1800 return hr;
1801}
1802
1803
1804// internal function definitions
1805
1806static void UninitializeRegistrationAction(
1807 __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction
1808 )
1809{
1810 ReleaseStr(pAction->sczDependentProviderKey);
1811 ReleaseStr(pAction->sczBundleId);
1812 memset(pAction, 0, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION));
1813}
1814
1815static void UninitializeCacheAction(
1816 __in BURN_CACHE_ACTION* pCacheAction
1817 )
1818{
1819 switch (pCacheAction->type)
1820 {
1821 case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT:
1822 ReleaseHandle(pCacheAction->syncpoint.hEvent);
1823 break;
1824
1825 case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE:
1826 ReleaseStr(pCacheAction->bundleLayout.sczExecutableName);
1827 ReleaseStr(pCacheAction->bundleLayout.sczUnverifiedPath);
1828 break;
1829 }
1830}
1831
1832static void ResetPlannedContainerState(
1833 __in BURN_CONTAINER* pContainer
1834 )
1835{
1836 pContainer->fPlanned = FALSE;
1837 pContainer->qwExtractSizeTotal = 0;
1838 pContainer->qwCommittedCacheProgress = 0;
1839 pContainer->qwCommittedExtractProgress = 0;
1840 pContainer->hrExtract = S_OK;
1841}
1842
1843static void ResetPlannedPayloadsState(
1844 __in BURN_PAYLOADS* pPayloads
1845 )
1846{
1847 for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
1848 {
1849 BURN_PAYLOAD* pPayload = pPayloads->rgPayloads + i;
1850
1851 pPayload->cRemainingInstances = 0;
1852 pPayload->state = BURN_PAYLOAD_STATE_NONE;
1853 ReleaseNullStr(pPayload->sczLocalFilePath);
1854 }
1855}
1856
1857static void ResetPlannedPayloadGroupState(
1858 __in BURN_PAYLOAD_GROUP* pPayloadGroup
1859 )
1860{
1861 for (DWORD i = 0; i < pPayloadGroup->cItems; ++i)
1862 {
1863 BURN_PAYLOAD_GROUP_ITEM* pItem = pPayloadGroup->rgItems + i;
1864
1865 pItem->fCached = FALSE;
1866 pItem->qwCommittedCacheProgress = 0;
1867 }
1868}
1869
1870static void ResetPlannedPackageState(
1871 __in BURN_PACKAGE* pPackage
1872 )
1873{
1874 // Reset package state that is a result of planning.
1875 pPackage->cacheType = pPackage->authoredCacheType;
1876 pPackage->defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1877 pPackage->requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1878 pPackage->fPlannedCache = FALSE;
1879 pPackage->fPlannedUncache = FALSE;
1880 pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
1881 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1882 pPackage->providerExecute = BURN_DEPENDENCY_ACTION_NONE;
1883 pPackage->providerRollback = BURN_DEPENDENCY_ACTION_NONE;
1884 pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE;
1885 pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE;
1886 pPackage->fDependencyManagerWasHere = FALSE;
1887 pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
1888 pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
1889
1890 ReleaseNullStr(pPackage->sczCacheFolder);
1891
1892 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
1893 {
1894 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
1895 {
1896 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
1897
1898 pFeature->expectedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
1899 pFeature->defaultRequested = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
1900 pFeature->requested = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
1901 pFeature->execute = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1902 pFeature->rollback = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1903 }
1904
1905 for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
1906 {
1907 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = &pPackage->Msi.rgSlipstreamMsps[i];
1908
1909 pSlipstreamMsp->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
1910 pSlipstreamMsp->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1911 }
1912 }
1913 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.rgTargetProducts)
1914 {
1915 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
1916 {
1917 BURN_MSPTARGETPRODUCT* pTargetProduct = &pPackage->Msp.rgTargetProducts[i];
1918
1919 pTargetProduct->defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1920 pTargetProduct->requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1921 pTargetProduct->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
1922 pTargetProduct->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1923 pTargetProduct->executeSkip = BURN_PATCH_SKIP_STATE_NONE;
1924 pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_NONE;
1925 }
1926 }
1927
1928 ResetPlannedPayloadGroupState(&pPackage->payloads);
1929}
1930
1931static void ResetPlannedRollbackBoundaryState(
1932 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
1933 )
1934{
1935 pRollbackBoundary->fActiveTransaction = FALSE;
1936 ReleaseNullStr(pRollbackBoundary->sczLogPath);
1937}
1938
1939static HRESULT GetActionDefaultRequestState(
1940 __in BOOTSTRAPPER_ACTION action,
1941 __in BOOL fPermanent,
1942 __in BOOTSTRAPPER_PACKAGE_STATE currentState,
1943 __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
1944 )
1945{
1946 HRESULT hr = S_OK;
1947
1948 switch (action)
1949 {
1950 case BOOTSTRAPPER_ACTION_CACHE:
1951 switch (currentState)
1952 {
1953 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
1954 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1955 break;
1956
1957 default:
1958 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE;
1959 break;
1960 }
1961 break;
1962
1963 case BOOTSTRAPPER_ACTION_INSTALL: __fallthrough;
1964 case BOOTSTRAPPER_ACTION_UPDATE_REPLACE: __fallthrough;
1965 case BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED:
1966 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1967 break;
1968
1969 case BOOTSTRAPPER_ACTION_REPAIR:
1970 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR;
1971 break;
1972
1973 case BOOTSTRAPPER_ACTION_UNINSTALL:
1974 *pRequestState = fPermanent ? BOOTSTRAPPER_REQUEST_STATE_NONE : BOOTSTRAPPER_REQUEST_STATE_ABSENT;
1975 break;
1976
1977 case BOOTSTRAPPER_ACTION_MODIFY:
1978 switch (currentState)
1979 {
1980 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
1981 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT;
1982 break;
1983
1984 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
1985 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1986 break;
1987
1988 default:
1989 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
1990 break;
1991 }
1992 break;
1993
1994 default:
1995 hr = E_INVALIDARG;
1996 ExitOnRootFailure(hr, "Invalid action state.");
1997 }
1998
1999LExit:
2000 return hr;
2001}
2002
2003static HRESULT AddRegistrationAction(
2004 __in BURN_PLAN* pPlan,
2005 __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type,
2006 __in_z LPCWSTR wzDependentProviderKey,
2007 __in_z LPCWSTR wzOwnerBundleId
2008 )
2009{
2010 HRESULT hr = S_OK;
2011 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;
2012 BURN_DEPENDENT_REGISTRATION_ACTION* pAction = NULL;
2013
2014 // Create forward registration action.
2015 hr = MemEnsureArraySize((void**)&pPlan->rgRegistrationActions, pPlan->cRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5);
2016 ExitOnFailure(hr, "Failed to grow plan's array of registration actions.");
2017
2018 pAction = pPlan->rgRegistrationActions + pPlan->cRegistrationActions;
2019 ++pPlan->cRegistrationActions;
2020
2021 pAction->type = type;
2022
2023 hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0);
2024 ExitOnFailure(hr, "Failed to copy owner bundle to registration action.");
2025
2026 hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0);
2027 ExitOnFailure(hr, "Failed to copy dependent provider key to registration action.");
2028
2029 // Create rollback registration action.
2030 hr = MemEnsureArraySize((void**)&pPlan->rgRollbackRegistrationActions, pPlan->cRollbackRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5);
2031 ExitOnFailure(hr, "Failed to grow plan's array of rollback registration actions.");
2032
2033 pAction = pPlan->rgRollbackRegistrationActions + pPlan->cRollbackRegistrationActions;
2034 ++pPlan->cRollbackRegistrationActions;
2035
2036 pAction->type = rollbackType;
2037
2038 hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0);
2039 ExitOnFailure(hr, "Failed to copy owner bundle to registration action.");
2040
2041 hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0);
2042 ExitOnFailure(hr, "Failed to copy dependent provider key to rollback registration action.");
2043
2044LExit:
2045 return hr;
2046}
2047
2048static HRESULT AddCachePackage(
2049 __in BURN_PLAN* pPlan,
2050 __in BURN_PACKAGE* pPackage,
2051 __out HANDLE* phSyncpointEvent
2052 )
2053{
2054 HRESULT hr = S_OK;
2055
2056 // If this is an MSI package with slipstream MSPs, ensure the MSPs are cached first.
2057 if (BURN_PACKAGE_TYPE_MSI == pPackage->type && 0 < pPackage->Msi.cSlipstreamMspPackages)
2058 {
2059 hr = AddCacheSlipstreamMsps(pPlan, pPackage);
2060 ExitOnFailure(hr, "Failed to plan slipstream patches for package.");
2061 }
2062
2063 hr = AddCachePackageHelper(pPlan, pPackage, phSyncpointEvent);
2064 ExitOnFailure(hr, "Failed to plan cache package.");
2065
2066LExit:
2067 return hr;
2068}
2069
2070static HRESULT AddCachePackageHelper(
2071 __in BURN_PLAN* pPlan,
2072 __in BURN_PACKAGE* pPackage,
2073 __out HANDLE* phSyncpointEvent
2074 )
2075{
2076 AssertSz(pPackage->sczCacheId && *pPackage->sczCacheId, "AddCachePackageHelper() expects the package to have a cache id.");
2077
2078 HRESULT hr = S_OK;
2079 BURN_CACHE_ACTION* pCacheAction = NULL;
2080 DWORD dwCheckpoint = 0;
2081
2082 BOOL fPlanned = AlreadyPlannedCachePackage(pPlan, pPackage->sczId, phSyncpointEvent);
2083 if (fPlanned)
2084 {
2085 ExitFunction();
2086 }
2087
2088 // Cache checkpoints happen before the package is cached because downloading packages'
2089 // payloads will not roll themselves back the way installation packages rollback on
2090 // failure automatically.
2091 dwCheckpoint = GetNextCheckpointId(pPlan);
2092
2093 hr = AppendCacheAction(pPlan, &pCacheAction);
2094 ExitOnFailure(hr, "Failed to append checkpoint before package start action.");
2095
2096 pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT;
2097 pCacheAction->checkpoint.dwId = dwCheckpoint;
2098
2099 // Only plan the cache rollback if the package is also going to be uninstalled;
2100 // otherwise, future operations like repair will not be able to locate the cached package.
2101 BOOL fPlanCacheRollback = (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->rollback);
2102
2103 if (fPlanCacheRollback)
2104 {
2105 hr = AppendRollbackCacheAction(pPlan, &pCacheAction);
2106 ExitOnFailure(hr, "Failed to append rollback cache action.");
2107
2108 pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT;
2109 pCacheAction->checkpoint.dwId = dwCheckpoint;
2110 }
2111
2112 hr = PlanLayoutPackage(pPlan, pPackage);
2113 ExitOnFailure(hr, "Failed to plan cache for package.");
2114
2115 // Create syncpoint action.
2116 hr = AppendCacheAction(pPlan, &pCacheAction);
2117 ExitOnFailure(hr, "Failed to append cache action.");
2118
2119 pCacheAction->type = BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT;
2120 pCacheAction->syncpoint.hEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
2121 ExitOnNullWithLastError(pCacheAction->syncpoint.hEvent, hr, "Failed to create syncpoint event.");
2122
2123 *phSyncpointEvent = pCacheAction->syncpoint.hEvent;
2124
2125 pPackage->fPlannedCache = TRUE;
2126 if (pPackage->fCanAffectRegistration)
2127 {
2128 pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
2129 }
2130
2131LExit:
2132 return hr;
2133}
2134
2135static HRESULT AddCacheSlipstreamMsps(
2136 __in BURN_PLAN* pPlan,
2137 __in BURN_PACKAGE* pPackage
2138 )
2139{
2140 HRESULT hr = S_OK;
2141 HANDLE hIgnored = NULL;
2142
2143 AssertSz(BURN_PACKAGE_TYPE_MSI == pPackage->type, "Only MSI packages can have slipstream patches.");
2144
2145 for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
2146 {
2147 BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[i].pMspPackage;
2148 AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches.");
2149
2150 hr = AddCachePackageHelper(pPlan, pMspPackage, &hIgnored);
2151 ExitOnFailure(hr, "Failed to plan slipstream MSP: %ls", pMspPackage->sczId);
2152 }
2153
2154LExit:
2155 return hr;
2156}
2157
2158static BOOL AlreadyPlannedCachePackage(
2159 __in BURN_PLAN* pPlan,
2160 __in_z LPCWSTR wzPackageId,
2161 __out HANDLE* phSyncpointEvent
2162 )
2163{
2164 BOOL fPlanned = FALSE;
2165
2166 for (DWORD iCacheAction = 0; iCacheAction < pPlan->cCacheActions; ++iCacheAction)
2167 {
2168 BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + iCacheAction;
2169
2170 if (BURN_CACHE_ACTION_TYPE_PACKAGE == pCacheAction->type)
2171 {
2172 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pCacheAction->package.pPackage->sczId, -1, wzPackageId, -1))
2173 {
2174 if (iCacheAction + 1 < pPlan->cCacheActions && BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT == pPlan->rgCacheActions[iCacheAction + 1].type)
2175 {
2176 *phSyncpointEvent = pPlan->rgCacheActions[iCacheAction + 1].syncpoint.hEvent;
2177 }
2178
2179 fPlanned = TRUE;
2180 break;
2181 }
2182 }
2183 }
2184
2185 return fPlanned;
2186}
2187
2188static DWORD GetNextCheckpointId(
2189 __in BURN_PLAN* pPlan
2190 )
2191{
2192 return ++pPlan->dwNextCheckpointId;
2193}
2194
2195static HRESULT AppendCacheAction(
2196 __in BURN_PLAN* pPlan,
2197 __out BURN_CACHE_ACTION** ppCacheAction
2198 )
2199{
2200 HRESULT hr = S_OK;
2201
2202 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgCacheActions), pPlan->cCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5);
2203 ExitOnFailure(hr, "Failed to grow plan's array of cache actions.");
2204
2205 *ppCacheAction = pPlan->rgCacheActions + pPlan->cCacheActions;
2206 ++pPlan->cCacheActions;
2207
2208LExit:
2209 return hr;
2210}
2211
2212static HRESULT AppendRollbackCacheAction(
2213 __in BURN_PLAN* pPlan,
2214 __out BURN_CACHE_ACTION** ppCacheAction
2215 )
2216{
2217 HRESULT hr = S_OK;
2218
2219 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgRollbackCacheActions), pPlan->cRollbackCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5);
2220 ExitOnFailure(hr, "Failed to grow plan's array of rollback cache actions.");
2221
2222 *ppCacheAction = pPlan->rgRollbackCacheActions + pPlan->cRollbackCacheActions;
2223 ++pPlan->cRollbackCacheActions;
2224
2225LExit:
2226 return hr;
2227}
2228
2229static HRESULT ProcessPayloadGroup(
2230 __in BURN_PLAN* pPlan,
2231 __in BURN_PAYLOAD_GROUP* pPayloadGroup
2232 )
2233{
2234 HRESULT hr = S_OK;
2235
2236 for (DWORD i = 0; i < pPayloadGroup->cItems; ++i)
2237 {
2238 BURN_PAYLOAD_GROUP_ITEM* pItem = pPayloadGroup->rgItems + i;
2239 BURN_PAYLOAD* pPayload = pItem->pPayload;
2240
2241 pPayload->cRemainingInstances += 1;
2242
2243 if (pPayload->pContainer && !pPayload->pContainer->fPlanned)
2244 {
2245 hr = PlanLayoutContainer(pPlan, pPayload->pContainer);
2246 ExitOnFailure(hr, "Failed to plan container: %ls", pPayload->pContainer->sczId);
2247 }
2248
2249 if (!pPlan->sczLayoutDirectory || !pPayload->pContainer)
2250 {
2251 // Acquire + Verify + Finalize
2252 pPlan->qwCacheSizeTotal += 3 * pPayload->qwFileSize;
2253
2254 if (!pPlan->sczLayoutDirectory)
2255 {
2256 // Staging
2257 pPlan->qwCacheSizeTotal += pPayload->qwFileSize;
2258 }
2259 }
2260
2261 if (!pPlan->sczLayoutDirectory && pPayload->pContainer && 1 == pPayload->cRemainingInstances)
2262 {
2263 // Extract
2264 pPlan->qwCacheSizeTotal += pPayload->qwFileSize;
2265 pPayload->pContainer->qwExtractSizeTotal += pPayload->qwFileSize;
2266 }
2267
2268 if (!pPayload->sczUnverifiedPath)
2269 {
2270 hr = CacheCalculatePayloadWorkingPath(pPlan->wzBundleId, pPayload, &pPayload->sczUnverifiedPath);
2271 ExitOnFailure(hr, "Failed to calculate unverified path for payload.");
2272 }
2273 }
2274
2275LExit:
2276 return hr;
2277}
2278
2279static void RemoveUnnecessaryActions(
2280 __in BOOL fExecute,
2281 __in BURN_EXECUTE_ACTION* rgActions,
2282 __in DWORD cActions
2283 )
2284{
2285 LPCSTR szExecuteOrRollback = fExecute ? "execute" : "rollback";
2286
2287 for (DWORD i = 0; i < cActions; ++i)
2288 {
2289 BURN_EXECUTE_ACTION* pAction = rgActions + i;
2290
2291 if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type && pAction->mspTarget.pChainedTargetPackage)
2292 {
2293 BURN_MSPTARGETPRODUCT* pFirstTargetProduct = pAction->mspTarget.rgOrderedPatches->pTargetProduct;
2294 BURN_PATCH_SKIP_STATE skipState = fExecute ? pFirstTargetProduct->executeSkip : pFirstTargetProduct->rollbackSkip;
2295 BOOTSTRAPPER_ACTION_STATE chainedTargetPackageAction = fExecute ? pAction->mspTarget.pChainedTargetPackage->execute : pAction->mspTarget.pChainedTargetPackage->rollback;
2296
2297 switch (skipState)
2298 {
2299 case BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL:
2300 pAction->fDeleted = TRUE;
2301 LogId(REPORT_STANDARD, MSG_PLAN_SKIP_PATCH_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback);
2302 break;
2303 case BURN_PATCH_SKIP_STATE_SLIPSTREAM:
2304 pAction->fDeleted = TRUE;
2305 LogId(REPORT_STANDARD, MSG_PLAN_SKIP_SLIPSTREAM_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback);
2306 break;
2307 }
2308 }
2309 }
2310}
2311
2312static void FinalizePatchActions(
2313 __in BOOL fExecute,
2314 __in BURN_EXECUTE_ACTION* rgActions,
2315 __in DWORD cActions
2316 )
2317{
2318 for (DWORD i = 0; i < cActions; ++i)
2319 {
2320 BURN_EXECUTE_ACTION* pAction = rgActions + i;
2321
2322 if (BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE == pAction->type)
2323 {
2324 BURN_PACKAGE* pPackage = pAction->msiPackage.pPackage;
2325 AssertSz(BOOTSTRAPPER_ACTION_STATE_NONE < pAction->msiPackage.action, "Planned execute MSI action to do nothing");
2326
2327 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msiPackage.action)
2328 {
2329 // If we are uninstalling the MSI, we must skip all the patches.
2330 for (DWORD j = 0; j < pPackage->Msi.cChainedPatches; ++j)
2331 {
2332 BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + j;
2333 BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex;
2334
2335 if (fExecute)
2336 {
2337 pTargetProduct->execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
2338 pTargetProduct->executeSkip = BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL;
2339 }
2340 else
2341 {
2342 pTargetProduct->rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
2343 pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL;
2344 }
2345 }
2346 }
2347 else
2348 {
2349 // If the slipstream target is being installed or upgraded (not uninstalled or repaired) then we will slipstream so skip
2350 // the patch's standalone action. Also, if the slipstream target is being repaired and the patch is being
2351 // repaired, skip this operation since it will be redundant.
2352 //
2353 // The primary goal here is to ensure that a slipstream patch that is yet not installed is installed even if the MSI
2354 // is already on the machine. The slipstream must be installed standalone if the MSI is being repaired.
2355 for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j)
2356 {
2357 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + j;
2358 BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pSlipstreamMsp->dwMsiChainedPatchIndex;
2359 BURN_MSPTARGETPRODUCT* pTargetProduct = pSlipstreamMsp->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex;
2360 BOOTSTRAPPER_ACTION_STATE action = fExecute ? pTargetProduct->execute : pTargetProduct->rollback;
2361 BOOL fSlipstream = BOOTSTRAPPER_ACTION_STATE_UNINSTALL < action &&
2362 (BOOTSTRAPPER_ACTION_STATE_REPAIR != pAction->msiPackage.action || BOOTSTRAPPER_ACTION_STATE_REPAIR == action);
2363
2364 if (fSlipstream)
2365 {
2366 if (fExecute)
2367 {
2368 pSlipstreamMsp->execute = action;
2369 pTargetProduct->executeSkip = BURN_PATCH_SKIP_STATE_SLIPSTREAM;
2370 }
2371 else
2372 {
2373 pSlipstreamMsp->rollback = action;
2374 pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_SLIPSTREAM;
2375 }
2376 }
2377 }
2378 }
2379 }
2380 }
2381}
2382
2383static void CalculateExpectedRegistrationStates(
2384 __in BURN_PACKAGE* rgPackages,
2385 __in DWORD cPackages
2386 )
2387{
2388 for (DWORD i = 0; i < cPackages; ++i)
2389 {
2390 BURN_PACKAGE* pPackage = rgPackages + i;
2391
2392 // MspPackages can have actions throughout the plan, so the plan needed to be finalized before anything could be calculated.
2393 if (BURN_PACKAGE_TYPE_MSP == pPackage->type && !pPackage->fDependencyManagerWasHere)
2394 {
2395 pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
2396 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
2397
2398 for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j)
2399 {
2400 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j;
2401
2402 // The highest aggregate action state found will be used.
2403 if (pPackage->execute < pTargetProduct->execute)
2404 {
2405 pPackage->execute = pTargetProduct->execute;
2406 }
2407
2408 if (pPackage->rollback < pTargetProduct->rollback)
2409 {
2410 pPackage->rollback = pTargetProduct->rollback;
2411 }
2412 }
2413 }
2414
2415 if (pPackage->fCanAffectRegistration)
2416 {
2417 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->execute)
2418 {
2419 pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
2420 }
2421 else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute)
2422 {
2423 pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
2424 }
2425
2426 if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute)
2427 {
2428 if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedCacheRegistrationState)
2429 {
2430 pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
2431 }
2432 if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedInstallRegistrationState)
2433 {
2434 pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
2435 }
2436 }
2437 else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pPackage->dependencyExecute)
2438 {
2439 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedCacheRegistrationState)
2440 {
2441 pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
2442 }
2443 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedInstallRegistrationState)
2444 {
2445 pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
2446 }
2447 }
2448 }
2449 }
2450}
2451
2452static HRESULT PlanDependencyActions(
2453 __in BOOL fBundlePerMachine,
2454 __in BURN_PLAN* pPlan,
2455 __in BURN_PACKAGE* pPackage
2456 )
2457{
2458 HRESULT hr = S_OK;
2459
2460 hr = DependencyPlanPackageBegin(fBundlePerMachine, pPackage, pPlan);
2461 ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId);
2462
2463 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
2464 ExitOnFailure(hr, "Failed to plan package dependency actions.");
2465
2466 hr = DependencyPlanPackageComplete(pPackage, pPlan);
2467 ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId);
2468
2469LExit:
2470 return hr;
2471}
2472
2473static HRESULT CalculateExecuteActions(
2474 __in BURN_PACKAGE* pPackage,
2475 __in_opt BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary
2476 )
2477{
2478 HRESULT hr = S_OK;
2479 BOOL fInsideMsiTransaction = pActiveRollbackBoundary && pActiveRollbackBoundary->fTransaction;
2480
2481 // Calculate execute actions.
2482 switch (pPackage->type)
2483 {
2484 case BURN_PACKAGE_TYPE_EXE:
2485 hr = ExeEnginePlanCalculatePackage(pPackage);
2486 break;
2487
2488 case BURN_PACKAGE_TYPE_MSI:
2489 hr = MsiEnginePlanCalculatePackage(pPackage, fInsideMsiTransaction);
2490 break;
2491
2492 case BURN_PACKAGE_TYPE_MSP:
2493 hr = MspEnginePlanCalculatePackage(pPackage, fInsideMsiTransaction);
2494 break;
2495
2496 case BURN_PACKAGE_TYPE_MSU:
2497 hr = MsuEnginePlanCalculatePackage(pPackage);
2498 break;
2499
2500 default:
2501 hr = E_UNEXPECTED;
2502 ExitOnFailure(hr, "Invalid package type.");
2503 }
2504
2505LExit:
2506 return hr;
2507}
2508
2509static BOOL NeedsCache(
2510 __in BURN_PACKAGE* pPackage,
2511 __in BOOL fExecute
2512 )
2513{
2514 BOOTSTRAPPER_ACTION_STATE action = fExecute ? pPackage->execute : pPackage->rollback;
2515 if (BURN_PACKAGE_TYPE_EXE == pPackage->type) // Exe packages require the package for all operations (even uninstall).
2516 {
2517 return BOOTSTRAPPER_ACTION_STATE_NONE != action;
2518 }
2519 else // The other package types can uninstall without the original package.
2520 {
2521 return BOOTSTRAPPER_ACTION_STATE_UNINSTALL < action;
2522 }
2523}
2524
2525static BOOL ForceCache(
2526 __in BURN_PLAN* pPlan,
2527 __in BURN_PACKAGE* pPackage
2528 )
2529{
2530 // All packages that have cacheType set to force should be cached if the bundle is going to be present.
2531 return BOOTSTRAPPER_CACHE_TYPE_FORCE == pPackage->cacheType && BOOTSTRAPPER_ACTION_UNINSTALL < pPlan->action;
2532}
2533
2534static void CacheActionLog(
2535 __in DWORD iAction,
2536 __in BURN_CACHE_ACTION* pAction,
2537 __in BOOL fRollback
2538 )
2539{
2540 LPCWSTR wzBase = fRollback ? L" Rollback cache" : L" Cache";
2541 switch (pAction->type)
2542 {
2543 case BURN_CACHE_ACTION_TYPE_CHECKPOINT:
2544 LogStringLine(PlanDumpLevel, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId);
2545 break;
2546
2547 case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE:
2548 LogStringLine(PlanDumpLevel, "%ls action[%u]: LAYOUT_BUNDLE working path: %ls, exe name: %ls", wzBase, iAction, pAction->bundleLayout.sczUnverifiedPath, pAction->bundleLayout.sczExecutableName);
2549 break;
2550
2551 case BURN_CACHE_ACTION_TYPE_CONTAINER:
2552 LogStringLine(PlanDumpLevel, "%ls action[%u]: CONTAINER container id: %ls, working path: %ls", wzBase, iAction, pAction->container.pContainer->sczId, pAction->container.pContainer->sczUnverifiedPath);
2553 break;
2554
2555 case BURN_CACHE_ACTION_TYPE_PACKAGE:
2556 LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE id: %ls", wzBase, iAction, pAction->package.pPackage->sczId);
2557 break;
2558
2559 case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE:
2560 LogStringLine(PlanDumpLevel, "%ls action[%u]: ROLLBACK_PACKAGE id: %ls", wzBase, iAction, pAction->rollbackPackage.pPackage->sczId);
2561 break;
2562
2563 case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT:
2564 LogStringLine(PlanDumpLevel, "%ls action[%u]: SIGNAL_SYNCPOINT event handle: 0x%p", wzBase, iAction, pAction->syncpoint.hEvent);
2565 break;
2566
2567 default:
2568 AssertSz(FALSE, "Unknown cache action type.");
2569 break;
2570 }
2571}
2572
2573static void ExecuteActionLog(
2574 __in DWORD iAction,
2575 __in BURN_EXECUTE_ACTION* pAction,
2576 __in BOOL fRollback
2577 )
2578{
2579 LPCWSTR wzBase = fRollback ? L" Rollback" : L" Execute";
2580 switch (pAction->type)
2581 {
2582 case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT:
2583 LogStringLine(PlanDumpLevel, "%ls action[%u]: CHECKPOINT id: %u, msi transaction id: %ls", wzBase, iAction, pAction->checkpoint.dwId, pAction->checkpoint.pActiveRollbackBoundary && pAction->checkpoint.pActiveRollbackBoundary->fTransaction ? pAction->checkpoint.pActiveRollbackBoundary->sczId : L"(none)");
2584 break;
2585
2586 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER:
2587 LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE_PROVIDER package id: %ls, action: %hs", wzBase, iAction, pAction->packageProvider.pPackage->sczId, LoggingDependencyActionToString(pAction->packageProvider.action));
2588 break;
2589
2590 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
2591 LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE_DEPENDENCY package id: %ls, bundle provider key: %ls, action: %hs", wzBase, iAction, pAction->packageDependency.pPackage->sczId, pAction->packageDependency.sczBundleProviderKey, LoggingDependencyActionToString(pAction->packageDependency.action));
2592 break;
2593
2594 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
2595 LogStringLine(PlanDumpLevel, "%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);
2596 break;
2597
2598 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
2599 LogStringLine(PlanDumpLevel, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), LoggingBurnMsiPropertyToString(pAction->msiPackage.actionMsiProperty), pAction->msiPackage.uiLevel, pAction->msiPackage.fDisableExternalUiHandler ? L"yes" : L"no", pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes);
2600 for (DWORD j = 0; j < pAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++j)
2601 {
2602 const BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pAction->msiPackage.pPackage->Msi.rgSlipstreamMsps + j;
2603 LogStringLine(PlanDumpLevel, " Patch[%u]: msp package id: %ls, action: %hs", j, pSlipstreamMsp->pMspPackage->sczId, LoggingActionStateToString(fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute));
2604 }
2605 break;
2606
2607 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
2608 LogStringLine(PlanDumpLevel, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %ls, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, pAction->mspTarget.fPerMachineTarget ? L"yes" : L"no", LoggingBurnMsiPropertyToString(pAction->mspTarget.actionMsiProperty), pAction->mspTarget.uiLevel, pAction->mspTarget.fDisableExternalUiHandler ? L"yes" : L"no", pAction->mspTarget.sczLogPath);
2609 for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j)
2610 {
2611 LogStringLine(PlanDumpLevel, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->mspTarget.rgOrderedPatches[j].pTargetProduct->dwOrder, pAction->mspTarget.rgOrderedPatches[j].pPackage->sczId);
2612 }
2613 break;
2614
2615 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
2616 LogStringLine(PlanDumpLevel, "%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);
2617 break;
2618
2619 case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY:
2620 LogStringLine(PlanDumpLevel, "%ls action[%u]: ROLLBACK_BOUNDARY id: %ls, vital: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no");
2621 break;
2622
2623 case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT:
2624 LogStringLine(PlanDumpLevel, "%ls action[%u]: WAIT_SYNCPOINT event handle: 0x%p", wzBase, iAction, pAction->syncpoint.hEvent);
2625 break;
2626
2627 case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE:
2628 LogStringLine(PlanDumpLevel, "%ls action[%u]: UNCACHE_PACKAGE id: %ls", wzBase, iAction, pAction->uncachePackage.pPackage->sczId);
2629 break;
2630
2631 case BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION:
2632 LogStringLine(PlanDumpLevel, "%ls action[%u]: BEGIN_MSI_TRANSACTION id: %ls", wzBase, iAction, pAction->msiTransaction.pRollbackBoundary->sczId);
2633 break;
2634
2635 case BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION:
2636 LogStringLine(PlanDumpLevel, "%ls action[%u]: COMMIT_MSI_TRANSACTION id: %ls", wzBase, iAction, pAction->msiTransaction.pRollbackBoundary->sczId);
2637 break;
2638
2639 default:
2640 AssertSz(FALSE, "Unknown execute action type.");
2641 break;
2642 }
2643
2644 if (pAction->fDeleted)
2645 {
2646 LogStringLine(PlanDumpLevel, " (deleted action)");
2647 }
2648}
2649
2650extern "C" void PlanDump(
2651 __in BURN_PLAN* pPlan
2652 )
2653{
2654 LogStringLine(PlanDumpLevel, "--- Begin plan dump ---");
2655
2656 LogStringLine(PlanDumpLevel, "Plan action: %hs", LoggingBurnActionToString(pPlan->action));
2657 LogStringLine(PlanDumpLevel, " per-machine: %hs", LoggingTrueFalseToString(pPlan->fPerMachine));
2658 LogStringLine(PlanDumpLevel, " disable-rollback: %hs", LoggingTrueFalseToString(pPlan->fDisableRollback));
2659 LogStringLine(PlanDumpLevel, " estimated size: %llu", pPlan->qwEstimatedSize);
2660 if (pPlan->sczLayoutDirectory)
2661 {
2662 LogStringLine(PlanDumpLevel, " layout directory: %ls", pPlan->sczLayoutDirectory);
2663 }
2664
2665 LogStringLine(PlanDumpLevel, "Plan cache size: %llu", pPlan->qwCacheSizeTotal);
2666 for (DWORD i = 0; i < pPlan->cCacheActions; ++i)
2667 {
2668 CacheActionLog(i, pPlan->rgCacheActions + i, FALSE);
2669 }
2670
2671 for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i)
2672 {
2673 CacheActionLog(i, pPlan->rgRollbackCacheActions + i, TRUE);
2674 }
2675
2676 LogStringLine(PlanDumpLevel, "Plan execute package count: %u", pPlan->cExecutePackagesTotal);
2677 LogStringLine(PlanDumpLevel, " overall progress ticks: %u", pPlan->cOverallProgressTicksTotal);
2678 for (DWORD i = 0; i < pPlan->cExecuteActions; ++i)
2679 {
2680 ExecuteActionLog(i, pPlan->rgExecuteActions + i, FALSE);
2681 }
2682
2683 for (DWORD i = 0; i < pPlan->cRollbackActions; ++i)
2684 {
2685 ExecuteActionLog(i, pPlan->rgRollbackActions + i, TRUE);
2686 }
2687
2688 for (DWORD i = 0; i < pPlan->cCleanActions; ++i)
2689 {
2690 LogStringLine(PlanDumpLevel, " Clean action[%u]: CLEAN_PACKAGE package id: %ls", i, pPlan->rgCleanActions[i].pPackage->sczId);
2691 }
2692
2693 for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i)
2694 {
2695 LogStringLine(PlanDumpLevel, " Dependency action[%u]: PLANNED_PROVIDER key: %ls, name: %ls", i, pPlan->rgPlannedProviders[i].sczKey, pPlan->rgPlannedProviders[i].sczName);
2696 }
2697
2698 LogStringLine(PlanDumpLevel, "--- End plan dump ---");
2699}