aboutsummaryrefslogtreecommitdiff
path: root/src/burn/engine/core.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/burn/engine/core.cpp')
-rw-r--r--src/burn/engine/core.cpp1856
1 files changed, 1856 insertions, 0 deletions
diff --git a/src/burn/engine/core.cpp b/src/burn/engine/core.cpp
new file mode 100644
index 00000000..535043af
--- /dev/null
+++ b/src/burn/engine/core.cpp
@@ -0,0 +1,1856 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// structs
7
8struct BURN_CACHE_THREAD_CONTEXT
9{
10 BURN_ENGINE_STATE* pEngineState;
11 DWORD* pcOverallProgressTicks;
12 BOOL* pfRollback;
13};
14
15
16// internal function declarations
17
18static HRESULT ParseCommandLine(
19 __in int argc,
20 __in LPWSTR* argv,
21 __in BOOTSTRAPPER_COMMAND* pCommand,
22 __in BURN_PIPE_CONNECTION* pCompanionConnection,
23 __in BURN_PIPE_CONNECTION* pEmbeddedConnection,
24 __in BURN_VARIABLES* pVariables,
25 __out BURN_MODE* pMode,
26 __out BURN_AU_PAUSE_ACTION* pAutomaticUpdates,
27 __out BOOL* pfDisableSystemRestore,
28 __out_z LPWSTR* psczSourceProcessPath,
29 __out_z LPWSTR* psczOriginalSource,
30 __out BOOL* pfDisableUnelevate,
31 __out DWORD *pdwLoggingAttributes,
32 __out_z LPWSTR* psczLogFile,
33 __out_z LPWSTR* psczActiveParent,
34 __out_z LPWSTR* psczIgnoreDependencies,
35 __out_z LPWSTR* psczAncestors,
36 __out_z LPWSTR* psczSanitizedCommandLine
37 );
38static HRESULT ParsePipeConnection(
39 __in_ecount(3) LPWSTR* rgArgs,
40 __in BURN_PIPE_CONNECTION* pConnection
41 );
42static HRESULT DetectPackage(
43 __in BURN_ENGINE_STATE* pEngineState,
44 __in BURN_PACKAGE* pPackage
45 );
46static HRESULT DetectPackagePayloadsCached(
47 __in BURN_PACKAGE* pPackage
48 );
49static DWORD WINAPI CacheThreadProc(
50 __in LPVOID lpThreadParameter
51 );
52static HRESULT WaitForCacheThread(
53 __in HANDLE hCacheThread
54 );
55static void LogPackages(
56 __in_opt const BURN_PACKAGE* pUpgradeBundlePackage,
57 __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage,
58 __in const BURN_PACKAGES* pPackages,
59 __in const BURN_RELATED_BUNDLES* pRelatedBundles,
60 __in const BOOTSTRAPPER_ACTION action
61 );
62static void LogRelatedBundles(
63 __in const BURN_RELATED_BUNDLES* pRelatedBundles,
64 __in BOOL fReverse
65 );
66
67
68// function definitions
69
70extern "C" HRESULT CoreInitialize(
71 __in BURN_ENGINE_STATE* pEngineState
72 )
73{
74 HRESULT hr = S_OK;
75 LPWSTR sczSanitizedCommandLine = NULL;
76 LPWSTR sczStreamName = NULL;
77 BYTE* pbBuffer = NULL;
78 SIZE_T cbBuffer = 0;
79 BURN_CONTAINER_CONTEXT containerContext = { };
80 BOOL fElevated = FALSE;
81 LPWSTR sczSourceProcessPath = NULL;
82 LPWSTR sczSourceProcessFolder = NULL;
83 LPWSTR sczOriginalSource = NULL;
84
85 // Initialize variables.
86 hr = VariableInitialize(&pEngineState->variables);
87 ExitOnFailure(hr, "Failed to initialize variables.");
88
89 // Open attached UX container.
90 hr = ContainerOpenUX(&pEngineState->section, &containerContext);
91 ExitOnFailure(hr, "Failed to open attached UX container.");
92
93 // Load manifest.
94 hr = ContainerNextStream(&containerContext, &sczStreamName);
95 ExitOnFailure(hr, "Failed to open manifest stream.");
96
97 hr = ContainerStreamToBuffer(&containerContext, &pbBuffer, &cbBuffer);
98 ExitOnFailure(hr, "Failed to get manifest stream from container.");
99
100 hr = ManifestLoadXmlFromBuffer(pbBuffer, cbBuffer, pEngineState);
101 ExitOnFailure(hr, "Failed to load manifest.");
102
103 hr = ContainersInitialize(&pEngineState->containers, &pEngineState->section);
104 ExitOnFailure(hr, "Failed to initialize containers.");
105
106 // Parse command line.
107 hr = ParseCommandLine(pEngineState->argc, pEngineState->argv, &pEngineState->command, &pEngineState->companionConnection, &pEngineState->embeddedConnection, &pEngineState->variables, &pEngineState->mode, &pEngineState->automaticUpdates, &pEngineState->fDisableSystemRestore, &sczSourceProcessPath, &sczOriginalSource, &pEngineState->fDisableUnelevate, &pEngineState->log.dwAttributes, &pEngineState->log.sczPath, &pEngineState->registration.sczActiveParent, &pEngineState->sczIgnoreDependencies, &pEngineState->registration.sczAncestors, &sczSanitizedCommandLine);
108 ExitOnFailure(hr, "Failed to parse command line.");
109
110 LogId(REPORT_STANDARD, MSG_BURN_COMMAND_LINE, sczSanitizedCommandLine ? sczSanitizedCommandLine : L"");
111
112 hr = CoreInitializeConstants(pEngineState);
113 ExitOnFailure(hr, "Failed to initialize contants.");
114
115 // Retain whether bundle was initially run elevated.
116 ProcElevated(::GetCurrentProcess(), &fElevated);
117
118 hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, fElevated, TRUE);
119 ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED);
120
121 hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_UILEVEL, pEngineState->command.display, TRUE);
122 ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_UILEVEL);
123
124 if (sczSourceProcessPath)
125 {
126 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_PATH, sczSourceProcessPath, TRUE, FALSE);
127 ExitOnFailure(hr, "Failed to set source process path variable.");
128
129 hr = PathGetDirectory(sczSourceProcessPath, &sczSourceProcessFolder);
130 ExitOnFailure(hr, "Failed to get source process folder from path.");
131
132 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, sczSourceProcessFolder, TRUE, FALSE);
133 ExitOnFailure(hr, "Failed to set source process folder variable.");
134 }
135
136 // Set BURN_BUNDLE_ORIGINAL_SOURCE, if it was passed in on the command line.
137 // Needs to be done after ManifestLoadXmlFromBuffer.
138 if (sczOriginalSource)
139 {
140 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, sczOriginalSource, FALSE, FALSE);
141 ExitOnFailure(hr, "Failed to set original source variable.");
142 }
143
144 if (BURN_MODE_UNTRUSTED == pEngineState->mode || BURN_MODE_NORMAL == pEngineState->mode || BURN_MODE_EMBEDDED == pEngineState->mode)
145 {
146 hr = CacheInitialize(&pEngineState->registration, &pEngineState->variables, sczSourceProcessPath);
147 ExitOnFailure(hr, "Failed to initialize internal cache functionality.");
148 }
149
150 // If we're not elevated then we'll be loading the bootstrapper application, so extract
151 // the payloads from the BA container.
152 if (BURN_MODE_NORMAL == pEngineState->mode || BURN_MODE_EMBEDDED == pEngineState->mode)
153 {
154 // Extract all UX payloads to working folder.
155 hr = UserExperienceEnsureWorkingFolder(pEngineState->registration.sczId, &pEngineState->userExperience.sczTempDirectory);
156 ExitOnFailure(hr, "Failed to get unique temporary folder for bootstrapper application.");
157
158 hr = PayloadExtractUXContainer(&pEngineState->userExperience.payloads, &containerContext, pEngineState->userExperience.sczTempDirectory);
159 ExitOnFailure(hr, "Failed to extract bootstrapper application payloads.");
160
161 hr = PathConcat(pEngineState->userExperience.sczTempDirectory, L"BootstrapperApplicationData.xml", &pEngineState->command.wzBootstrapperApplicationDataPath);
162 ExitOnFailure(hr, "Failed to get BootstrapperApplicationDataPath.");
163
164 hr = StrAllocString(&pEngineState->command.wzBootstrapperWorkingFolder, pEngineState->userExperience.sczTempDirectory, 0);
165 ExitOnFailure(hr, "Failed to copy sczBootstrapperWorkingFolder.");
166 }
167
168LExit:
169 ReleaseStr(sczOriginalSource);
170 ReleaseStr(sczSourceProcessFolder);
171 ReleaseStr(sczSourceProcessPath);
172 ContainerClose(&containerContext);
173 ReleaseStr(sczStreamName);
174 ReleaseStr(sczSanitizedCommandLine);
175 ReleaseMem(pbBuffer);
176
177 return hr;
178}
179
180extern "C" HRESULT CoreInitializeConstants(
181 __in BURN_ENGINE_STATE* pEngineState
182 )
183{
184 HRESULT hr = S_OK;
185 BURN_REGISTRATION* pRegistration = &pEngineState->registration;
186
187 hr = DependencyInitialize(pRegistration, pEngineState->sczIgnoreDependencies);
188 ExitOnFailure(hr, "Failed to initialize dependency data.");
189
190 // Support passing Ancestors to embedded burn bundles.
191 if (pRegistration->sczAncestors && *pRegistration->sczAncestors)
192 {
193 hr = StrAllocFormatted(&pRegistration->sczBundlePackageAncestors, L"%ls;%ls", pRegistration->sczAncestors, pRegistration->sczId);
194 ExitOnFailure(hr, "Failed to copy ancestors and self to bundle package ancestors.");
195 }
196 else
197 {
198 hr = StrAllocString(&pRegistration->sczBundlePackageAncestors, pRegistration->sczId, 0);
199 ExitOnFailure(hr, "Failed to copy self to bundle package ancestors.");
200 }
201
202 for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i)
203 {
204 BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i;
205
206 if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) // TODO: Don't assume exePackages with burn protocol are bundles.
207 {
208 // Pass along any ancestors and ourself to prevent infinite loops.
209 pPackage->Exe.wzAncestors = pRegistration->sczBundlePackageAncestors;
210 }
211 }
212
213LExit:
214 return hr;
215}
216
217extern "C" HRESULT CoreSerializeEngineState(
218 __in BURN_ENGINE_STATE* pEngineState,
219 __inout BYTE** ppbBuffer,
220 __inout SIZE_T* piBuffer
221 )
222{
223 HRESULT hr = S_OK;
224
225 hr = VariableSerialize(&pEngineState->variables, TRUE, ppbBuffer, piBuffer);
226 ExitOnFailure(hr, "Failed to serialize variables.");
227
228LExit:
229 return hr;
230}
231
232extern "C" HRESULT CoreQueryRegistration(
233 __in BURN_ENGINE_STATE* pEngineState
234 )
235{
236 HRESULT hr = S_OK;
237 BYTE* pbBuffer = NULL;
238 SIZE_T cbBuffer = 0;
239 SIZE_T iBuffer = 0;
240
241 // Detect if bundle is already installed.
242 hr = RegistrationDetectInstalled(&pEngineState->registration);
243 ExitOnFailure(hr, "Failed to detect bundle install state.");
244
245 // detect resume type
246 hr = RegistrationDetectResumeType(&pEngineState->registration, &pEngineState->command.resumeType);
247 ExitOnFailure(hr, "Failed to detect resume type.");
248
249 // If we have a resume mode that suggests the bundle might already be present, try to load any
250 // previously stored state.
251 if (BOOTSTRAPPER_RESUME_TYPE_INVALID < pEngineState->command.resumeType)
252 {
253 // load resume state
254 hr = RegistrationLoadState(&pEngineState->registration, &pbBuffer, &cbBuffer);
255 if (SUCCEEDED(hr))
256 {
257 hr = VariableDeserialize(&pEngineState->variables, TRUE, pbBuffer, cbBuffer, &iBuffer);
258 }
259
260 // Log any failures and continue.
261 if (FAILED(hr))
262 {
263 LogId(REPORT_STANDARD, MSG_CANNOT_LOAD_STATE_FILE, hr, pEngineState->registration.sczStateFile);
264 hr = S_OK;
265 }
266 }
267
268LExit:
269 ReleaseBuffer(pbBuffer);
270
271 return hr;
272}
273
274extern "C" HRESULT CoreDetect(
275 __in BURN_ENGINE_STATE* pEngineState,
276 __in_opt HWND hwndParent
277 )
278{
279 HRESULT hr = S_OK;
280 BOOL fDetectBegan = FALSE;
281 BURN_PACKAGE* pPackage = NULL;
282 HRESULT hrFirstPackageFailure = S_OK;
283
284 LogId(REPORT_STANDARD, MSG_DETECT_BEGIN, pEngineState->packages.cPackages);
285
286 // Always reset the detect state which means the plan should be reset too.
287 pEngineState->fDetected = FALSE;
288 pEngineState->fPlanned = FALSE;
289 DetectReset(&pEngineState->registration, &pEngineState->packages);
290 PlanReset(&pEngineState->plan, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads);
291
292 // Detect if bundle installed state has changed since start up. This
293 // only happens if Apply() changed the state of bundle (installed or
294 // uninstalled). In that case, Detect() can be used here to reset
295 // the installed state.
296 hr = RegistrationDetectInstalled(&pEngineState->registration);
297 ExitOnFailure(hr, "Failed to detect bundle install state.");
298
299 if (pEngineState->registration.fInstalled)
300 {
301 hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_INSTALLED, 1, TRUE);
302 ExitOnFailure(hr, "Failed to set the bundle installed built-in variable.");
303 }
304 else
305 {
306 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_INSTALLED, NULL, TRUE, FALSE);
307 ExitOnFailure(hr, "Failed to unset the bundle installed built-in variable.");
308 }
309
310 fDetectBegan = TRUE;
311 hr = UserExperienceOnDetectBegin(&pEngineState->userExperience, pEngineState->registration.fCached, pEngineState->registration.fInstalled, pEngineState->packages.cPackages);
312 ExitOnRootFailure(hr, "UX aborted detect begin.");
313
314 pEngineState->userExperience.hwndDetect = hwndParent;
315
316 hr = SearchesExecute(&pEngineState->searches, &pEngineState->variables);
317 ExitOnFailure(hr, "Failed to execute searches.");
318
319 // Load all of the related bundles.
320 hr = RegistrationDetectRelatedBundles(&pEngineState->registration);
321 ExitOnFailure(hr, "Failed to detect related bundles.");
322
323 hr = DependencyDetectProviderKeyBundleId(&pEngineState->registration);
324 if (SUCCEEDED(hr))
325 {
326 hr = DetectForwardCompatibleBundles(&pEngineState->userExperience, &pEngineState->registration);
327 ExitOnFailure(hr, "Failed to detect forward compatible bundle.");
328 }
329 else if (E_NOTFOUND == hr)
330 {
331 hr = S_OK;
332 }
333 ExitOnFailure(hr, "Failed to detect provider key bundle id.");
334
335 // Report the related bundles.
336 hr = DetectReportRelatedBundles(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, pEngineState->command.action, &pEngineState->registration.fEligibleForCleanup);
337 ExitOnFailure(hr, "Failed to report detected related bundles.");
338
339 // Do update detection.
340 hr = DetectUpdate(pEngineState->registration.sczId, &pEngineState->userExperience, &pEngineState->update);
341 ExitOnFailure(hr, "Failed to detect update.");
342
343 // Detecting MSPs requires special initialization before processing each package but
344 // only do the detection if there are actually patch packages to detect because it
345 // can be expensive.
346 if (pEngineState->packages.cPatchInfo)
347 {
348 hr = MspEngineDetectInitialize(&pEngineState->packages);
349 ExitOnFailure(hr, "Failed to initialize MSP engine detection.");
350
351 hr = MsiEngineDetectInitialize(&pEngineState->packages);
352 ExitOnFailure(hr, "Failed to initialize MSI engine detection.");
353 }
354
355 for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i)
356 {
357 pPackage = pEngineState->packages.rgPackages + i;
358
359 hr = DetectPackage(pEngineState, pPackage);
360
361 // If the package detection failed, ensure the package state is set to unknown.
362 if (FAILED(hr))
363 {
364 if (SUCCEEDED(hrFirstPackageFailure))
365 {
366 hrFirstPackageFailure = hr;
367 }
368
369 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
370 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
371 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
372 }
373 }
374
375 hr = DependencyDetect(pEngineState);
376 ExitOnFailure(hr, "Failed to detect the dependencies.");
377
378 // Log the detected states.
379 for (DWORD iPackage = 0; iPackage < pEngineState->packages.cPackages; ++iPackage)
380 {
381 pPackage = pEngineState->packages.rgPackages + iPackage;
382
383 // If any packages that can affect registration are present, then the bundle should not automatically be uninstalled.
384 if (pEngineState->registration.fEligibleForCleanup && pPackage->fCanAffectRegistration &&
385 (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState ||
386 BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState))
387 {
388 pEngineState->registration.fEligibleForCleanup = FALSE;
389 }
390
391 LogId(REPORT_STANDARD, MSG_DETECTED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingBoolToString(pPackage->fCached), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState));
392
393 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
394 {
395 for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature)
396 {
397 const BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature;
398 LogId(REPORT_STANDARD, MSG_DETECTED_MSI_FEATURE, pPackage->sczId, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState));
399 }
400 }
401 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
402 {
403 for (DWORD iTargetProduct = 0; iTargetProduct < pPackage->Msp.cTargetProductCodes; ++iTargetProduct)
404 {
405 const BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + iTargetProduct;
406 LogId(REPORT_STANDARD, MSG_DETECTED_MSP_TARGET, pPackage->sczId, pTargetProduct->wzTargetProductCode, LoggingPackageStateToString(pTargetProduct->patchPackageState));
407 }
408 }
409 }
410
411LExit:
412 if (SUCCEEDED(hr))
413 {
414 hr = hrFirstPackageFailure;
415 }
416
417 if (SUCCEEDED(hr))
418 {
419 pEngineState->fDetected = TRUE;
420 }
421
422 if (fDetectBegan)
423 {
424 UserExperienceOnDetectComplete(&pEngineState->userExperience, hr, pEngineState->registration.fEligibleForCleanup);
425 }
426
427 pEngineState->userExperience.hwndDetect = NULL;
428
429 LogId(REPORT_STANDARD, MSG_DETECT_COMPLETE, hr, !fDetectBegan ? "(failed)" : LoggingBoolToString(pEngineState->registration.fInstalled), !fDetectBegan ? "(failed)" : LoggingBoolToString(pEngineState->registration.fCached), FAILED(hr) ? "(failed)" : LoggingBoolToString(pEngineState->registration.fEligibleForCleanup));
430
431 return hr;
432}
433
434extern "C" HRESULT CorePlan(
435 __in BURN_ENGINE_STATE* pEngineState,
436 __in BOOTSTRAPPER_ACTION action
437 )
438{
439 HRESULT hr = S_OK;
440 BOOL fPlanBegan = FALSE;
441 BURN_PACKAGE* pUpgradeBundlePackage = NULL;
442 BURN_PACKAGE* pForwardCompatibleBundlePackage = NULL;
443 BOOL fContinuePlanning = TRUE; // assume we won't skip planning due to dependencies.
444
445 LogId(REPORT_STANDARD, MSG_PLAN_BEGIN, pEngineState->packages.cPackages, LoggingBurnActionToString(action));
446
447 fPlanBegan = TRUE;
448 hr = UserExperienceOnPlanBegin(&pEngineState->userExperience, pEngineState->packages.cPackages);
449 ExitOnRootFailure(hr, "BA aborted plan begin.");
450
451 if (!pEngineState->fDetected)
452 {
453 ExitOnFailure(hr = E_INVALIDSTATE, "Plan cannot be done without a successful Detect.");
454 }
455 else if (pEngineState->plan.fAffectedMachineState)
456 {
457 ExitOnFailure(hr = E_INVALIDSTATE, "Plan requires a new successful Detect after calling Apply.");
458 }
459
460 // Always reset the plan.
461 pEngineState->fPlanned = FALSE;
462 PlanReset(&pEngineState->plan, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads);
463
464 // Remember the overall action state in the plan since it shapes the changes
465 // we make everywhere.
466 pEngineState->plan.action = action;
467 pEngineState->plan.pPayloads = &pEngineState->payloads;
468 pEngineState->plan.wzBundleId = pEngineState->registration.sczId;
469 pEngineState->plan.wzBundleProviderKey = pEngineState->registration.sczId;
470 pEngineState->plan.fDisableRollback = pEngineState->fDisableRollback;
471
472 hr = PlanSetVariables(action, &pEngineState->variables);
473 ExitOnFailure(hr, "Failed to update action.");
474
475 // Set resume commandline
476 hr = PlanSetResumeCommand(&pEngineState->registration, action, &pEngineState->command, &pEngineState->log);
477 ExitOnFailure(hr, "Failed to set resume command");
478
479 hr = DependencyPlanInitialize(&pEngineState->registration, &pEngineState->plan);
480 ExitOnFailure(hr, "Failed to initialize the dependencies for the plan.");
481
482 if (BOOTSTRAPPER_ACTION_LAYOUT == action)
483 {
484 Assert(!pEngineState->plan.fPerMachine);
485
486 // Plan the bundle's layout.
487 hr = PlanLayoutBundle(&pEngineState->plan, pEngineState->registration.sczExecutableName, pEngineState->section.qwBundleSize, &pEngineState->variables, &pEngineState->layoutPayloads);
488 ExitOnFailure(hr, "Failed to plan the layout of the bundle.");
489
490 // Plan the packages' layout.
491 hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType);
492 ExitOnFailure(hr, "Failed to plan packages.");
493 }
494 else if (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED == action)
495 {
496 Assert(!pEngineState->plan.fPerMachine);
497
498 pUpgradeBundlePackage = &pEngineState->update.package;
499
500 hr = PlanUpdateBundle(&pEngineState->userExperience, pUpgradeBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType);
501 ExitOnFailure(hr, "Failed to plan update.");
502 }
503 else
504 {
505 hr = PlanForwardCompatibleBundles(&pEngineState->userExperience, &pEngineState->command, &pEngineState->plan, &pEngineState->registration, action);
506 ExitOnFailure(hr, "Failed to plan forward compatible bundles.");
507
508 if (pEngineState->plan.fEnabledForwardCompatibleBundle)
509 {
510 Assert(!pEngineState->plan.fPerMachine);
511
512 pForwardCompatibleBundlePackage = &pEngineState->plan.forwardCompatibleBundle;
513
514 hr = PlanPassThroughBundle(&pEngineState->userExperience, pForwardCompatibleBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType);
515 ExitOnFailure(hr, "Failed to plan passthrough.");
516 }
517 else // doing an action that modifies the machine state.
518 {
519 pEngineState->plan.fPerMachine = pEngineState->registration.fPerMachine; // default the scope of the plan to the per-machine state of the bundle.
520
521 hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, pEngineState->command.resumeType, pEngineState->command.relationType, &fContinuePlanning);
522 ExitOnFailure(hr, "Failed to plan registration.");
523
524 if (fContinuePlanning)
525 {
526 // Remember the early index, because we want to be able to insert some related bundles
527 // into the plan before other executed packages. This particularly occurs for uninstallation
528 // of addons and patches, which should be uninstalled before the main product.
529 DWORD dwExecuteActionEarlyIndex = pEngineState->plan.cExecuteActions;
530
531 // Plan the related bundles first to support downgrades with ref-counting.
532 hr = PlanRelatedBundlesBegin(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan);
533 ExitOnFailure(hr, "Failed to plan related bundles.");
534
535 hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType);
536 ExitOnFailure(hr, "Failed to plan packages.");
537
538 // Schedule the update of related bundles last.
539 hr = PlanRelatedBundlesComplete(&pEngineState->registration, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, dwExecuteActionEarlyIndex);
540 ExitOnFailure(hr, "Failed to schedule related bundles.");
541 }
542 }
543 }
544
545 if (fContinuePlanning)
546 {
547 // Finally, display all packages and related bundles in the log.
548 LogPackages(pUpgradeBundlePackage, pForwardCompatibleBundlePackage, &pEngineState->packages, &pEngineState->registration.relatedBundles, action);
549 }
550
551 PlanDump(&pEngineState->plan);
552
553LExit:
554 if (SUCCEEDED(hr))
555 {
556 pEngineState->fPlanned = TRUE;
557 }
558
559 if (fPlanBegan)
560 {
561 UserExperienceOnPlanComplete(&pEngineState->userExperience, hr);
562 }
563
564 LogId(REPORT_STANDARD, MSG_PLAN_COMPLETE, hr);
565
566 return hr;
567}
568
569extern "C" HRESULT CoreElevate(
570 __in BURN_ENGINE_STATE* pEngineState,
571 __in_opt HWND hwndParent
572 )
573{
574 HRESULT hr = S_OK;
575 DWORD cAVRetryAttempts = 0;
576
577 while (INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe)
578 {
579 // If the elevated companion pipe isn't created yet, let's make that happen.
580 if (!pEngineState->sczBundleEngineWorkingPath)
581 {
582 hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath);
583 ExitOnFailure(hr, "Failed to cache engine to working directory.");
584 }
585
586 hr = ElevationElevate(pEngineState, hwndParent);
587 if (E_SUSPECTED_AV_INTERFERENCE == hr && 1 > cAVRetryAttempts)
588 {
589 ++cAVRetryAttempts;
590 continue;
591 }
592 ExitOnFailure(hr, "Failed to actually elevate.");
593
594 hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, TRUE, TRUE);
595 ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED);
596 }
597
598LExit:
599 return hr;
600}
601
602extern "C" HRESULT CoreApply(
603 __in BURN_ENGINE_STATE* pEngineState,
604 __in_opt HWND hwndParent
605 )
606{
607 HRESULT hr = S_OK;
608 HANDLE hLock = NULL;
609 DWORD cOverallProgressTicks = 0;
610 HANDLE hCacheThread = NULL;
611 BOOL fApplyInitialize = FALSE;
612 BOOL fElevated = FALSE;
613 BOOL fRegistered = FALSE;
614 BOOL fRollback = FALSE;
615 BOOL fSuspend = FALSE;
616 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
617 BURN_CACHE_THREAD_CONTEXT cacheThreadContext = { };
618 DWORD dwPhaseCount = 0;
619 BOOTSTRAPPER_APPLYCOMPLETE_ACTION applyCompleteAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE;
620
621 LogId(REPORT_STANDARD, MSG_APPLY_BEGIN);
622
623 if (!pEngineState->fPlanned)
624 {
625 ExitOnFailure(hr = E_INVALIDSTATE, "Apply cannot be done without a successful Plan.");
626 }
627 else if (pEngineState->plan.fAffectedMachineState)
628 {
629 ExitOnFailure(hr = E_INVALIDSTATE, "Plans cannot be applied multiple times.");
630 }
631
632 // Ensure any previous attempts to execute are reset.
633 ApplyReset(&pEngineState->userExperience, &pEngineState->packages);
634
635 if (pEngineState->plan.cCacheActions)
636 {
637 ++dwPhaseCount;
638 }
639 if (pEngineState->plan.cExecuteActions)
640 {
641 ++dwPhaseCount;
642 }
643
644 hr = UserExperienceOnApplyBegin(&pEngineState->userExperience, dwPhaseCount);
645 ExitOnRootFailure(hr, "BA aborted apply begin.");
646
647 pEngineState->plan.fAffectedMachineState = pEngineState->plan.fCanAffectMachineState;
648
649 // Abort if this bundle already requires a restart.
650 if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING == pEngineState->command.resumeType)
651 {
652 restart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
653
654 hr = HRESULT_FROM_WIN32(ERROR_FAIL_NOACTION_REBOOT);
655 UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_APPLY, NULL, hr, NULL, MB_ICONERROR | MB_OK, IDNOACTION); // ignore return value.
656 ExitFunction();
657 }
658
659 hr = ApplyLock(FALSE, &hLock);
660 ExitOnFailure(hr, "Another per-user setup is already executing.");
661
662 // Initialize only after getting a lock.
663 fApplyInitialize = TRUE;
664 ApplyInitialize();
665
666 pEngineState->userExperience.hwndApply = hwndParent;
667
668 hr = ApplySetVariables(&pEngineState->variables);
669 ExitOnFailure(hr, "Failed to set initial apply variables.");
670
671 // If the plan is empty of work to do, skip everything.
672 if (!(pEngineState->plan.cRegistrationActions || pEngineState->plan.cCacheActions || pEngineState->plan.cExecuteActions || pEngineState->plan.cCleanActions))
673 {
674 LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED);
675 ExitFunction();
676 }
677
678 // Ensure the engine is cached to the working path.
679 if (!pEngineState->sczBundleEngineWorkingPath)
680 {
681 hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath);
682 ExitOnFailure(hr, "Failed to cache engine to working directory.");
683 }
684
685 // Elevate.
686 if (pEngineState->plan.fPerMachine)
687 {
688 hr = CoreElevate(pEngineState, pEngineState->userExperience.hwndApply);
689 ExitOnFailure(hr, "Failed to elevate.");
690
691 hr = ElevationApplyInitialize(pEngineState->companionConnection.hPipe, &pEngineState->userExperience, &pEngineState->variables, pEngineState->plan.action, pEngineState->automaticUpdates, !pEngineState->fDisableSystemRestore);
692 ExitOnFailure(hr, "Failed to initialize apply in elevated process.");
693
694 fElevated = TRUE;
695 }
696
697 // Register.
698 if (pEngineState->plan.fCanAffectMachineState)
699 {
700 fRegistered = TRUE;
701 hr = ApplyRegister(pEngineState);
702 ExitOnFailure(hr, "Failed to register bundle.");
703 }
704
705 // Cache.
706 if (pEngineState->plan.cCacheActions)
707 {
708 // Launch the cache thread.
709 cacheThreadContext.pEngineState = pEngineState;
710 cacheThreadContext.pcOverallProgressTicks = &cOverallProgressTicks;
711 cacheThreadContext.pfRollback = &fRollback;
712
713 hCacheThread = ::CreateThread(NULL, 0, CacheThreadProc, &cacheThreadContext, 0, NULL);
714 ExitOnNullWithLastError(hCacheThread, hr, "Failed to create cache thread.");
715
716 // If we're not caching in parallel, wait for the cache thread to terminate.
717 if (!pEngineState->fParallelCacheAndExecute)
718 {
719 hr = WaitForCacheThread(hCacheThread);
720 ExitOnFailure(hr, "Failed while caching, aborting execution.");
721
722 ReleaseHandle(hCacheThread);
723 }
724 }
725
726 // Execute.
727 if (pEngineState->plan.cExecuteActions)
728 {
729 hr = ApplyExecute(pEngineState, hCacheThread, &cOverallProgressTicks, &fRollback, &fSuspend, &restart);
730 UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that execute completed.
731 }
732
733 // Wait for cache thread to terminate, this should return immediately unless we're waiting for layout to complete.
734 if (hCacheThread)
735 {
736 HRESULT hrCached = WaitForCacheThread(hCacheThread);
737 if (SUCCEEDED(hr))
738 {
739 hr = hrCached;
740 }
741 }
742
743 // If something went wrong or force restarted, skip cleaning.
744 if (FAILED(hr) || fRollback || fSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart)
745 {
746 ExitFunction();
747 }
748
749 // Clean.
750 if (pEngineState->plan.cCleanActions)
751 {
752 ApplyClean(&pEngineState->userExperience, &pEngineState->plan, pEngineState->companionConnection.hPipe);
753 }
754
755LExit:
756 // Unregister.
757 if (fRegistered)
758 {
759 ApplyUnregister(pEngineState, FAILED(hr) || fRollback, fSuspend, restart);
760 }
761
762 if (fElevated)
763 {
764 ElevationApplyUninitialize(pEngineState->companionConnection.hPipe);
765 }
766
767 pEngineState->userExperience.hwndApply = NULL;
768
769 if (fApplyInitialize)
770 {
771 ApplyUninitialize();
772 }
773
774 if (hLock)
775 {
776 ::ReleaseMutex(hLock);
777 ::CloseHandle(hLock);
778 }
779
780 ReleaseHandle(hCacheThread);
781
782 UserExperienceOnApplyComplete(&pEngineState->userExperience, hr, restart, &applyCompleteAction);
783 if (BOOTSTRAPPER_APPLYCOMPLETE_ACTION_RESTART == applyCompleteAction)
784 {
785 pEngineState->fRestart = TRUE;
786 }
787
788 LogId(REPORT_STANDARD, MSG_APPLY_COMPLETE, hr, LoggingRestartToString(restart), LoggingBoolToString(pEngineState->fRestart));
789
790 return hr;
791}
792
793extern "C" HRESULT CoreLaunchApprovedExe(
794 __in BURN_ENGINE_STATE* pEngineState,
795 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
796 )
797{
798 HRESULT hr = S_OK;
799 DWORD dwProcessId = 0;
800
801 LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_BEGIN, pLaunchApprovedExe->sczId);
802
803 hr = UserExperienceOnLaunchApprovedExeBegin(&pEngineState->userExperience);
804 ExitOnRootFailure(hr, "BA aborted LaunchApprovedExe begin.");
805
806 // Elevate.
807 hr = CoreElevate(pEngineState, pLaunchApprovedExe->hwndParent);
808 ExitOnFailure(hr, "Failed to elevate.");
809
810 // Launch.
811 hr = ElevationLaunchApprovedExe(pEngineState->companionConnection.hPipe, pLaunchApprovedExe, &dwProcessId);
812
813LExit:
814 UserExperienceOnLaunchApprovedExeComplete(&pEngineState->userExperience, hr, dwProcessId);
815
816 LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_COMPLETE, hr, dwProcessId);
817
818 ApprovedExesUninitializeLaunch(pLaunchApprovedExe);
819
820 return hr;
821}
822
823extern "C" HRESULT CoreQuit(
824 __in BURN_ENGINE_STATE* pEngineState,
825 __in int nExitCode
826 )
827{
828 HRESULT hr = S_OK;
829
830 // Save engine state if resume mode is unequal to "none".
831 if (BURN_RESUME_MODE_NONE != pEngineState->resumeMode)
832 {
833 hr = CoreSaveEngineState(pEngineState);
834 if (FAILED(hr))
835 {
836 LogErrorId(hr, MSG_STATE_NOT_SAVED);
837 hr = S_OK;
838 }
839 }
840
841 LogId(REPORT_STANDARD, MSG_QUIT, nExitCode);
842
843 pEngineState->fQuit = TRUE;
844
845 ::PostQuitMessage(nExitCode); // go bye-bye.
846
847 return hr;
848}
849
850extern "C" HRESULT CoreSaveEngineState(
851 __in BURN_ENGINE_STATE* pEngineState
852 )
853{
854 HRESULT hr = S_OK;
855 BYTE* pbBuffer = NULL;
856 SIZE_T cbBuffer = 0;
857
858 // serialize engine state
859 hr = CoreSerializeEngineState(pEngineState, &pbBuffer, &cbBuffer);
860 ExitOnFailure(hr, "Failed to serialize engine state.");
861
862 // write to registration store
863 if (pEngineState->registration.fPerMachine)
864 {
865 hr = ElevationSaveState(pEngineState->companionConnection.hPipe, pbBuffer, cbBuffer);
866 ExitOnFailure(hr, "Failed to save engine state in per-machine process.");
867 }
868 else
869 {
870 hr = RegistrationSaveState(&pEngineState->registration, pbBuffer, cbBuffer);
871 ExitOnFailure(hr, "Failed to save engine state.");
872 }
873
874LExit:
875 ReleaseBuffer(pbBuffer);
876
877 return hr;
878}
879
880extern "C" LPCWSTR CoreRelationTypeToCommandLineString(
881 __in BOOTSTRAPPER_RELATION_TYPE relationType
882 )
883{
884 LPCWSTR wzRelationTypeCommandLine = NULL;
885 switch (relationType)
886 {
887 case BOOTSTRAPPER_RELATION_DETECT:
888 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_DETECT;
889 break;
890 case BOOTSTRAPPER_RELATION_UPGRADE:
891 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE;
892 break;
893 case BOOTSTRAPPER_RELATION_ADDON:
894 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_ADDON;
895 break;
896 case BOOTSTRAPPER_RELATION_PATCH:
897 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_PATCH;
898 break;
899 case BOOTSTRAPPER_RELATION_UPDATE:
900 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPDATE;
901 break;
902 case BOOTSTRAPPER_RELATION_DEPENDENT:
903 break;
904 case BOOTSTRAPPER_RELATION_NONE: __fallthrough;
905 default:
906 wzRelationTypeCommandLine = NULL;
907 break;
908 }
909
910 return wzRelationTypeCommandLine;
911}
912
913extern "C" HRESULT CoreRecreateCommandLine(
914 __deref_inout_z LPWSTR* psczCommandLine,
915 __in BOOTSTRAPPER_ACTION action,
916 __in BOOTSTRAPPER_DISPLAY display,
917 __in BOOTSTRAPPER_RESTART restart,
918 __in BOOTSTRAPPER_RELATION_TYPE relationType,
919 __in BOOL fPassthrough,
920 __in_z_opt LPCWSTR wzActiveParent,
921 __in_z_opt LPCWSTR wzAncestors,
922 __in_z_opt LPCWSTR wzAppendLogPath,
923 __in_z_opt LPCWSTR wzAdditionalCommandLineArguments
924 )
925{
926 HRESULT hr = S_OK;
927 LPWSTR scz = NULL;
928 LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType);
929
930 hr = StrAllocString(psczCommandLine, L"", 0);
931 ExitOnFailure(hr, "Failed to empty command line.");
932
933 switch (display)
934 {
935 case BOOTSTRAPPER_DISPLAY_NONE:
936 hr = StrAllocConcat(psczCommandLine, L" /quiet", 0);
937 break;
938 case BOOTSTRAPPER_DISPLAY_PASSIVE:
939 hr = StrAllocConcat(psczCommandLine, L" /passive", 0);
940 break;
941 }
942 ExitOnFailure(hr, "Failed to append display state to command-line");
943
944 switch (action)
945 {
946 case BOOTSTRAPPER_ACTION_MODIFY:
947 hr = StrAllocConcat(psczCommandLine, L" /modify", 0);
948 break;
949 case BOOTSTRAPPER_ACTION_REPAIR:
950 hr = StrAllocConcat(psczCommandLine, L" /repair", 0);
951 break;
952 case BOOTSTRAPPER_ACTION_UNINSTALL:
953 hr = StrAllocConcat(psczCommandLine, L" /uninstall", 0);
954 break;
955 }
956 ExitOnFailure(hr, "Failed to append action state to command-line");
957
958 switch (restart)
959 {
960 case BOOTSTRAPPER_RESTART_ALWAYS:
961 hr = StrAllocConcat(psczCommandLine, L" /forcerestart", 0);
962 break;
963 case BOOTSTRAPPER_RESTART_NEVER:
964 hr = StrAllocConcat(psczCommandLine, L" /norestart", 0);
965 break;
966 }
967 ExitOnFailure(hr, "Failed to append restart state to command-line");
968
969 if (wzActiveParent)
970 {
971 if (*wzActiveParent)
972 {
973 hr = StrAllocFormatted(&scz, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_PARENT, wzActiveParent);
974 ExitOnFailure(hr, "Failed to format active parent command-line for command-line.");
975 }
976 else
977 {
978 hr = StrAllocFormatted(&scz, L" /%ls", BURN_COMMANDLINE_SWITCH_PARENT_NONE);
979 ExitOnFailure(hr, "Failed to format parent:none command-line for command-line.");
980 }
981
982 hr = StrAllocConcat(psczCommandLine, scz, 0);
983 ExitOnFailure(hr, "Failed to append active parent command-line to command-line.");
984 }
985
986 if (wzAncestors)
987 {
988 hr = StrAllocFormatted(&scz, L" /%ls=%ls", BURN_COMMANDLINE_SWITCH_ANCESTORS, wzAncestors);
989 ExitOnFailure(hr, "Failed to format ancestors for command-line.");
990
991 hr = StrAllocConcat(psczCommandLine, scz, 0);
992 ExitOnFailure(hr, "Failed to append ancestors to command-line.");
993 }
994
995 if (wzRelationTypeCommandLine)
996 {
997 hr = StrAllocFormatted(&scz, L" /%ls", wzRelationTypeCommandLine);
998 ExitOnFailure(hr, "Failed to format relation type for command-line.");
999
1000 hr = StrAllocConcat(psczCommandLine, scz, 0);
1001 ExitOnFailure(hr, "Failed to append relation type to command-line.");
1002 }
1003
1004 if (fPassthrough)
1005 {
1006 hr = StrAllocFormatted(&scz, L" /%ls", BURN_COMMANDLINE_SWITCH_PASSTHROUGH);
1007 ExitOnFailure(hr, "Failed to format passthrough for command-line.");
1008
1009 hr = StrAllocConcat(psczCommandLine, scz, 0);
1010 ExitOnFailure(hr, "Failed to append passthrough to command-line.");
1011 }
1012
1013 if (wzAppendLogPath && *wzAppendLogPath)
1014 {
1015 hr = StrAllocFormatted(&scz, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_LOG_APPEND, wzAppendLogPath);
1016 ExitOnFailure(hr, "Failed to format append log command-line for command-line.");
1017
1018 hr = StrAllocConcat(psczCommandLine, scz, 0);
1019 ExitOnFailure(hr, "Failed to append log command-line to command-line");
1020 }
1021
1022 if (wzAdditionalCommandLineArguments && *wzAdditionalCommandLineArguments)
1023 {
1024 hr = StrAllocConcat(psczCommandLine, L" ", 0);
1025 ExitOnFailure(hr, "Failed to append space to command-line.");
1026
1027 hr = StrAllocConcat(psczCommandLine, wzAdditionalCommandLineArguments, 0);
1028 ExitOnFailure(hr, "Failed to append command-line to command-line.");
1029 }
1030
1031LExit:
1032 ReleaseStr(scz);
1033
1034 return hr;
1035}
1036
1037extern "C" HRESULT CoreAppendFileHandleAttachedToCommandLine(
1038 __in HANDLE hFileWithAttachedContainer,
1039 __out HANDLE* phExecutableFile,
1040 __deref_inout_z LPWSTR* psczCommandLine
1041 )
1042{
1043 HRESULT hr = S_OK;
1044 HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
1045
1046 *phExecutableFile = INVALID_HANDLE_VALUE;
1047
1048 if (!::DuplicateHandle(::GetCurrentProcess(), hFileWithAttachedContainer, ::GetCurrentProcess(), &hExecutableFile, 0, TRUE, DUPLICATE_SAME_ACCESS))
1049 {
1050 ExitWithLastError(hr, "Failed to duplicate file handle for attached container.");
1051 }
1052
1053 hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%Iu", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, reinterpret_cast<size_t>(hExecutableFile));
1054 ExitOnFailure(hr, "Failed to append the file handle to the command line.");
1055
1056 *phExecutableFile = hExecutableFile;
1057 hExecutableFile = INVALID_HANDLE_VALUE;
1058
1059LExit:
1060 ReleaseFileHandle(hExecutableFile);
1061
1062 return hr;
1063}
1064
1065extern "C" HRESULT CoreAppendFileHandleSelfToCommandLine(
1066 __in LPCWSTR wzExecutablePath,
1067 __out HANDLE* phExecutableFile,
1068 __deref_inout_z LPWSTR* psczCommandLine,
1069 __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine
1070 )
1071{
1072 HRESULT hr = S_OK;
1073 HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
1074 SECURITY_ATTRIBUTES securityAttributes = { };
1075 securityAttributes.bInheritHandle = TRUE;
1076 *phExecutableFile = INVALID_HANDLE_VALUE;
1077
1078 hExecutableFile = ::CreateFileW(wzExecutablePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, &securityAttributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1079 if (INVALID_HANDLE_VALUE != hExecutableFile)
1080 {
1081 hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%Iu", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, reinterpret_cast<size_t>(hExecutableFile));
1082 ExitOnFailure(hr, "Failed to append the file handle to the command line.");
1083
1084 if (psczObfuscatedCommandLine)
1085 {
1086 hr = StrAllocFormatted(psczObfuscatedCommandLine, L"%ls -%ls=%Iu", *psczObfuscatedCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, reinterpret_cast<size_t>(hExecutableFile));
1087 ExitOnFailure(hr, "Failed to append the file handle to the obfuscated command line.");
1088 }
1089
1090 *phExecutableFile = hExecutableFile;
1091 hExecutableFile = INVALID_HANDLE_VALUE;
1092 }
1093
1094LExit:
1095 ReleaseFileHandle(hExecutableFile);
1096
1097 return hr;
1098}
1099
1100extern "C" void CoreCleanup(
1101 __in BURN_ENGINE_STATE* pEngineState
1102 )
1103{
1104 HRESULT hr = S_OK;
1105 LONGLONG llValue = 0;
1106 BOOL fNeedsElevation = pEngineState->registration.fPerMachine && INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe;
1107
1108 LogId(REPORT_STANDARD, MSG_CLEANUP_BEGIN);
1109
1110 if (pEngineState->plan.fAffectedMachineState)
1111 {
1112 LogId(REPORT_STANDARD, MSG_CLEANUP_SKIPPED_APPLY);
1113 ExitFunction();
1114 }
1115
1116 if (fNeedsElevation)
1117 {
1118 hr = VariableGetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, &llValue);
1119 ExitOnFailure(hr, "Failed to get value of WixBundleElevated variable during cleanup");
1120
1121 if (llValue)
1122 {
1123 fNeedsElevation = FALSE;
1124 }
1125 else
1126 {
1127 LogId(REPORT_STANDARD, MSG_CLEANUP_SKIPPED_ELEVATION_REQUIRED);
1128 ExitFunction();
1129 }
1130 }
1131
1132 if (!pEngineState->fDetected)
1133 {
1134 hr = CoreDetect(pEngineState, pEngineState->hMessageWindow);
1135 ExitOnFailure(hr, "Detect during cleanup failed");
1136 }
1137
1138 if (!pEngineState->registration.fEligibleForCleanup)
1139 {
1140 ExitFunction();
1141 }
1142
1143 hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL);
1144 ExitOnFailure(hr, "Plan during cleanup failed");
1145
1146 hr = CoreApply(pEngineState, pEngineState->hMessageWindow);
1147 ExitOnFailure(hr, "Apply during cleanup failed");
1148
1149LExit:
1150 LogId(REPORT_STANDARD, MSG_CLEANUP_COMPLETE, hr);
1151}
1152
1153// internal helper functions
1154
1155static HRESULT ParseCommandLine(
1156 __in int argc,
1157 __in LPWSTR* argv,
1158 __in BOOTSTRAPPER_COMMAND* pCommand,
1159 __in BURN_PIPE_CONNECTION* pCompanionConnection,
1160 __in BURN_PIPE_CONNECTION* pEmbeddedConnection,
1161 __in BURN_VARIABLES* pVariables,
1162 __out BURN_MODE* pMode,
1163 __out BURN_AU_PAUSE_ACTION* pAutomaticUpdates,
1164 __out BOOL* pfDisableSystemRestore,
1165 __out_z LPWSTR* psczSourceProcessPath,
1166 __out_z LPWSTR* psczOriginalSource,
1167 __out BOOL* pfDisableUnelevate,
1168 __out DWORD *pdwLoggingAttributes,
1169 __out_z LPWSTR* psczLogFile,
1170 __out_z LPWSTR* psczActiveParent,
1171 __out_z LPWSTR* psczIgnoreDependencies,
1172 __out_z LPWSTR* psczAncestors,
1173 __out_z LPWSTR* psczSanitizedCommandLine
1174 )
1175{
1176 HRESULT hr = S_OK;
1177 BOOL fUnknownArg = FALSE;
1178 BOOL fHidden = FALSE;
1179 LPWSTR sczCommandLine = NULL;
1180 LPWSTR sczSanitizedArgument = NULL;
1181 LPWSTR sczVariableName = NULL;
1182
1183 for (int i = 0; i < argc; ++i)
1184 {
1185 fUnknownArg = FALSE;
1186 int originalIndex = i;
1187 ReleaseNullStr(sczSanitizedArgument);
1188
1189 if (argv[i][0] == L'-' || argv[i][0] == L'/')
1190 {
1191 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"l", -1) ||
1192 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"log", -1) ||
1193 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"xlog", -1))
1194 {
1195 *pdwLoggingAttributes &= ~BURN_LOGGING_ATTRIBUTE_APPEND;
1196
1197 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], 1, L"x", 1))
1198 {
1199 *pdwLoggingAttributes |= BURN_LOGGING_ATTRIBUTE_VERBOSE | BURN_LOGGING_ATTRIBUTE_EXTRADEBUG;
1200 }
1201
1202 if (i + 1 >= argc)
1203 {
1204 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for log.");
1205 }
1206
1207 ++i;
1208
1209 hr = StrAllocString(psczLogFile, argv[i], 0);
1210 ExitOnFailure(hr, "Failed to copy log file path.");
1211 }
1212 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"?", -1) ||
1213 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"h", -1) ||
1214 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"help", -1))
1215 {
1216 pCommand->action = BOOTSTRAPPER_ACTION_HELP;
1217 }
1218 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"q", -1) ||
1219 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"quiet", -1) ||
1220 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"s", -1) ||
1221 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"silent", -1))
1222 {
1223 pCommand->display = BOOTSTRAPPER_DISPLAY_NONE;
1224
1225 if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart)
1226 {
1227 pCommand->restart = BOOTSTRAPPER_RESTART_AUTOMATIC;
1228 }
1229 }
1230 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"passive", -1))
1231 {
1232 pCommand->display = BOOTSTRAPPER_DISPLAY_PASSIVE;
1233
1234 if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart)
1235 {
1236 pCommand->restart = BOOTSTRAPPER_RESTART_AUTOMATIC;
1237 }
1238 }
1239 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"norestart", -1))
1240 {
1241 pCommand->restart = BOOTSTRAPPER_RESTART_NEVER;
1242 }
1243 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"forcerestart", -1))
1244 {
1245 pCommand->restart = BOOTSTRAPPER_RESTART_ALWAYS;
1246 }
1247 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"promptrestart", -1))
1248 {
1249 pCommand->restart = BOOTSTRAPPER_RESTART_PROMPT;
1250 }
1251 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"layout", -1))
1252 {
1253 if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1254 {
1255 pCommand->action = BOOTSTRAPPER_ACTION_LAYOUT;
1256 }
1257
1258 // If there is another command line argument and it is not a switch, use that as the layout directory.
1259 if (i + 1 < argc && argv[i + 1][0] != L'-' && argv[i + 1][0] != L'/')
1260 {
1261 ++i;
1262
1263 hr = PathExpand(&pCommand->wzLayoutDirectory, argv[i], PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH);
1264 ExitOnFailure(hr, "Failed to copy path for layout directory.");
1265 }
1266 }
1267 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"uninstall", -1))
1268 {
1269 if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1270 {
1271 pCommand->action = BOOTSTRAPPER_ACTION_UNINSTALL;
1272 }
1273 }
1274 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"repair", -1))
1275 {
1276 if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1277 {
1278 pCommand->action = BOOTSTRAPPER_ACTION_REPAIR;
1279 }
1280 }
1281 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"modify", -1))
1282 {
1283 if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1284 {
1285 pCommand->action = BOOTSTRAPPER_ACTION_MODIFY;
1286 }
1287 }
1288 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"package", -1) ||
1289 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"update", -1))
1290 {
1291 if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action)
1292 {
1293 pCommand->action = BOOTSTRAPPER_ACTION_INSTALL;
1294 }
1295 }
1296 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"noaupause", -1))
1297 {
1298 *pAutomaticUpdates = BURN_AU_PAUSE_ACTION_NONE;
1299 }
1300 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"keepaupaused", -1))
1301 {
1302 // Switch /noaupause takes precedence.
1303 if (BURN_AU_PAUSE_ACTION_NONE != *pAutomaticUpdates)
1304 {
1305 *pAutomaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME;
1306 }
1307 }
1308 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"disablesystemrestore", -1))
1309 {
1310 *pfDisableSystemRestore = TRUE;
1311 }
1312 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"originalsource", -1))
1313 {
1314 if (i + 1 >= argc)
1315 {
1316 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for original source.");
1317 }
1318
1319 ++i;
1320 hr = StrAllocString(psczOriginalSource, argv[i], 0);
1321 ExitOnFailure(hr, "Failed to copy last used source.");
1322 }
1323 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT, -1))
1324 {
1325 if (i + 1 >= argc)
1326 {
1327 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a value for parent.");
1328 }
1329
1330 ++i;
1331
1332 hr = StrAllocString(psczActiveParent, argv[i], 0);
1333 ExitOnFailure(hr, "Failed to copy parent.");
1334 }
1335 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT_NONE, -1))
1336 {
1337 hr = StrAllocString(psczActiveParent, L"", 0);
1338 ExitOnFailure(hr, "Failed to initialize parent to none.");
1339 }
1340 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_LOG_APPEND, -1))
1341 {
1342 if (i + 1 >= argc)
1343 {
1344 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for append log.");
1345 }
1346
1347 ++i;
1348
1349 hr = StrAllocString(psczLogFile, argv[i], 0);
1350 ExitOnFailure(hr, "Failed to copy append log file path.");
1351
1352 *pdwLoggingAttributes |= BURN_LOGGING_ATTRIBUTE_APPEND;
1353 }
1354 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_ELEVATED, -1))
1355 {
1356 if (i + 3 >= argc)
1357 {
1358 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify the elevated name, token and parent process id.");
1359 }
1360
1361 if (BURN_MODE_UNTRUSTED != *pMode)
1362 {
1363 ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided.");
1364 }
1365
1366 *pMode = BURN_MODE_ELEVATED;
1367
1368 ++i;
1369
1370 hr = ParsePipeConnection(argv + i, pCompanionConnection);
1371 ExitOnFailure(hr, "Failed to parse elevated connection.");
1372
1373 i += 2;
1374 }
1375 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM), BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM)))
1376 {
1377 // Get a pointer to the next character after the switch.
1378 LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM)];
1379 if (L'=' != wzParam[0] || L'\0' == wzParam[1])
1380 {
1381 ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_CLEAN_ROOM);
1382 }
1383
1384 if (BURN_MODE_UNTRUSTED != *pMode)
1385 {
1386 ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided.");
1387 }
1388
1389 *pMode = BURN_MODE_NORMAL;
1390
1391 hr = StrAllocString(psczSourceProcessPath, wzParam + 1, 0);
1392 ExitOnFailure(hr, "Failed to copy source process path.");
1393 }
1394 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_EMBEDDED, -1))
1395 {
1396 if (i + 3 >= argc)
1397 {
1398 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify the embedded name, token and parent process id.");
1399 }
1400
1401 switch (*pMode)
1402 {
1403 case BURN_MODE_UNTRUSTED:
1404 // Leave mode as UNTRUSTED to launch the clean room process.
1405 break;
1406 case BURN_MODE_NORMAL:
1407 // The initialization code already assumes that the
1408 // clean room switch is at the beginning of the command line,
1409 // so it's safe to assume that the mode is NORMAL in the clean room.
1410 *pMode = BURN_MODE_EMBEDDED;
1411 break;
1412 default:
1413 ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided.");
1414 }
1415
1416 ++i;
1417
1418 hr = ParsePipeConnection(argv + i, pEmbeddedConnection);
1419 ExitOnFailure(hr, "Failed to parse embedded connection.");
1420
1421 i += 2;
1422 }
1423 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_DETECT, -1))
1424 {
1425 pCommand->relationType = BOOTSTRAPPER_RELATION_DETECT;
1426
1427 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1428 }
1429 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE, -1))
1430 {
1431 pCommand->relationType = BOOTSTRAPPER_RELATION_UPGRADE;
1432
1433 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1434 }
1435 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_ADDON, -1))
1436 {
1437 pCommand->relationType = BOOTSTRAPPER_RELATION_ADDON;
1438
1439 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1440 }
1441 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_PATCH, -1))
1442 {
1443 pCommand->relationType = BOOTSTRAPPER_RELATION_PATCH;
1444
1445 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1446 }
1447 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPDATE, -1))
1448 {
1449 pCommand->relationType = BOOTSTRAPPER_RELATION_UPDATE;
1450
1451 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1452 }
1453 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PASSTHROUGH, -1))
1454 {
1455 pCommand->fPassthrough = TRUE;
1456 }
1457 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE, -1))
1458 {
1459 *pfDisableUnelevate = TRUE;
1460 }
1461 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RUNONCE, -1))
1462 {
1463 if (BURN_MODE_UNTRUSTED != *pMode)
1464 {
1465 ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided.");
1466 }
1467
1468 *pMode = BURN_MODE_RUNONCE;
1469 }
1470 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES), BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES)))
1471 {
1472 // Get a pointer to the next character after the switch.
1473 LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES)];
1474 if (L'=' != wzParam[0] || L'\0' == wzParam[1])
1475 {
1476 ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES);
1477 }
1478
1479 hr = StrAllocString(psczIgnoreDependencies, &wzParam[1], 0);
1480 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
1481 }
1482 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS), BURN_COMMANDLINE_SWITCH_ANCESTORS, lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS)))
1483 {
1484 // Get a pointer to the next character after the switch.
1485 LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS)];
1486 if (L'=' != wzParam[0] || L'\0' == wzParam[1])
1487 {
1488 ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_ANCESTORS);
1489 }
1490
1491 hr = StrAllocString(psczAncestors, &wzParam[1], 0);
1492 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
1493 }
1494 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED), BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED)))
1495 {
1496 // Already processed in InitializeEngineState.
1497 }
1498 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF), BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF)))
1499 {
1500 // Already processed in InitializeEngineState.
1501 }
1502 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX), BURN_COMMANDLINE_SWITCH_PREFIX, lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX)))
1503 {
1504 // Skip (but log) any other private burn switches we don't recognize, so that
1505 // adding future private variables doesn't break old bundles
1506 LogId(REPORT_STANDARD, MSG_BURN_UNKNOWN_PRIVATE_SWITCH, &argv[i][1]);
1507 }
1508 else
1509 {
1510 fUnknownArg = TRUE;
1511 }
1512 }
1513 else
1514 {
1515 fUnknownArg = TRUE;
1516
1517 const wchar_t* pwc = wcschr(argv[i], L'=');
1518 if (pwc)
1519 {
1520 hr = StrAllocString(&sczVariableName, argv[i], pwc - argv[i]);
1521 ExitOnFailure(hr, "Failed to copy variable name.");
1522
1523 hr = VariableIsHidden(pVariables, sczVariableName, &fHidden);
1524 ExitOnFailure(hr, "Failed to determine whether variable is hidden.");
1525
1526 if (fHidden)
1527 {
1528 hr = StrAllocFormatted(&sczSanitizedArgument, L"%ls=*****", sczVariableName);
1529 ExitOnFailure(hr, "Failed to copy sanitized argument.");
1530 }
1531 }
1532 }
1533
1534 // Remember command-line switch to pass off to UX.
1535 if (fUnknownArg)
1536 {
1537 PathCommandLineAppend(&pCommand->wzCommandLine, argv[i]);
1538 }
1539
1540 if (sczSanitizedArgument)
1541 {
1542 PathCommandLineAppend(psczSanitizedCommandLine, sczSanitizedArgument);
1543 }
1544 else
1545 {
1546 for (; originalIndex <= i; ++originalIndex)
1547 {
1548 PathCommandLineAppend(psczSanitizedCommandLine, argv[originalIndex]);
1549 }
1550 }
1551 }
1552
1553 // If embedded, ensure the display goes embedded as well.
1554 if (BURN_MODE_EMBEDDED == *pMode)
1555 {
1556 pCommand->display = BOOTSTRAPPER_DISPLAY_EMBEDDED;
1557 }
1558
1559 // Set the defaults if nothing was set above.
1560 if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action)
1561 {
1562 pCommand->action = BOOTSTRAPPER_ACTION_INSTALL;
1563 }
1564
1565 if (BOOTSTRAPPER_DISPLAY_UNKNOWN == pCommand->display)
1566 {
1567 pCommand->display = BOOTSTRAPPER_DISPLAY_FULL;
1568 }
1569
1570 if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart)
1571 {
1572 pCommand->restart = BOOTSTRAPPER_RESTART_PROMPT;
1573 }
1574
1575LExit:
1576 ReleaseStr(sczVariableName);
1577 ReleaseStr(sczSanitizedArgument);
1578 ReleaseStr(sczCommandLine);
1579
1580 return hr;
1581}
1582
1583static HRESULT ParsePipeConnection(
1584 __in_ecount(3) LPWSTR* rgArgs,
1585 __in BURN_PIPE_CONNECTION* pConnection
1586 )
1587{
1588 HRESULT hr = S_OK;
1589
1590 hr = StrAllocString(&pConnection->sczName, rgArgs[0], 0);
1591 ExitOnFailure(hr, "Failed to copy connection name from command line.");
1592
1593 hr = StrAllocString(&pConnection->sczSecret, rgArgs[1], 0);
1594 ExitOnFailure(hr, "Failed to copy connection secret from command line.");
1595
1596 hr = StrStringToUInt32(rgArgs[2], 0, reinterpret_cast<UINT*>(&pConnection->dwProcessId));
1597 ExitOnFailure(hr, "Failed to copy parent process id from command line.");
1598
1599LExit:
1600 return hr;
1601}
1602
1603static HRESULT DetectPackage(
1604 __in BURN_ENGINE_STATE* pEngineState,
1605 __in BURN_PACKAGE* pPackage
1606 )
1607{
1608 HRESULT hr = S_OK;
1609 BOOL fBegan = FALSE;
1610
1611 fBegan = TRUE;
1612 hr = UserExperienceOnDetectPackageBegin(&pEngineState->userExperience, pPackage->sczId);
1613 ExitOnRootFailure(hr, "BA aborted detect package begin.");
1614
1615 // Detect the cache state of the package.
1616 hr = DetectPackagePayloadsCached(pPackage);
1617 ExitOnFailure(hr, "Failed to detect if payloads are all cached for package: %ls", pPackage->sczId);
1618
1619 // Use the correct engine to detect the package.
1620 switch (pPackage->type)
1621 {
1622 case BURN_PACKAGE_TYPE_EXE:
1623 hr = ExeEngineDetectPackage(pPackage, &pEngineState->variables);
1624 break;
1625
1626 case BURN_PACKAGE_TYPE_MSI:
1627 hr = MsiEngineDetectPackage(pPackage, &pEngineState->userExperience);
1628 break;
1629
1630 case BURN_PACKAGE_TYPE_MSP:
1631 hr = MspEngineDetectPackage(pPackage, &pEngineState->userExperience);
1632 break;
1633
1634 case BURN_PACKAGE_TYPE_MSU:
1635 hr = MsuEngineDetectPackage(pPackage, &pEngineState->variables);
1636 break;
1637
1638 default:
1639 hr = E_NOTIMPL;
1640 ExitOnRootFailure(hr, "Package type not supported by detect yet.");
1641 }
1642
1643LExit:
1644 if (FAILED(hr))
1645 {
1646 LogErrorId(hr, MSG_FAILED_DETECT_PACKAGE, pPackage->sczId, NULL, NULL);
1647 }
1648
1649 if (fBegan)
1650 {
1651 UserExperienceOnDetectPackageComplete(&pEngineState->userExperience, pPackage->sczId, hr, pPackage->currentState, pPackage->fCached);
1652 }
1653
1654 return hr;
1655}
1656
1657static HRESULT DetectPackagePayloadsCached(
1658 __in BURN_PACKAGE* pPackage
1659 )
1660{
1661 HRESULT hr = S_OK;
1662 LPWSTR sczCachePath = NULL;
1663 BOOL fCached = FALSE; // assume the package is not cached.
1664 LPWSTR sczPayloadCachePath = NULL;
1665
1666 if (pPackage->sczCacheId && *pPackage->sczCacheId)
1667 {
1668 hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &sczCachePath);
1669 ExitOnFailure(hr, "Failed to get completed cache path.");
1670
1671 // If the cached directory exists, we have something.
1672 if (DirExists(sczCachePath, NULL))
1673 {
1674 // Check all payloads to see if any exist.
1675 for (DWORD i = 0; i < pPackage->payloads.cItems; ++i)
1676 {
1677 BURN_PAYLOAD* pPayload = pPackage->payloads.rgItems[i].pPayload;
1678
1679 hr = PathConcat(sczCachePath, pPayload->sczFilePath, &sczPayloadCachePath);
1680 ExitOnFailure(hr, "Failed to concat payload cache path.");
1681
1682 if (FileExistsEx(sczPayloadCachePath, NULL))
1683 {
1684 fCached = TRUE;
1685 break;
1686 }
1687 else
1688 {
1689 LogId(REPORT_STANDARD, MSG_DETECT_PACKAGE_NOT_FULLY_CACHED, pPackage->sczId, pPayload->sczKey);
1690 }
1691 }
1692 }
1693 }
1694
1695 pPackage->fCached = fCached;
1696
1697 if (pPackage->fCanAffectRegistration)
1698 {
1699 pPackage->cacheRegistrationState = pPackage->fCached ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
1700 }
1701
1702LExit:
1703 ReleaseStr(sczPayloadCachePath);
1704 ReleaseStr(sczCachePath);
1705 return hr;
1706}
1707
1708static DWORD WINAPI CacheThreadProc(
1709 __in LPVOID lpThreadParameter
1710 )
1711{
1712 HRESULT hr = S_OK;
1713 BURN_CACHE_THREAD_CONTEXT* pContext = reinterpret_cast<BURN_CACHE_THREAD_CONTEXT*>(lpThreadParameter);
1714 BURN_ENGINE_STATE* pEngineState = pContext->pEngineState;
1715 DWORD* pcOverallProgressTicks = pContext->pcOverallProgressTicks;
1716 BOOL* pfRollback = pContext->pfRollback;
1717 BOOL fComInitialized = FALSE;
1718
1719 // initialize COM
1720 hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
1721 ExitOnFailure(hr, "Failed to initialize COM on cache thread.");
1722 fComInitialized = TRUE;
1723
1724 // cache packages
1725 hr = ApplyCache(pEngineState->section.hSourceEngineFile, &pEngineState->userExperience, &pEngineState->variables, &pEngineState->plan, pEngineState->companionConnection.hCachePipe, pcOverallProgressTicks, pfRollback);
1726
1727LExit:
1728 UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that cache completed.
1729
1730 if (fComInitialized)
1731 {
1732 ::CoUninitialize();
1733 }
1734
1735 return (DWORD)hr;
1736}
1737
1738static HRESULT WaitForCacheThread(
1739 __in HANDLE hCacheThread
1740 )
1741{
1742 HRESULT hr = S_OK;
1743
1744 if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, INFINITE))
1745 {
1746 ExitWithLastError(hr, "Failed to wait for cache thread to terminate.");
1747 }
1748
1749 if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr))
1750 {
1751 ExitWithLastError(hr, "Failed to get cache thread exit code.");
1752 }
1753
1754LExit:
1755 return hr;
1756}
1757
1758static void LogPackages(
1759 __in_opt const BURN_PACKAGE* pUpgradeBundlePackage,
1760 __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage,
1761 __in const BURN_PACKAGES* pPackages,
1762 __in const BURN_RELATED_BUNDLES* pRelatedBundles,
1763 __in const BOOTSTRAPPER_ACTION action
1764 )
1765{
1766 if (pUpgradeBundlePackage)
1767 {
1768 LogId(REPORT_STANDARD, MSG_PLANNED_UPGRADE_BUNDLE, pUpgradeBundlePackage->sczId, LoggingRequestStateToString(pUpgradeBundlePackage->defaultRequested), LoggingRequestStateToString(pUpgradeBundlePackage->requested), LoggingActionStateToString(pUpgradeBundlePackage->execute), LoggingActionStateToString(pUpgradeBundlePackage->rollback), LoggingDependencyActionToString(pUpgradeBundlePackage->dependencyExecute));
1769 }
1770 else if (pForwardCompatibleBundlePackage)
1771 {
1772 LogId(REPORT_STANDARD, MSG_PLANNED_FORWARD_COMPATIBLE_BUNDLE, pForwardCompatibleBundlePackage->sczId, LoggingRequestStateToString(pForwardCompatibleBundlePackage->defaultRequested), LoggingRequestStateToString(pForwardCompatibleBundlePackage->requested), LoggingActionStateToString(pForwardCompatibleBundlePackage->execute), LoggingActionStateToString(pForwardCompatibleBundlePackage->rollback), LoggingDependencyActionToString(pForwardCompatibleBundlePackage->dependencyExecute));
1773 }
1774 else
1775 {
1776 // Display related bundles first if uninstalling.
1777 if (BOOTSTRAPPER_ACTION_UNINSTALL == action)
1778 {
1779 LogRelatedBundles(pRelatedBundles, TRUE);
1780 }
1781
1782 // Display all the packages in the log.
1783 for (DWORD i = 0; i < pPackages->cPackages; ++i)
1784 {
1785 const DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? pPackages->cPackages - 1 - i : i;
1786 const BURN_PACKAGE* pPackage = &pPackages->rgPackages[iPackage];
1787
1788 LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fPlannedCache), LoggingBoolToString(pPackage->fPlannedUncache), LoggingDependencyActionToString(pPackage->dependencyExecute), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedInstallRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedCacheRegistrationState));
1789
1790 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
1791 {
1792 if (pPackage->Msi.cFeatures)
1793 {
1794 LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURES, pPackage->Msi.cFeatures, pPackage->sczId);
1795
1796 for (DWORD j = 0; j < pPackage->Msi.cFeatures; ++j)
1797 {
1798 const BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[j];
1799
1800 LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURE, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState), LoggingMsiFeatureStateToString(pFeature->defaultRequested), LoggingMsiFeatureStateToString(pFeature->requested), LoggingMsiFeatureActionToString(pFeature->execute), LoggingMsiFeatureActionToString(pFeature->rollback));
1801 }
1802 }
1803
1804 if (pPackage->Msi.cSlipstreamMspPackages)
1805 {
1806 LogId(REPORT_STANDARD, MSG_PLANNED_SLIPSTREAMED_MSP_TARGETS, pPackage->Msi.cSlipstreamMspPackages, pPackage->sczId);
1807
1808 for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j)
1809 {
1810 const BURN_SLIPSTREAM_MSP* pSlipstreamMsp = &pPackage->Msi.rgSlipstreamMsps[j];
1811
1812 LogId(REPORT_STANDARD, MSG_PLANNED_SLIPSTREAMED_MSP_TARGET, pSlipstreamMsp->pMspPackage->sczId, LoggingActionStateToString(pSlipstreamMsp->execute), LoggingActionStateToString(pSlipstreamMsp->rollback));
1813 }
1814 }
1815 }
1816 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.cTargetProductCodes)
1817 {
1818 LogId(REPORT_STANDARD, MSG_PLANNED_MSP_TARGETS, pPackage->Msp.cTargetProductCodes, pPackage->sczId);
1819
1820 for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j)
1821 {
1822 const BURN_MSPTARGETPRODUCT* pTargetProduct = &pPackage->Msp.rgTargetProducts[j];
1823
1824 LogId(REPORT_STANDARD, MSG_PLANNED_MSP_TARGET, pTargetProduct->wzTargetProductCode, LoggingPackageStateToString(pTargetProduct->patchPackageState), LoggingRequestStateToString(pTargetProduct->defaultRequested), LoggingRequestStateToString(pTargetProduct->requested), LoggingMspTargetActionToString(pTargetProduct->execute, pTargetProduct->executeSkip), LoggingMspTargetActionToString(pTargetProduct->rollback, pTargetProduct->rollbackSkip));
1825 }
1826 }
1827 }
1828
1829 // Display related bundles last if caching, installing, modifying, or repairing.
1830 if (BOOTSTRAPPER_ACTION_UNINSTALL < action)
1831 {
1832 LogRelatedBundles(pRelatedBundles, FALSE);
1833 }
1834 }
1835}
1836
1837static void LogRelatedBundles(
1838 __in const BURN_RELATED_BUNDLES* pRelatedBundles,
1839 __in BOOL fReverse
1840 )
1841{
1842 if (0 < pRelatedBundles->cRelatedBundles)
1843 {
1844 for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i)
1845 {
1846 const DWORD iRelatedBundle = fReverse ? pRelatedBundles->cRelatedBundles - 1 - i : i;
1847 const BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + iRelatedBundle;
1848 const BURN_PACKAGE* pPackage = &pRelatedBundle->package;
1849
1850 if (pRelatedBundle->fPlannable)
1851 {
1852 LogId(REPORT_STANDARD, MSG_PLANNED_RELATED_BUNDLE, pPackage->sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingDependencyActionToString(pPackage->dependencyExecute));
1853 }
1854 }
1855 }
1856}