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