main
cpp 2,432 lines 95 KB
Raw
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
8 struct BURN_CACHE_THREAD_CONTEXT
9 {
10 BURN_ENGINE_STATE* pEngineState;
11 BURN_APPLY_CONTEXT* pApplyContext;
12 };
13
14
15 static PFN_CREATEPROCESSW vpfnCreateProcessW = ::CreateProcessW;
16 static PFN_PROCWAITFORCOMPLETION vpfnProcWaitForCompletion = ProcWaitForCompletion;
17
18
19 // internal function declarations
20
21 static HRESULT CoreRecreateCommandLine(
22 __deref_inout_z LPWSTR* psczCommandLine,
23 __in BOOTSTRAPPER_ACTION action,
24 __in BURN_ENGINE_COMMAND* pInternalCommand,
25 __in BOOTSTRAPPER_COMMAND* pCommand,
26 __in BOOTSTRAPPER_RELATION_TYPE relationType,
27 __in BOOL fPassthrough
28 );
29 static HRESULT AppendEscapedArgumentToCommandLine(
30 __in_z LPCWSTR wzEscapedArgument,
31 __deref_inout_z LPWSTR* psczCommandLine,
32 __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine
33 );
34 static HRESULT EscapeAndAppendArgumentToCommandLineFormatted(
35 __deref_inout_z LPWSTR* psczCommandLine,
36 __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine,
37 __in __format_string LPCWSTR wzFormat,
38 ...
39 );
40 static HRESULT EscapeAndAppendArgumentToCommandLineFormattedArgs(
41 __deref_inout_z LPWSTR* psczCommandLine,
42 __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine,
43 __in __format_string LPCWSTR wzFormat,
44 __in va_list args
45 );
46 static HRESULT GetSanitizedCommandLine(
47 __in BURN_ENGINE_COMMAND* pInternalCommand,
48 __in BOOTSTRAPPER_COMMAND* pCommand,
49 __in BURN_VARIABLES* pVariables,
50 __inout_z LPWSTR* psczSanitizedCommandLine
51 );
52 static HRESULT ParsePipeConnection(
53 __in_ecount(3) LPWSTR* rgArgs,
54 __in BURN_PIPE_CONNECTION* pConnection
55 );
56 static HRESULT DetectPackage(
57 __in BURN_ENGINE_STATE* pEngineState,
58 __in BURN_PACKAGE* pPackage
59 );
60 static HRESULT DetectPackagePayloadsCached(
61 __in BURN_CACHE* pCache,
62 __in BURN_PACKAGE* pPackage
63 );
64 static DWORD WINAPI CacheThreadProc(
65 __in LPVOID lpThreadParameter
66 );
67 static DWORD WINAPI LoggingThreadProc(
68 __in LPVOID lpThreadParameter
69 );
70 static void LogPackages(
71 __in_opt const BURN_PACKAGE* pUpgradeBundlePackage,
72 __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage,
73 __in const BURN_PACKAGES* pPackages,
74 __in const BURN_RELATED_BUNDLES* pRelatedBundles,
75 __in const BOOTSTRAPPER_ACTION action
76 );
77 static void LogRelatedBundles(
78 __in const BURN_RELATED_BUNDLES* pRelatedBundles,
79 __in BOOL fReverse
80 );
81 static void LogRollbackBoundary(
82 __in const BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
83 );
84
85
86 // function definitions
87
88 extern "C" HRESULT CoreInitialize(
89 __in BURN_ENGINE_STATE* pEngineState
90 )
91 {
92 HRESULT hr = S_OK;
93 LPWSTR sczSanitizedCommandLine = NULL;
94 LPWSTR sczStreamName = NULL;
95 BYTE* pbBuffer = NULL;
96 SIZE_T cbBuffer = 0;
97 BURN_CONTAINER_CONTEXT containerContext = { };
98 LPWSTR sczSourceProcessFolder = NULL;
99
100 // Initialize variables.
101 hr = VariableInitialize(&pEngineState->variables);
102 ExitOnFailure(hr, "Failed to initialize variables.");
103
104 // Open attached UX container.
105 hr = ContainerOpenUX(&pEngineState->section, &containerContext);
106 ExitOnFailure(hr, "Failed to open attached UX container.");
107
108 // Load manifest.
109 hr = ContainerNextStream(&containerContext, &sczStreamName);
110 ExitOnFailure(hr, "Failed to open manifest stream.");
111
112 hr = ContainerStreamToBuffer(&containerContext, &pbBuffer, &cbBuffer);
113 ExitOnFailure(hr, "Failed to get manifest stream from container.");
114
115 hr = ManifestLoadXmlFromBuffer(pbBuffer, cbBuffer, pEngineState);
116 ExitOnFailure(hr, "Failed to load manifest.");
117
118 hr = ContainersInitialize(&pEngineState->containers, &pEngineState->section);
119 ExitOnFailure(hr, "Failed to initialize containers.");
120
121 hr = GetSanitizedCommandLine(&pEngineState->internalCommand, &pEngineState->command, &pEngineState->variables, &sczSanitizedCommandLine);
122 ExitOnFailure(hr, "Fatal error while sanitizing command line.");
123
124 LogId(REPORT_STANDARD, MSG_BURN_COMMAND_LINE, sczSanitizedCommandLine ? sczSanitizedCommandLine : L"");
125
126 // The command line wasn't logged immediately so that hidden variables set on the command line can be obscured in the log.
127 // This delay creates issues when troubleshooting parsing errors because the original command line is not in the log.
128 // The code does its best to process the entire command line and keep track if the command line was invalid so that it can log the sanitized command line before erroring out.
129 if (pEngineState->internalCommand.fInvalidCommandLine)
130 {
131 LogExitOnRootFailure(hr = E_INVALIDARG, MSG_FAILED_PARSE_COMMAND_LINE, "Failed to parse command line.");
132 }
133
134 hr = CoreInitializeConstants(pEngineState);
135 ExitOnFailure(hr, "Failed to initialize contants.");
136
137 hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, pEngineState->internalCommand.fInitiallyElevated, TRUE);
138 ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED);
139
140 hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_UILEVEL, pEngineState->command.display, TRUE);
141 ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_UILEVEL);
142
143 if (pEngineState->internalCommand.sczActiveParent && *pEngineState->internalCommand.sczActiveParent)
144 {
145 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_ACTIVE_PARENT, pEngineState->internalCommand.sczActiveParent, TRUE, FALSE);
146 ExitOnFailure(hr, "Failed to overwrite the bundle active parent built-in variable.");
147 }
148
149 // Set BURN_BUNDLE_ORIGINAL_SOURCE, if it was passed in on the command line.
150 // Needs to be done after ManifestLoadXmlFromBuffer.
151 if (pEngineState->internalCommand.sczOriginalSource)
152 {
153 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, pEngineState->internalCommand.sczOriginalSource, FALSE, FALSE);
154 ExitOnFailure(hr, "Failed to set original source variable.");
155 }
156
157 if (BURN_MODE_NORMAL == pEngineState->internalCommand.mode || BURN_MODE_EMBEDDED == pEngineState->internalCommand.mode)
158 {
159 hr = CacheInitializeSources(&pEngineState->cache, &pEngineState->registration, &pEngineState->variables);
160 ExitOnFailure(hr, "Failed to initialize internal cache source functionality.");
161 }
162
163 // If we're not elevated then we'll be loading the bootstrapper application, so extract
164 // the payloads from the BA container.
165 if (BURN_MODE_NORMAL == pEngineState->internalCommand.mode || BURN_MODE_EMBEDDED == pEngineState->internalCommand.mode)
166 {
167 // Extract all UX payloads to working folder.
168 hr = BootstrapperApplicationEnsureWorkingFolder(pEngineState->internalCommand.fInitiallyElevated, &pEngineState->cache, &pEngineState->userExperience.sczTempDirectory);
169 ExitOnFailure(hr, "Failed to get unique temporary folder for bootstrapper application.");
170
171 hr = PayloadExtractUXContainer(&pEngineState->userExperience.payloads, &containerContext, pEngineState->userExperience.sczTempDirectory);
172 ExitOnFailure(hr, "Failed to extract bootstrapper application payloads.");
173
174 hr = PathConcat(pEngineState->userExperience.sczTempDirectory, L"BootstrapperApplicationData.xml", &pEngineState->command.wzBootstrapperApplicationDataPath);
175 ExitOnFailure(hr, "Failed to get BootstrapperApplicationDataPath.");
176
177 hr = StrAllocString(&pEngineState->command.wzBootstrapperWorkingFolder, pEngineState->userExperience.sczTempDirectory, 0);
178 ExitOnFailure(hr, "Failed to copy sczBootstrapperWorkingFolder.");
179 }
180
181 LExit:
182 ReleaseStr(sczSourceProcessFolder);
183 ContainerClose(&containerContext);
184 ReleaseStr(sczStreamName);
185 ReleaseStr(sczSanitizedCommandLine);
186 ReleaseMem(pbBuffer);
187
188 return hr;
189 }
190
191 extern "C" HRESULT CoreInitializeConstants(
192 __in BURN_ENGINE_STATE* pEngineState
193 )
194 {
195 HRESULT hr = S_OK;
196 BURN_ENGINE_COMMAND* pInternalCommand = &pEngineState->internalCommand;
197 BURN_REGISTRATION* pRegistration = &pEngineState->registration;
198
199 hr = DependencyInitialize(pInternalCommand, &pEngineState->dependencies, pRegistration);
200 ExitOnFailure(hr, "Failed to initialize dependency data.");
201
202 // Support passing Ancestors to embedded burn bundles.
203 if (pInternalCommand->sczAncestors && *pInternalCommand->sczAncestors)
204 {
205 hr = StrAllocFormatted(&pRegistration->sczBundlePackageAncestors, L"%ls;%ls", pInternalCommand->sczAncestors, pRegistration->sczId);
206 ExitOnFailure(hr, "Failed to copy ancestors and self to bundle package ancestors.");
207 }
208 else
209 {
210 hr = StrAllocString(&pRegistration->sczBundlePackageAncestors, pRegistration->sczId, 0);
211 ExitOnFailure(hr, "Failed to copy self to bundle package ancestors.");
212 }
213
214 for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i)
215 {
216 BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i;
217
218 if (BURN_PACKAGE_TYPE_BUNDLE == pPackage->type)
219 {
220 // Pass along any ancestors and ourself to prevent infinite loops.
221 pPackage->Bundle.wzAncestors = pRegistration->sczBundlePackageAncestors;
222 pPackage->Bundle.wzEngineWorkingDirectory = pInternalCommand->sczEngineWorkingDirectory;
223 }
224 else if (BURN_PACKAGE_TYPE_EXE == pPackage->type && pPackage->Exe.fBundle)
225 {
226 pPackage->Exe.wzAncestors = pRegistration->sczBundlePackageAncestors;
227 pPackage->Exe.wzEngineWorkingDirectory = pInternalCommand->sczEngineWorkingDirectory;
228 }
229 }
230
231 LExit:
232 return hr;
233 }
234
235 extern "C" HRESULT CoreSerializeEngineState(
236 __in BURN_ENGINE_STATE* pEngineState,
237 __inout BYTE** ppbBuffer,
238 __inout SIZE_T* piBuffer
239 )
240 {
241 HRESULT hr = S_OK;
242
243 hr = VariableSerialize(&pEngineState->variables, TRUE, ppbBuffer, piBuffer);
244 ExitOnFailure(hr, "Failed to serialize variables.");
245
246 LExit:
247 return hr;
248 }
249
250 extern "C" HRESULT CoreQueryRegistration(
251 __in BURN_ENGINE_STATE* pEngineState
252 )
253 {
254 HRESULT hr = S_OK;
255 BYTE* pbBuffer = NULL;
256 SIZE_T cbBuffer = 0;
257 SIZE_T iBuffer = 0;
258
259 // detect resume type
260 hr = RegistrationDetectResumeType(&pEngineState->registration, &pEngineState->command.resumeType);
261 ExitOnFailure(hr, "Failed to detect resume type.");
262
263 // If we have a resume mode that suggests the bundle might already be present, try to load any
264 // previously stored state.
265 if (BOOTSTRAPPER_RESUME_TYPE_INVALID < pEngineState->command.resumeType)
266 {
267 // load resume state
268 hr = RegistrationLoadState(&pEngineState->registration, &pbBuffer, &cbBuffer);
269 if (SUCCEEDED(hr))
270 {
271 hr = VariableDeserialize(&pEngineState->variables, TRUE, pbBuffer, cbBuffer, &iBuffer);
272 }
273
274 // Log any failures and continue.
275 if (FAILED(hr))
276 {
277 LogId(REPORT_STANDARD, MSG_CANNOT_LOAD_STATE_FILE, hr, pEngineState->registration.sczStateFile);
278 hr = S_OK;
279 }
280 }
281
282 LExit:
283 ReleaseMem(pbBuffer);
284
285 return hr;
286 }
287
288 extern "C" HRESULT CoreDetect(
289 __in BURN_ENGINE_STATE* pEngineState,
290 __in_opt HWND hwndParent
291 )
292 {
293 HRESULT hr = S_OK;
294 BOOL fDetectBegan = FALSE;
295 BURN_PACKAGE* pPackage = NULL;
296 HRESULT hrFirstPackageFailure = S_OK;
297
298 LogId(REPORT_STANDARD, MSG_DETECT_BEGIN, pEngineState->packages.cPackages);
299
300 // Always reset the detect state which means the plan should be reset too.
301 pEngineState->fDetected = FALSE;
302 pEngineState->fPlanned = FALSE;
303 DetectReset(&pEngineState->registration, &pEngineState->packages);
304 PlanReset(&pEngineState->plan, &pEngineState->variables, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads);
305
306 hr = RegistrationSetDynamicVariables(&pEngineState->registration, &pEngineState->variables);
307 ExitOnFailure(hr, "Failed to reset the dynamic registration variables during detect.");
308
309 fDetectBegan = TRUE;
310 hr = BACallbackOnDetectBegin(&pEngineState->userExperience, pEngineState->registration.fCached, pEngineState->registration.detectedRegistrationType, pEngineState->packages.cPackages);
311 ExitOnRootFailure(hr, "UX aborted detect begin.");
312
313 pEngineState->userExperience.hwndDetect = hwndParent;
314
315 hr = SearchesExecute(&pEngineState->searches, &pEngineState->variables);
316 ExitOnFailure(hr, "Failed to execute searches.");
317
318 hr = DependencyDetectBundle(&pEngineState->dependencies, &pEngineState->registration);
319 ExitOnFailure(hr, "Failed to detect the dependencies.");
320
321 // Load all of the related bundles.
322 hr = RegistrationDetectRelatedBundles(&pEngineState->registration);
323 ExitOnFailure(hr, "Failed to detect related bundles.");
324
325 hr = DetectForwardCompatibleBundles(&pEngineState->userExperience, &pEngineState->registration);
326 ExitOnFailure(hr, "Failed to detect forward compatible bundle.");
327
328 // Report the related bundles.
329 hr = DetectReportRelatedBundles(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->registration.fEligibleForCleanup);
330 ExitOnFailure(hr, "Failed to report detected related bundles.");
331
332 // Do update detection.
333 hr = DetectUpdate(pEngineState->registration.sczId, &pEngineState->userExperience, &pEngineState->update);
334 ExitOnFailure(hr, "Failed to detect update.");
335
336 // Detecting MSPs requires special initialization before processing each package but
337 // only do the detection if there are actually patch packages to detect because it
338 // can be expensive.
339 if (pEngineState->packages.cPatchInfo)
340 {
341 hr = MspEngineDetectInitialize(&pEngineState->packages);
342 ExitOnFailure(hr, "Failed to initialize MSP engine detection.");
343
344 hr = MsiEngineDetectInitialize(&pEngineState->packages);
345 ExitOnFailure(hr, "Failed to initialize MSI engine detection.");
346 }
347
348 for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i)
349 {
350 pPackage = pEngineState->packages.rgPackages + i;
351
352 hr = DetectPackage(pEngineState, pPackage);
353
354 // If the package detection failed, ensure the package state is set to unknown.
355 if (FAILED(hr))
356 {
357 if (SUCCEEDED(hrFirstPackageFailure))
358 {
359 hrFirstPackageFailure = hr;
360 }
361
362 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
363 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
364 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
365 pPackage->compatiblePackage.fDetected = FALSE;
366 }
367 }
368
369 // Log the detected states.
370 for (DWORD iPackage = 0; iPackage < pEngineState->packages.cPackages; ++iPackage)
371 {
372 pPackage = pEngineState->packages.rgPackages + iPackage;
373
374 // If any packages that can affect registration are present, then the bundle should not automatically be uninstalled.
375 if (pEngineState->registration.fEligibleForCleanup && pPackage->fCanAffectRegistration &&
376 (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState ||
377 BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState))
378 {
379 pEngineState->registration.fEligibleForCleanup = FALSE;
380 }
381
382 LogId(REPORT_STANDARD, MSG_DETECTED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingBoolToString(pPackage->fCached), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState));
383
384 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
385 {
386 for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature)
387 {
388 const BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature;
389 LogId(REPORT_STANDARD, MSG_DETECTED_MSI_FEATURE, pPackage->sczId, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState));
390 }
391 }
392 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
393 {
394 for (DWORD iTargetProduct = 0; iTargetProduct < pPackage->Msp.cTargetProductCodes; ++iTargetProduct)
395 {
396 const BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + iTargetProduct;
397 LogId(REPORT_STANDARD, MSG_DETECTED_MSP_TARGET, pPackage->sczId, pTargetProduct->wzTargetProductCode, LoggingPackageStateToString(pTargetProduct->patchPackageState));
398 }
399 }
400 }
401
402 LExit:
403 if (SUCCEEDED(hr))
404 {
405 hr = hrFirstPackageFailure;
406 }
407
408 if (SUCCEEDED(hr))
409 {
410 pEngineState->fDetected = TRUE;
411 }
412
413 if (fDetectBegan)
414 {
415 BACallbackOnDetectComplete(&pEngineState->userExperience, hr, pEngineState->registration.fEligibleForCleanup);
416 }
417
418 pEngineState->userExperience.hwndDetect = NULL;
419
420 LogId(REPORT_STANDARD, MSG_DETECT_COMPLETE, hr, !fDetectBegan ? "(failed)" : LoggingRegistrationTypeToString(pEngineState->registration.detectedRegistrationType), !fDetectBegan ? "(failed)" : LoggingBoolToString(pEngineState->registration.fCached), FAILED(hr) ? "(failed)" : LoggingBoolToString(pEngineState->registration.fEligibleForCleanup));
421
422 return hr;
423 }
424
425 extern "C" HRESULT CorePlan(
426 __in BURN_ENGINE_STATE* pEngineState,
427 __in BOOTSTRAPPER_ACTION action
428 )
429 {
430 HRESULT hr = S_OK;
431 BOOL fPlanBegan = FALSE;
432 BURN_PACKAGE* pUpgradeBundlePackage = NULL;
433 BURN_PACKAGE* pForwardCompatibleBundlePackage = NULL;
434 BOOL fContinuePlanning = TRUE; // assume we won't skip planning due to dependencies.
435
436 LogId(REPORT_STANDARD, MSG_PLAN_BEGIN, pEngineState->packages.cPackages, LoggingBurnActionToString(action));
437
438 fPlanBegan = TRUE;
439 hr = BACallbackOnPlanBegin(&pEngineState->userExperience, pEngineState->packages.cPackages);
440 ExitOnRootFailure(hr, "BA aborted plan begin.");
441
442 if (!pEngineState->fDetected)
443 {
444 ExitWithRootFailure(hr, E_INVALIDSTATE, "Plan cannot be done without a successful Detect.");
445 }
446 else if (pEngineState->plan.fAffectedMachineState)
447 {
448 ExitWithRootFailure(hr, E_INVALIDSTATE, "Plan requires a new successful Detect after calling Apply.");
449 }
450
451 // Always reset the plan.
452 pEngineState->fPlanned = FALSE;
453 PlanReset(&pEngineState->plan, &pEngineState->variables, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads);
454
455 hr = PlanSetVariables(action, &pEngineState->variables);
456 ExitOnFailure(hr, "Failed to update action.");
457
458 // Remember the overall action state in the plan since it shapes the changes
459 // we make everywhere.
460 pEngineState->plan.action = action;
461 pEngineState->plan.pCache = &pEngineState->cache;
462 pEngineState->plan.pCommand = &pEngineState->command;
463 pEngineState->plan.pInternalCommand = &pEngineState->internalCommand;
464 pEngineState->plan.pPayloads = &pEngineState->payloads;
465 pEngineState->plan.wzBundleId = pEngineState->registration.sczId;
466 pEngineState->plan.wzBundleProviderKey = pEngineState->registration.sczId;
467 pEngineState->plan.fDisableRollback = pEngineState->fDisableRollback || BOOTSTRAPPER_ACTION_UNSAFE_UNINSTALL == pEngineState->plan.action;
468 pEngineState->plan.fPlanPackageCacheRollback = BOOTSTRAPPER_REGISTRATION_TYPE_NONE == pEngineState->registration.detectedRegistrationType;
469
470 // Set resume commandline
471 hr = PlanSetResumeCommand(&pEngineState->plan, &pEngineState->registration, &pEngineState->log);
472 ExitOnFailure(hr, "Failed to set resume command");
473
474 hr = DependencyPlanInitialize(&pEngineState->dependencies, &pEngineState->plan);
475 ExitOnFailure(hr, "Failed to initialize the dependencies for the plan.");
476
477 hr = RegistrationPlanInitialize(&pEngineState->registration);
478 ExitOnFailure(hr, "Failed to initialize registration for the plan.");
479
480 if (BOOTSTRAPPER_ACTION_LAYOUT == action)
481 {
482 Assert(!pEngineState->plan.fPerMachine);
483
484 // Plan the bundle's layout.
485 hr = PlanLayoutBundle(&pEngineState->plan, pEngineState->registration.sczExecutableName, pEngineState->section.qwBundleSize, &pEngineState->variables, &pEngineState->layoutPayloads);
486 ExitOnFailure(hr, "Failed to plan the layout of the bundle.");
487
488 // Plan the packages' layout.
489 hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables);
490 ExitOnFailure(hr, "Failed to plan packages.");
491 }
492 else if (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED == action)
493 {
494 Assert(!pEngineState->plan.fPerMachine);
495
496 pUpgradeBundlePackage = &pEngineState->update.package;
497
498 hr = PlanUpdateBundle(&pEngineState->userExperience, pUpgradeBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables);
499 ExitOnFailure(hr, "Failed to plan update.");
500 }
501 else
502 {
503 hr = PlanForwardCompatibleBundles(&pEngineState->userExperience, &pEngineState->plan, &pEngineState->registration);
504 ExitOnFailure(hr, "Failed to plan forward compatible bundles.");
505
506 if (pEngineState->plan.fEnabledForwardCompatibleBundle)
507 {
508 Assert(!pEngineState->plan.fPerMachine);
509
510 pForwardCompatibleBundlePackage = &pEngineState->plan.forwardCompatibleBundle;
511
512 hr = PlanPassThroughBundle(&pEngineState->userExperience, pForwardCompatibleBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables);
513 ExitOnFailure(hr, "Failed to plan passthrough.");
514 }
515 else // doing an action that modifies the machine state.
516 {
517 pEngineState->plan.fPerMachine = pEngineState->registration.fPerMachine; // default the scope of the plan to the per-machine state of the bundle.
518
519 hr = PlanRelatedBundlesInitialize(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan);
520 ExitOnFailure(hr, "Failed to initialize related bundles for plan.");
521
522 if (pEngineState->plan.fDowngrade)
523 {
524 fContinuePlanning = FALSE;
525 }
526 else
527 {
528 hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, &pEngineState->dependencies, pEngineState->command.resumeType, pEngineState->command.relationType, &fContinuePlanning);
529 ExitOnFailure(hr, "Failed to plan registration.");
530 }
531
532 if (fContinuePlanning)
533 {
534 // Remember the early index, because we want to be able to insert some related bundles
535 // into the plan before other executed packages. This particularly occurs for uninstallation
536 // of addons and patches, which should be uninstalled before the main product.
537 DWORD dwExecuteActionEarlyIndex = pEngineState->plan.cExecuteActions;
538
539 // Plan the related bundles first to support downgrades with ref-counting.
540 hr = PlanRelatedBundlesBegin(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan);
541 ExitOnFailure(hr, "Failed to plan related bundles.");
542
543 hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables);
544 ExitOnFailure(hr, "Failed to plan packages.");
545
546 // Schedule the update of related bundles last.
547 hr = PlanRelatedBundlesComplete(&pEngineState->userExperience, &pEngineState->registration, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, dwExecuteActionEarlyIndex);
548 ExitOnFailure(hr, "Failed to schedule related bundles.");
549 }
550 }
551 }
552
553 if (fContinuePlanning)
554 {
555 // Finally, display all packages and related bundles in the log.
556 LogPackages(pUpgradeBundlePackage, pForwardCompatibleBundlePackage, &pEngineState->packages, &pEngineState->registration.relatedBundles, action);
557 }
558
559 PlanDump(&pEngineState->plan);
560
561 LExit:
562 if (SUCCEEDED(hr))
563 {
564 pEngineState->fPlanned = TRUE;
565 }
566
567 if (fPlanBegan)
568 {
569 BACallbackOnPlanComplete(&pEngineState->userExperience, hr);
570 }
571
572 LogId(REPORT_STANDARD, MSG_PLAN_COMPLETE, hr);
573
574 return hr;
575 }
576
577 extern "C" HRESULT CoreElevate(
578 __in BURN_ENGINE_STATE* pEngineState,
579 __in WM_BURN reason,
580 __in_opt HWND hwndParent
581 )
582 {
583 HRESULT hr = S_OK;
584 DWORD cAVRetryAttempts = 0;
585
586 while (INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe)
587 {
588 // If the elevated companion pipe isn't created yet, let's make that happen.
589 if (!pEngineState->sczBundleEngineWorkingPath)
590 {
591 hr = CacheBundleToWorkingDirectory(pEngineState->internalCommand.fInitiallyElevated, &pEngineState->cache, pEngineState->registration.sczExecutableName, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath);
592 ExitOnFailure(hr, "Failed to cache engine to working directory.");
593 }
594
595 hr = ElevationElevate(pEngineState, reason, hwndParent);
596 if (E_SUSPECTED_AV_INTERFERENCE == hr && 1 > cAVRetryAttempts)
597 {
598 ++cAVRetryAttempts;
599 continue;
600 }
601 ExitOnFailure(hr, "Failed to actually elevate.");
602
603 hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, TRUE, TRUE);
604 ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED);
605
606 pEngineState->hUnelevatedLoggingThread = ::CreateThread(NULL, 0, LoggingThreadProc, pEngineState, 0, NULL);
607 ExitOnNullWithLastError(pEngineState->hUnelevatedLoggingThread, hr, "Failed to create unelevated logging thread.");
608 }
609
610 LExit:
611 return hr;
612 }
613
614 extern "C" HRESULT CoreApply(
615 __in BURN_ENGINE_STATE* pEngineState,
616 __in_opt HWND hwndParent
617 )
618 {
619 HRESULT hr = S_OK;
620 HANDLE hLock = NULL;
621 BOOL fApplyBegan = FALSE;
622 BOOL fApplyInitialize = FALSE;
623 BOOL fElevated = FALSE;
624 BOOL fRegistered = FALSE;
625 BOOL fSuspend = FALSE;
626 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
627 BURN_APPLY_CONTEXT applyContext = { };
628 BOOL fDeleteApplyCs = FALSE;
629 BURN_CACHE_THREAD_CONTEXT cacheThreadContext = { };
630 DWORD dwCacheExitCode = 0;
631 BOOL fRollbackCache = FALSE;
632 DWORD dwPhaseCount = 0;
633 BOOTSTRAPPER_APPLYCOMPLETE_ACTION applyCompleteAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE;
634
635 if (!pEngineState->fPlanned)
636 {
637 ExitOnFailure(hr = E_INVALIDSTATE, "Apply cannot be done without a successful Plan.");
638 }
639 else if (pEngineState->plan.fAffectedMachineState)
640 {
641 ExitOnFailure(hr = E_INVALIDSTATE, "Plans cannot be applied multiple times.");
642 }
643
644 fApplyBegan = TRUE;
645
646 LogId(REPORT_STANDARD, MSG_APPLY_BEGIN);
647
648 // Ensure any previous attempts to execute are reset.
649 ApplyReset(&pEngineState->userExperience, &pEngineState->packages);
650
651 if (pEngineState->plan.cCacheActions)
652 {
653 ++dwPhaseCount;
654 }
655 if (pEngineState->plan.cExecuteActions)
656 {
657 ++dwPhaseCount;
658 }
659
660 hr = BACallbackOnApplyBegin(&pEngineState->userExperience, dwPhaseCount);
661 ExitOnRootFailure(hr, "BA aborted apply begin.");
662
663 if (pEngineState->plan.fDowngrade)
664 {
665 hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION);
666 BACallbackOnApplyDowngrade(&pEngineState->userExperience, &hr);
667
668 ExitFunction();
669 }
670
671 pEngineState->plan.fAffectedMachineState = pEngineState->plan.fCanAffectMachineState;
672
673 hr = ApplyLock(FALSE, &hLock);
674 ExitOnFailure(hr, "Another per-user setup is already executing.");
675
676 pEngineState->plan.fApplying = TRUE;
677
678 // Initialize only after getting a lock.
679 fApplyInitialize = TRUE;
680 ApplyInitialize();
681
682 pEngineState->userExperience.hwndApply = hwndParent;
683
684 hr = ApplySetVariables(&pEngineState->variables);
685 ExitOnFailure(hr, "Failed to set initial apply variables.");
686
687 // If the plan is empty of work to do, skip everything.
688 if (!(pEngineState->plan.cRegistrationActions || pEngineState->plan.cCacheActions || pEngineState->plan.cExecuteActions || pEngineState->plan.cCleanActions))
689 {
690 LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED);
691 ExitFunction();
692 }
693
694 fDeleteApplyCs = TRUE;
695 ::InitializeCriticalSection(&applyContext.csApply);
696
697 // Ensure the engine is cached to the working path.
698 if (!pEngineState->sczBundleEngineWorkingPath)
699 {
700 hr = CacheBundleToWorkingDirectory(pEngineState->internalCommand.fInitiallyElevated, &pEngineState->cache, pEngineState->registration.sczExecutableName, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath);
701 ExitOnFailure(hr, "Failed to cache engine to working directory.");
702 }
703
704 // Elevate.
705 if (pEngineState->plan.fPerMachine)
706 {
707 hr = CoreElevate(pEngineState, WM_BURN_APPLY, pEngineState->userExperience.hwndApply);
708 ExitOnFailure(hr, "Failed to elevate.");
709
710 hr = ElevationApplyInitialize(pEngineState->companionConnection.hPipe, &pEngineState->userExperience, &pEngineState->variables, &pEngineState->plan);
711 ExitOnFailure(hr, "Failed to initialize apply in elevated process.");
712
713 fElevated = TRUE;
714 }
715
716 // Register.
717 if (pEngineState->plan.fCanAffectMachineState)
718 {
719 fRegistered = TRUE;
720 hr = ApplyRegister(pEngineState);
721 ExitOnFailure(hr, "Failed to register bundle.");
722 }
723
724 // Cache.
725 if (pEngineState->plan.cCacheActions)
726 {
727 // Launch the cache thread.
728 cacheThreadContext.pEngineState = pEngineState;
729 cacheThreadContext.pApplyContext = &applyContext;
730
731 applyContext.hCacheThread = ::CreateThread(NULL, 0, CacheThreadProc, &cacheThreadContext, 0, NULL);
732 ExitOnNullWithLastError(applyContext.hCacheThread, hr, "Failed to create cache thread.");
733
734 fRollbackCache = TRUE;
735
736 // If we're not caching in parallel, wait for the cache thread to terminate.
737 if (!pEngineState->fParallelCacheAndExecute)
738 {
739 hr = ThrdWaitForCompletion(applyContext.hCacheThread, INFINITE, &dwCacheExitCode);
740 ExitOnFailure(hr, "Failed to wait for cache thread before execute.");
741
742 hr = (HRESULT)dwCacheExitCode;
743 ExitOnFailure(hr, "Failed while caching, aborting execution.");
744
745 ReleaseHandle(applyContext.hCacheThread);
746 }
747 }
748
749 // Execute.
750 if (pEngineState->plan.cExecuteActions)
751 {
752 hr = ApplyExecute(pEngineState, &applyContext, &fSuspend, &restart);
753 BootstrapperApplicationExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that execute completed.
754 }
755
756 // Wait for cache thread to terminate, this should return immediately unless we're waiting for layout to complete.
757 if (applyContext.hCacheThread)
758 {
759 HRESULT hrCached = ThrdWaitForCompletion(applyContext.hCacheThread, INFINITE, &dwCacheExitCode);
760 ExitOnFailure(hrCached, "Failed to wait for cache thread after execute.");
761
762 if (SUCCEEDED(hr))
763 {
764 hr = (HRESULT)dwCacheExitCode;
765 }
766 }
767
768 if (BOOTSTRAPPER_ACTION_UNSAFE_UNINSTALL == pEngineState->plan.action)
769 {
770 fSuspend = FALSE;
771 restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
772
773 LogId(REPORT_STANDARD, MSG_UNSAFE_APPLY_COMPLETED);
774 }
775
776 if (fSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart)
777 {
778 // Leave cache alone.
779 fRollbackCache = FALSE;
780 }
781 else if (SUCCEEDED(hr))
782 {
783 // Clean.
784 fRollbackCache = FALSE;
785
786 if (pEngineState->plan.cCleanActions)
787 {
788 ApplyClean(&pEngineState->userExperience, &pEngineState->plan, pEngineState->companionConnection.hPipe);
789 }
790 }
791
792 LExit:
793 if (fRollbackCache && !pEngineState->plan.fDisableRollback)
794 {
795 ApplyCacheRollback(&pEngineState->userExperience, &pEngineState->plan, pEngineState->companionConnection.hCachePipe, &applyContext);
796 }
797
798 // Unregister.
799 if (fRegistered)
800 {
801 ApplyUnregister(pEngineState, FAILED(hr), fSuspend, restart);
802 }
803
804 if (fElevated)
805 {
806 ElevationApplyUninitialize(pEngineState->companionConnection.hPipe);
807 }
808
809 pEngineState->userExperience.hwndApply = NULL;
810
811 if (fApplyInitialize)
812 {
813 ApplyUninitialize();
814 }
815
816 pEngineState->plan.fApplying = FALSE;
817
818 if (hLock)
819 {
820 ::ReleaseMutex(hLock);
821 ::CloseHandle(hLock);
822 }
823
824 ReleaseHandle(applyContext.hCacheThread);
825
826 if (fDeleteApplyCs)
827 {
828 DeleteCriticalSection(&applyContext.csApply);
829 }
830
831 if (fApplyBegan)
832 {
833 BACallbackOnApplyComplete(&pEngineState->userExperience, hr, restart, &applyCompleteAction);
834 if (BOOTSTRAPPER_APPLYCOMPLETE_ACTION_RESTART == applyCompleteAction)
835 {
836 pEngineState->fRestart = TRUE;
837 }
838
839 LogId(REPORT_STANDARD, MSG_APPLY_COMPLETE, hr, LoggingRestartToString(restart), LoggingBoolToString(pEngineState->fRestart));
840 }
841
842 return hr;
843 }
844
845 extern "C" HRESULT CoreLaunchApprovedExe(
846 __in BURN_ENGINE_STATE* pEngineState,
847 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
848 )
849 {
850 HRESULT hr = S_OK;
851 DWORD dwProcessId = 0;
852
853 LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_BEGIN, pLaunchApprovedExe->sczId);
854
855 hr = BACallbackOnLaunchApprovedExeBegin(&pEngineState->userExperience);
856 ExitOnRootFailure(hr, "BA aborted LaunchApprovedExe begin.");
857
858 // Elevate.
859 hr = CoreElevate(pEngineState, WM_BURN_LAUNCH_APPROVED_EXE, pLaunchApprovedExe->hwndParent);
860 ExitOnFailure(hr, "Failed to elevate.");
861
862 // Launch.
863 hr = ElevationLaunchApprovedExe(pEngineState->companionConnection.hPipe, pLaunchApprovedExe, &dwProcessId);
864
865 LExit:
866 BACallbackOnLaunchApprovedExeComplete(&pEngineState->userExperience, hr, dwProcessId);
867
868 LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_COMPLETE, hr, dwProcessId);
869
870 return hr;
871 }
872
873 extern "C" void CoreQuit(
874 __in BAENGINE_CONTEXT* pEngineContext,
875 __in DWORD dwExitCode
876 )
877 {
878 HRESULT hr = S_OK;
879 BURN_ENGINE_STATE* pEngineState = pEngineContext->pEngineState;
880
881 // Save engine state if resume mode is unequal to "none".
882 if (BURN_RESUME_MODE_NONE != pEngineState->resumeMode)
883 {
884 hr = CoreSaveEngineState(pEngineState);
885 if (FAILED(hr))
886 {
887 LogErrorId(hr, MSG_STATE_NOT_SAVED);
888 hr = S_OK;
889 }
890 }
891
892 LogId(REPORT_STANDARD, MSG_QUIT, dwExitCode);
893
894 pEngineState->userExperience.dwExitCode = dwExitCode;
895
896 ::EnterCriticalSection(&pEngineContext->csQueue);
897
898 pEngineState->fQuit = TRUE;
899
900 ::LeaveCriticalSection(&pEngineContext->csQueue);
901 }
902
903 extern "C" HRESULT CoreSaveEngineState(
904 __in BURN_ENGINE_STATE* pEngineState
905 )
906 {
907 HRESULT hr = S_OK;
908 BYTE* pbBuffer = NULL;
909 SIZE_T cbBuffer = 0;
910
911 // serialize engine state
912 hr = CoreSerializeEngineState(pEngineState, &pbBuffer, &cbBuffer);
913 ExitOnFailure(hr, "Failed to serialize engine state.");
914
915 // write to registration store
916 if (pEngineState->registration.fPerMachine)
917 {
918 hr = ElevationSaveState(pEngineState->companionConnection.hPipe, pbBuffer, cbBuffer);
919 ExitOnFailure(hr, "Failed to save engine state in per-machine process.");
920 }
921 else
922 {
923 hr = RegistrationSaveState(&pEngineState->registration, pbBuffer, cbBuffer);
924 ExitOnFailure(hr, "Failed to save engine state.");
925 }
926
927 LExit:
928 ReleaseMem(pbBuffer);
929
930 return hr;
931 }
932
933 extern "C" LPCWSTR CoreRelationTypeToCommandLineString(
934 __in BOOTSTRAPPER_RELATION_TYPE relationType
935 )
936 {
937 LPCWSTR wzRelationTypeCommandLine = NULL;
938 switch (relationType)
939 {
940 case BOOTSTRAPPER_RELATION_DETECT:
941 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_DETECT;
942 break;
943 case BOOTSTRAPPER_RELATION_UPGRADE:
944 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE;
945 break;
946 case BOOTSTRAPPER_RELATION_ADDON:
947 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_ADDON;
948 break;
949 case BOOTSTRAPPER_RELATION_PATCH:
950 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_PATCH;
951 break;
952 case BOOTSTRAPPER_RELATION_UPDATE:
953 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPDATE;
954 break;
955 case BOOTSTRAPPER_RELATION_DEPENDENT_ADDON:
956 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_DEPENDENT_ADDON;
957 break;
958 case BOOTSTRAPPER_RELATION_DEPENDENT_PATCH:
959 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_DEPENDENT_PATCH;
960 break;
961 case BOOTSTRAPPER_RELATION_CHAIN_PACKAGE:
962 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_CHAIN_PACKAGE;
963 break;
964 case BOOTSTRAPPER_RELATION_NONE: __fallthrough;
965 default:
966 wzRelationTypeCommandLine = NULL;
967 break;
968 }
969
970 return wzRelationTypeCommandLine;
971 }
972
973 static HRESULT CoreRecreateCommandLine(
974 __deref_inout_z LPWSTR* psczCommandLine,
975 __in BOOTSTRAPPER_ACTION action,
976 __in BURN_ENGINE_COMMAND* pInternalCommand,
977 __in BOOTSTRAPPER_COMMAND* pCommand,
978 __in BOOTSTRAPPER_RELATION_TYPE relationType,
979 __in BOOL fPassthrough
980 )
981 {
982 HRESULT hr = S_OK;
983 LPWSTR scz = NULL;
984 LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType);
985
986 switch (pCommand->display)
987 {
988 case BOOTSTRAPPER_DISPLAY_NONE:
989 hr = StrAllocConcat(psczCommandLine, L" /quiet", 0);
990 break;
991 case BOOTSTRAPPER_DISPLAY_PASSIVE:
992 hr = StrAllocConcat(psczCommandLine, L" /passive", 0);
993 break;
994 }
995 ExitOnFailure(hr, "Failed to append display state to command-line");
996
997 switch (action)
998 {
999 case BOOTSTRAPPER_ACTION_HELP:
1000 hr = StrAllocConcat(psczCommandLine, L" /help", 0);
1001 break;
1002 case BOOTSTRAPPER_ACTION_MODIFY:
1003 hr = StrAllocConcat(psczCommandLine, L" /modify", 0);
1004 break;
1005 case BOOTSTRAPPER_ACTION_REPAIR:
1006 hr = StrAllocConcat(psczCommandLine, L" /repair", 0);
1007 break;
1008 case BOOTSTRAPPER_ACTION_UNINSTALL:
1009 hr = StrAllocConcat(psczCommandLine, L" /uninstall", 0);
1010 break;
1011 case BOOTSTRAPPER_ACTION_UNSAFE_UNINSTALL:
1012 hr = StrAllocConcat(psczCommandLine, L" /unsafeuninstall", 0);
1013 break;
1014 }
1015 ExitOnFailure(hr, "Failed to append action state to command-line");
1016
1017 if (pInternalCommand->sczActiveParent)
1018 {
1019 if (*pInternalCommand->sczActiveParent)
1020 {
1021 hr = StrAllocFormatted(&scz, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_PARENT, pInternalCommand->sczActiveParent);
1022 ExitOnFailure(hr, "Failed to format active parent command-line for command-line.");
1023 }
1024 else
1025 {
1026 hr = StrAllocFormatted(&scz, L" /%ls", BURN_COMMANDLINE_SWITCH_PARENT_NONE);
1027 ExitOnFailure(hr, "Failed to format parent:none command-line for command-line.");
1028 }
1029
1030 hr = StrAllocConcat(psczCommandLine, scz, 0);
1031 ExitOnFailure(hr, "Failed to append active parent command-line to command-line.");
1032 }
1033
1034 if (pInternalCommand->sczAncestors)
1035 {
1036 hr = StrAllocConcatFormatted(psczCommandLine, L" /%ls=%ls", BURN_COMMANDLINE_SWITCH_ANCESTORS, pInternalCommand->sczAncestors);
1037 ExitOnFailure(hr, "Failed to append ancestors to command-line.");
1038 }
1039
1040 hr = CoreAppendEngineWorkingDirectoryToCommandLine(pInternalCommand->sczEngineWorkingDirectory, psczCommandLine, NULL);
1041 ExitOnFailure(hr, "Failed to append the custom working directory to command-line.");
1042
1043 if (wzRelationTypeCommandLine)
1044 {
1045 hr = StrAllocConcatFormatted(psczCommandLine, L" /%ls", wzRelationTypeCommandLine);
1046 ExitOnFailure(hr, "Failed to append relation type to command-line.");
1047 }
1048
1049 if (pInternalCommand->fArpSystemComponent)
1050 {
1051 hr = StrAllocConcatFormatted(psczCommandLine, L" /%ls", BURN_COMMANDLINE_SWITCH_SYSTEM_COMPONENT);
1052 ExitOnFailure(hr, "Failed to append system component to command-line.");
1053 }
1054
1055 if (fPassthrough)
1056 {
1057 hr = StrAllocConcatFormatted(psczCommandLine, L" /%ls", BURN_COMMANDLINE_SWITCH_PASSTHROUGH);
1058 ExitOnFailure(hr, "Failed to append passthrough to command-line.");
1059 }
1060
1061 if (pCommand->wzCommandLine && *pCommand->wzCommandLine)
1062 {
1063 hr = StrAllocConcatFormattedSecure(psczCommandLine, L" %ls", pCommand->wzCommandLine);
1064 ExitOnFailure(hr, "Failed to append command-line to command-line.");
1065 }
1066
1067 LExit:
1068 ReleaseStr(scz);
1069
1070 return hr;
1071 }
1072
1073 extern "C" HRESULT CoreCreatePassthroughBundleCommandLine(
1074 __deref_inout_z LPWSTR* psczCommandLine,
1075 __in BURN_ENGINE_COMMAND* pInternalCommand,
1076 __in BOOTSTRAPPER_COMMAND* pCommand
1077 )
1078 {
1079 HRESULT hr = S_OK;
1080
1081 // No matter the operation, we're passing the same command-line.
1082 // That's what makes this a passthrough bundle.
1083 hr = CoreRecreateCommandLine(psczCommandLine, pCommand->action, pInternalCommand, pCommand, pCommand->relationType, TRUE);
1084 ExitOnFailure(hr, "Failed to recreate passthrough bundle command-line.");
1085
1086 LExit:
1087 return hr;
1088 }
1089
1090 extern "C" HRESULT CoreCreateResumeCommandLine(
1091 __deref_inout_z LPWSTR* psczCommandLine,
1092 __in BURN_PLAN* pPlan,
1093 __in BURN_LOGGING* pLog
1094 )
1095 {
1096 HRESULT hr = S_OK;
1097
1098 if (BURN_LOGGING_ATTRIBUTE_EXTRADEBUG & pPlan->pInternalCommand->dwLoggingAttributes)
1099 {
1100 hr = StrAllocConcatFormatted(psczCommandLine, L" /%ls=%ls", BURN_COMMANDLINE_SWITCH_LOG_MODE, L"x");
1101 ExitOnFailure(hr, "Failed to set log mode in resume command-line.");
1102 }
1103
1104 if (pLog->sczPath && *(pLog->sczPath))
1105 {
1106 hr = StrAllocConcatFormatted(psczCommandLine, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_LOG_APPEND, pLog->sczPath);
1107 ExitOnFailure(hr, "Failed to set log path in resume command-line.");
1108 }
1109
1110 hr = CoreRecreateCommandLine(psczCommandLine, pPlan->action, pPlan->pInternalCommand, pPlan->pCommand, pPlan->pCommand->relationType, pPlan->pCommand->fPassthrough);
1111 ExitOnFailure(hr, "Failed to recreate resume command-line.");
1112
1113 LExit:
1114 return hr;
1115 }
1116
1117 extern "C" HRESULT CoreCreateUpdateBundleCommandLine(
1118 __deref_inout_z LPWSTR* psczCommandLine,
1119 __in BURN_ENGINE_COMMAND* pInternalCommand,
1120 __in BOOTSTRAPPER_COMMAND* pCommand
1121 )
1122 {
1123 HRESULT hr = S_OK;
1124
1125 hr = CoreRecreateCommandLine(psczCommandLine, BOOTSTRAPPER_ACTION_INSTALL, pInternalCommand, pCommand, BOOTSTRAPPER_RELATION_UPDATE, FALSE);
1126 ExitOnFailure(hr, "Failed to recreate update bundle command-line.");
1127
1128 LExit:
1129 return hr;
1130 }
1131
1132 extern "C" HRESULT CoreAppendFileHandleAttachedToCommandLine(
1133 __in HANDLE hFileWithAttachedContainer,
1134 __out HANDLE* phExecutableFile,
1135 __deref_inout_z LPWSTR* psczCommandLine
1136 )
1137 {
1138 HRESULT hr = S_OK;
1139 HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
1140
1141 *phExecutableFile = INVALID_HANDLE_VALUE;
1142
1143 if (!::DuplicateHandle(::GetCurrentProcess(), hFileWithAttachedContainer, ::GetCurrentProcess(), &hExecutableFile, 0, TRUE, DUPLICATE_SAME_ACCESS))
1144 {
1145 ExitWithLastError(hr, "Failed to duplicate file handle for attached container.");
1146 }
1147
1148 hr = StrAllocConcatFormattedSecure(psczCommandLine, L" -%ls=%Iu", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, reinterpret_cast<size_t>(hExecutableFile));
1149 ExitOnFailure(hr, "Failed to append the file handle to the command line.");
1150
1151 *phExecutableFile = hExecutableFile;
1152 hExecutableFile = INVALID_HANDLE_VALUE;
1153
1154 LExit:
1155 ReleaseFileHandle(hExecutableFile);
1156
1157 return hr;
1158 }
1159
1160 extern "C" HRESULT CoreAppendFileHandleSelfToCommandLine(
1161 __in LPCWSTR wzExecutablePath,
1162 __out HANDLE* phExecutableFile,
1163 __deref_inout_z LPWSTR* psczCommandLine,
1164 __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine
1165 )
1166 {
1167 HRESULT hr = S_OK;
1168 HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
1169 SECURITY_ATTRIBUTES securityAttributes = { };
1170 securityAttributes.bInheritHandle = TRUE;
1171 *phExecutableFile = INVALID_HANDLE_VALUE;
1172
1173 hExecutableFile = ::CreateFileW(wzExecutablePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, &securityAttributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1174 if (INVALID_HANDLE_VALUE != hExecutableFile)
1175 {
1176 hr = StrAllocConcatFormattedSecure(psczCommandLine, L" -%ls=%Iu", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, reinterpret_cast<size_t>(hExecutableFile));
1177 ExitOnFailure(hr, "Failed to append the file handle to the command line.");
1178
1179 if (psczObfuscatedCommandLine)
1180 {
1181 hr = StrAllocConcatFormatted(psczObfuscatedCommandLine, L" -%ls=%Iu", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, reinterpret_cast<size_t>(hExecutableFile));
1182 ExitOnFailure(hr, "Failed to append the file handle to the obfuscated command line.");
1183 }
1184
1185 *phExecutableFile = hExecutableFile;
1186 hExecutableFile = INVALID_HANDLE_VALUE;
1187 }
1188
1189 LExit:
1190 ReleaseFileHandle(hExecutableFile);
1191
1192 return hr;
1193 }
1194
1195 HRESULT CoreAppendLogToCommandLine(
1196 __deref_inout_z LPWSTR* psczCommandLine,
1197 __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine,
1198 __in BOOL fRollback,
1199 __in BURN_VARIABLES* pVariables,
1200 __in BURN_PACKAGE *pPackage
1201 )
1202 {
1203 HRESULT hr = S_OK;
1204 INT ccArgs = 0;
1205 LPWSTR* rgszArgs = nullptr;
1206 LPWSTR szLogArg = nullptr;
1207 LPWSTR szLogArgFormatted = nullptr;
1208 LPCWSTR szLogVar = fRollback ? pPackage->sczRollbackLogPathVariable : pPackage->sczLogPathVariable;
1209
1210 if (!szLogVar || !*szLogVar)
1211 {
1212 ExitFunction1(hr = S_FALSE);
1213 }
1214
1215 hr = AppParseCommandLine(*psczCommandLine, &ccArgs, &rgszArgs);
1216 ExitOnFailure(hr, "Failed parsing command line");
1217
1218 // Log flag already present?
1219 for (INT i = 0; i < ccArgs; ++i)
1220 {
1221 if (rgszArgs[i][0] == L'-' || rgszArgs[i][0] == L'/')
1222 {
1223 // Now looking for 'log' or 'l'
1224 if ((CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &rgszArgs[i][1], -1, L"log", -1))
1225 || (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &rgszArgs[i][1], -1, L"l", -1)))
1226 {
1227 ExitFunction1(hr = S_FALSE);
1228 }
1229 }
1230 }
1231
1232 hr = StrAllocFormatted(&szLogArg, L" -log \"[%ls]\"", szLogVar);
1233 ExitOnFailure(hr, "Failed creating log argument");
1234
1235 hr = VariableFormatString(pVariables, szLogArg, &szLogArgFormatted, NULL);
1236 ExitOnFailure(hr, "Failed to format argument string.");
1237
1238 hr = StrAllocConcat(psczCommandLine, szLogArgFormatted, 0);
1239 ExitOnFailure(hr, "Failed concatenating '-log' to command line");
1240
1241 hr = StrAllocConcat(psczObfuscatedCommandLine, szLogArgFormatted, 0);
1242 ExitOnFailure(hr, "Failed concatenating '-log' to obfuscated command line");
1243
1244 LExit:
1245 if (rgszArgs)
1246 {
1247 AppFreeCommandLineArgs(rgszArgs);
1248 }
1249 ReleaseStr(szLogArg);
1250 ReleaseStr(szLogArgFormatted);
1251
1252 return hr;
1253 }
1254
1255 extern "C" HRESULT CoreAppendSplashScreenWindowToCommandLine(
1256 __in_opt HWND hwndSplashScreen,
1257 __deref_inout_z LPWSTR* psczCommandLine
1258 )
1259 {
1260 HRESULT hr = S_OK;
1261
1262 if (hwndSplashScreen)
1263 {
1264 hr = StrAllocConcatFormattedSecure(psczCommandLine, L" -%ls=%Iu", BURN_COMMANDLINE_SWITCH_SPLASH_SCREEN, reinterpret_cast<size_t>(hwndSplashScreen));
1265 ExitOnFailure(hr, "Failed to append the splash screen window to the command line.");
1266 }
1267
1268 LExit:
1269 return hr;
1270 }
1271
1272 extern "C" HRESULT CoreAppendEngineWorkingDirectoryToCommandLine(
1273 __in_z_opt LPCWSTR wzEngineWorkingDirectory,
1274 __deref_inout_z LPWSTR* psczCommandLine,
1275 __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine
1276 )
1277 {
1278 HRESULT hr = S_OK;
1279 LPWSTR sczArgument = NULL;
1280
1281 if (wzEngineWorkingDirectory)
1282 {
1283 hr = EscapeAndAppendArgumentToCommandLineFormatted(psczCommandLine, psczObfuscatedCommandLine, L"-%ls=%ls", BURN_COMMANDLINE_SWITCH_WORKING_DIRECTORY, wzEngineWorkingDirectory);
1284 ExitOnFailure(hr, "Failed to append the custom working directory to the command line.");
1285 }
1286
1287 LExit:
1288 ReleaseStr(sczArgument);
1289
1290 return hr;
1291 }
1292
1293
1294 extern "C" void CoreCleanup(
1295 __in BURN_ENGINE_STATE* pEngineState
1296 )
1297 {
1298 HRESULT hr = S_OK;
1299 LONGLONG llValue = 0;
1300 BOOL fNeedsElevation = pEngineState->registration.fPerMachine && INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe;
1301
1302 LogId(REPORT_STANDARD, MSG_CLEANUP_BEGIN);
1303
1304 if (pEngineState->plan.fAffectedMachineState)
1305 {
1306 LogId(REPORT_STANDARD, MSG_CLEANUP_SKIPPED_APPLY);
1307 ExitFunction();
1308 }
1309
1310 if (fNeedsElevation)
1311 {
1312 hr = VariableGetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, &llValue);
1313 ExitOnFailure(hr, "Failed to get value of WixBundleElevated variable during cleanup");
1314
1315 if (llValue)
1316 {
1317 fNeedsElevation = FALSE;
1318 }
1319 else
1320 {
1321 LogId(REPORT_STANDARD, MSG_CLEANUP_SKIPPED_ELEVATION_REQUIRED);
1322 ExitFunction();
1323 }
1324 }
1325
1326 if (!pEngineState->fDetected)
1327 {
1328 hr = CoreDetect(pEngineState, pEngineState->hMessageWindow);
1329 ExitOnFailure(hr, "Detect during cleanup failed");
1330 }
1331
1332 if (!pEngineState->registration.fEligibleForCleanup)
1333 {
1334 ExitFunction();
1335 }
1336
1337 hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL);
1338 ExitOnFailure(hr, "Plan during cleanup failed");
1339
1340 hr = CoreApply(pEngineState, pEngineState->hMessageWindow);
1341 ExitOnFailure(hr, "Apply during cleanup failed");
1342
1343 LExit:
1344 LogId(REPORT_STANDARD, MSG_CLEANUP_COMPLETE, hr);
1345 }
1346
1347 extern "C" HRESULT CoreParseCommandLine(
1348 __in BURN_ENGINE_COMMAND* pInternalCommand,
1349 __in BOOTSTRAPPER_COMMAND* pCommand,
1350 __in BURN_PIPE_CONNECTION* pCompanionConnection,
1351 __in BURN_PIPE_CONNECTION* pEmbeddedConnection,
1352 __inout HANDLE* phSectionFile,
1353 __inout HANDLE* phSourceEngineFile
1354 )
1355 {
1356 HRESULT hr = S_OK;
1357 BOOL fUnknownArg = FALSE;
1358 BOOL fInvalidCommandLine = FALSE;
1359 DWORD64 qw = 0;
1360 int argc = pInternalCommand->argc;
1361 LPWSTR* argv = pInternalCommand->argv;
1362
1363 for (int i = 0; i < argc; ++i)
1364 {
1365 fUnknownArg = FALSE;
1366
1367 if (argv[i][0] == L'-' || argv[i][0] == L'/')
1368 {
1369 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"l", -1) ||
1370 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"log", -1) ||
1371 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"xlog", -1))
1372 {
1373 pInternalCommand->dwLoggingAttributes &= ~BURN_LOGGING_ATTRIBUTE_APPEND;
1374
1375 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], 1, L"x", 1))
1376 {
1377 pInternalCommand->dwLoggingAttributes |= BURN_LOGGING_ATTRIBUTE_VERBOSE | BURN_LOGGING_ATTRIBUTE_EXTRADEBUG;
1378 }
1379
1380 if (i + 1 >= argc)
1381 {
1382 fInvalidCommandLine = TRUE;
1383 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for log.");
1384 }
1385
1386 ++i;
1387
1388 hr = PathExpand(&pInternalCommand->sczLogFile, argv[i], PATH_EXPAND_FULLPATH);
1389 ExitOnFailure(hr, "Failed to copy log file path.");
1390 }
1391 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"?", -1) ||
1392 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"h", -1) ||
1393 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"help", -1))
1394 {
1395 pCommand->action = BOOTSTRAPPER_ACTION_HELP;
1396 }
1397 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"q", -1) ||
1398 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"quiet", -1) ||
1399 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"s", -1) ||
1400 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"silent", -1))
1401 {
1402 pCommand->display = BOOTSTRAPPER_DISPLAY_NONE;
1403 }
1404 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"passive", -1))
1405 {
1406 pCommand->display = BOOTSTRAPPER_DISPLAY_PASSIVE;
1407 }
1408 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"layout", -1))
1409 {
1410 if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1411 {
1412 pCommand->action = BOOTSTRAPPER_ACTION_LAYOUT;
1413 }
1414
1415 // If there is another command line argument and it is not a switch, use that as the layout directory.
1416 if (i + 1 < argc && argv[i + 1][0] != L'-' && argv[i + 1][0] != L'/')
1417 {
1418 ++i;
1419
1420 hr = PathExpand(&pCommand->wzLayoutDirectory, argv[i], PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH);
1421 ExitOnFailure(hr, "Failed to copy path for layout directory.");
1422 }
1423 }
1424 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"unsafeuninstall", -1))
1425 {
1426 if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1427 {
1428 pCommand->action = BOOTSTRAPPER_ACTION_UNSAFE_UNINSTALL;
1429 }
1430 }
1431 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"uninstall", -1))
1432 {
1433 if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1434 {
1435 pCommand->action = BOOTSTRAPPER_ACTION_UNINSTALL;
1436 }
1437 }
1438 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"repair", -1))
1439 {
1440 if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1441 {
1442 pCommand->action = BOOTSTRAPPER_ACTION_REPAIR;
1443 }
1444 }
1445 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"modify", -1))
1446 {
1447 if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1448 {
1449 pCommand->action = BOOTSTRAPPER_ACTION_MODIFY;
1450 }
1451 }
1452 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"package", -1) ||
1453 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"update", -1))
1454 {
1455 if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action)
1456 {
1457 pCommand->action = BOOTSTRAPPER_ACTION_INSTALL;
1458 }
1459 }
1460 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"noaupause", -1))
1461 {
1462 pInternalCommand->automaticUpdates = BURN_AU_PAUSE_ACTION_NONE;
1463 }
1464 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"keepaupaused", -1))
1465 {
1466 // Switch /noaupause takes precedence.
1467 if (BURN_AU_PAUSE_ACTION_NONE != pInternalCommand->automaticUpdates)
1468 {
1469 pInternalCommand->automaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME;
1470 }
1471 }
1472 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"disablesystemrestore", -1))
1473 {
1474 pInternalCommand->fDisableSystemRestore = TRUE;
1475 }
1476 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"originalsource", -1))
1477 {
1478 if (i + 1 >= argc)
1479 {
1480 fInvalidCommandLine = TRUE;
1481 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for original source.");
1482 }
1483
1484 ++i;
1485 hr = PathExpand(&pInternalCommand->sczOriginalSource, argv[i], PATH_EXPAND_FULLPATH);
1486 ExitOnFailure(hr, "Failed to copy last used source.");
1487 }
1488 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT, -1))
1489 {
1490 if (i + 1 >= argc)
1491 {
1492 fInvalidCommandLine = TRUE;
1493 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a value for parent.");
1494 }
1495
1496 ++i;
1497
1498 hr = StrAllocString(&pInternalCommand->sczActiveParent, argv[i], 0);
1499 ExitOnFailure(hr, "Failed to copy parent.");
1500 }
1501 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT_NONE, -1))
1502 {
1503 hr = StrAllocString(&pInternalCommand->sczActiveParent, L"", 0);
1504 ExitOnFailure(hr, "Failed to initialize parent to none.");
1505 }
1506 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_LOG_APPEND, -1))
1507 {
1508 if (i + 1 >= argc)
1509 {
1510 fInvalidCommandLine = TRUE;
1511 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for append log.");
1512 }
1513
1514 ++i;
1515
1516 hr = PathExpand(&pInternalCommand->sczLogFile, argv[i], PATH_EXPAND_FULLPATH);
1517 ExitOnFailure(hr, "Failed to copy append log file path.");
1518
1519 pInternalCommand->dwLoggingAttributes |= BURN_LOGGING_ATTRIBUTE_APPEND;
1520 }
1521 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_LOG_MODE), BURN_COMMANDLINE_SWITCH_LOG_MODE, -1))
1522 {
1523 // Get a pointer to the next character after the switch.
1524 LPCWSTR wzParam = &argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_LOG_MODE)];
1525 if (L'=' != wzParam[-1] || L'\0' == wzParam[0])
1526 {
1527 fInvalidCommandLine = TRUE;
1528 TraceLog(E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_LOG_MODE);
1529 }
1530 else
1531 {
1532 while (L'\0' != wzParam[0])
1533 {
1534 switch (wzParam[0])
1535 {
1536 case L'x':
1537 pInternalCommand->dwLoggingAttributes |= BURN_LOGGING_ATTRIBUTE_EXTRADEBUG | BURN_LOGGING_ATTRIBUTE_VERBOSE;
1538 break;
1539 default:
1540 // Skip (but log) any other modifiers we don't recognize,
1541 // so that adding future modifiers doesn't break old bundles.
1542 LogId(REPORT_STANDARD, MSG_BURN_UNKNOWN_PRIVATE_SWITCH_MODIFIER, BURN_COMMANDLINE_SWITCH_LOG_MODE, wzParam[0]);
1543 break;
1544 }
1545
1546 ++wzParam;
1547 }
1548 }
1549 }
1550 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_ELEVATED, -1))
1551 {
1552 if (i + 3 >= argc)
1553 {
1554 fInvalidCommandLine = TRUE;
1555 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify the elevated name, token and parent process id.");
1556 }
1557
1558 if (BURN_MODE_UNKNOWN != pInternalCommand->mode)
1559 {
1560 fInvalidCommandLine = TRUE;
1561 TraceLog(E_INVALIDARG, "Multiple mode command-line switches were provided.");
1562 }
1563
1564 pInternalCommand->mode = BURN_MODE_ELEVATED;
1565
1566 ++i;
1567
1568 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(&pInternalCommand->rgSecretArgs), pInternalCommand->cSecretArgs, 3, sizeof(int), 3);
1569 ExitOnFailure(hr, "Failed to ensure size for secret args.");
1570
1571 pInternalCommand->rgSecretArgs[pInternalCommand->cSecretArgs] = i;
1572 pInternalCommand->cSecretArgs += 1;
1573 pInternalCommand->rgSecretArgs[pInternalCommand->cSecretArgs] = i + 1;
1574 pInternalCommand->cSecretArgs += 1;
1575 pInternalCommand->rgSecretArgs[pInternalCommand->cSecretArgs] = i + 2;
1576 pInternalCommand->cSecretArgs += 1;
1577
1578 hr = ParsePipeConnection(argv + i, pCompanionConnection);
1579 if (FAILED(hr))
1580 {
1581 fInvalidCommandLine = TRUE;
1582 TraceLog(hr, "Failed to parse elevated connection.");
1583 hr = S_OK;
1584 }
1585
1586 i += 2;
1587 }
1588 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_SYSTEM_COMPONENT), BURN_COMMANDLINE_SWITCH_SYSTEM_COMPONENT, lstrlenW(BURN_COMMANDLINE_SWITCH_SYSTEM_COMPONENT)))
1589 {
1590 // Get a pointer to the next character after the switch.
1591 LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_SYSTEM_COMPONENT)];
1592
1593 // Switch without an argument is allowed. An empty string means FALSE, everything else means TRUE.
1594 if (L'\0' != wzParam[0])
1595 {
1596 if (L'=' != wzParam[0])
1597 {
1598 fInvalidCommandLine = TRUE;
1599 TraceLog(E_INVALIDARG, "Invalid switch: %ls", argv[i]);
1600 }
1601 else
1602 {
1603 pInternalCommand->fArpSystemComponent = L'\0' != wzParam[1];
1604 }
1605 }
1606 else
1607 {
1608 pInternalCommand->fArpSystemComponent = TRUE;
1609 }
1610 }
1611 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_EMBEDDED, -1))
1612 {
1613 if (i + 3 >= argc)
1614 {
1615 fInvalidCommandLine = TRUE;
1616 ExitWithRootFailure(hr, E_INVALIDARG, "Must specify the embedded name, token and parent process id.");
1617 }
1618
1619 switch (pInternalCommand->mode)
1620 {
1621 case BURN_MODE_UNKNOWN:
1622 case BURN_MODE_NORMAL:
1623 pInternalCommand->mode = BURN_MODE_EMBEDDED;
1624 break;
1625 default:
1626 fInvalidCommandLine = TRUE;
1627 ExitWithRootFailure(hr, E_INVALIDARG, "Multiple mode command-line switches were provided.");
1628 }
1629
1630 ++i;
1631
1632 hr = ParsePipeConnection(argv + i, pEmbeddedConnection);
1633 if (FAILED(hr))
1634 {
1635 fInvalidCommandLine = TRUE;
1636 TraceLog(hr, "Failed to parse embedded connection.");
1637 hr = S_OK;
1638 }
1639
1640 i += 2;
1641 }
1642 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_DETECT, -1))
1643 {
1644 pCommand->relationType = BOOTSTRAPPER_RELATION_DETECT;
1645
1646 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1647 }
1648 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE, -1))
1649 {
1650 pCommand->relationType = BOOTSTRAPPER_RELATION_UPGRADE;
1651
1652 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1653 }
1654 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_ADDON, -1))
1655 {
1656 pCommand->relationType = BOOTSTRAPPER_RELATION_ADDON;
1657
1658 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1659 }
1660 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_DEPENDENT_ADDON, -1))
1661 {
1662 pCommand->relationType = BOOTSTRAPPER_RELATION_DEPENDENT_ADDON;
1663
1664 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1665 }
1666 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_PATCH, -1))
1667 {
1668 pCommand->relationType = BOOTSTRAPPER_RELATION_PATCH;
1669
1670 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1671 }
1672 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_DEPENDENT_PATCH, -1))
1673 {
1674 pCommand->relationType = BOOTSTRAPPER_RELATION_DEPENDENT_PATCH;
1675
1676 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1677 }
1678 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPDATE, -1))
1679 {
1680 pCommand->relationType = BOOTSTRAPPER_RELATION_UPDATE;
1681
1682 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1683 }
1684 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_CHAIN_PACKAGE, -1))
1685 {
1686 pCommand->relationType = BOOTSTRAPPER_RELATION_CHAIN_PACKAGE;
1687
1688 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1689 }
1690 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PASSTHROUGH, -1))
1691 {
1692 pCommand->fPassthrough = TRUE;
1693 }
1694 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RUNONCE, -1))
1695 {
1696 switch (pInternalCommand->mode)
1697 {
1698 case BURN_MODE_UNKNOWN: __fallthrough;
1699 case BURN_MODE_NORMAL:
1700 pInternalCommand->mode = BURN_MODE_RUNONCE;
1701 break;
1702 default:
1703 fInvalidCommandLine = TRUE;
1704 TraceLog(E_INVALIDARG, "Multiple mode command-line switches were provided.");
1705 }
1706 }
1707 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)))
1708 {
1709 // Get a pointer to the next character after the switch.
1710 LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES)];
1711 if (L'=' != wzParam[0] || L'\0' == wzParam[1])
1712 {
1713 fInvalidCommandLine = TRUE;
1714 TraceLog(E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES);
1715 }
1716 else
1717 {
1718 hr = StrAllocString(&pInternalCommand->sczIgnoreDependencies, &wzParam[1], 0);
1719 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
1720 }
1721 }
1722 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)))
1723 {
1724 // Get a pointer to the next character after the switch.
1725 LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS)];
1726 if (L'=' != wzParam[0] || L'\0' == wzParam[1])
1727 {
1728 fInvalidCommandLine = TRUE;
1729 TraceLog(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_ANCESTORS);
1730 }
1731 else
1732 {
1733 hr = StrAllocString(&pInternalCommand->sczAncestors, &wzParam[1], 0);
1734 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
1735 }
1736 }
1737 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_WORKING_DIRECTORY), BURN_COMMANDLINE_SWITCH_WORKING_DIRECTORY, lstrlenW(BURN_COMMANDLINE_SWITCH_WORKING_DIRECTORY)))
1738 {
1739 // Get a pointer to the next character after the switch.
1740 LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_WORKING_DIRECTORY)];
1741 if (L'=' != wzParam[0])
1742 {
1743 fInvalidCommandLine = TRUE;
1744 TraceLog(E_INVALIDARG, "Invalid switch: %ls", argv[i]);
1745 }
1746 else
1747 {
1748 hr = PathExpand(&pInternalCommand->sczEngineWorkingDirectory, wzParam + 1, PATH_EXPAND_FULLPATH);
1749 ExitOnFailure(hr, "Failed to store the custom working directory.");
1750 }
1751 }
1752 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)))
1753 {
1754 LPCWSTR wzParam = &argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED)];
1755 if (L'=' != wzParam[-1] || L'\0' == wzParam[0])
1756 {
1757 fInvalidCommandLine = TRUE;
1758 TraceLog(E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED);
1759 }
1760 else
1761 {
1762 hr = StrStringToUInt64(wzParam, 0, &qw);
1763 if (FAILED(hr))
1764 {
1765 TraceLog(hr, "Failed to parse file handle: '%ls'", wzParam);
1766 hr = S_OK;
1767 }
1768 else
1769 {
1770 *phSourceEngineFile = (HANDLE)qw;
1771 }
1772 }
1773 }
1774 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)))
1775 {
1776 LPCWSTR wzParam = &argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF)];
1777 if (L'=' != wzParam[-1] || L'\0' == wzParam[0])
1778 {
1779 fInvalidCommandLine = TRUE;
1780 TraceLog(E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF);
1781 }
1782 else
1783 {
1784 hr = StrStringToUInt64(wzParam, 0, &qw);
1785 if (FAILED(hr))
1786 {
1787 TraceLog(hr, "Failed to parse file handle: '%ls'", wzParam);
1788 hr = S_OK;
1789 }
1790 else
1791 {
1792 *phSectionFile = (HANDLE)qw;
1793 }
1794 }
1795 }
1796 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_SPLASH_SCREEN), BURN_COMMANDLINE_SWITCH_SPLASH_SCREEN, lstrlenW(BURN_COMMANDLINE_SWITCH_SPLASH_SCREEN)))
1797 {
1798 LPCWSTR wzParam = &argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_SPLASH_SCREEN)];
1799 if (L'=' != wzParam[-1] || L'\0' == wzParam[0])
1800 {
1801 fInvalidCommandLine = TRUE;
1802 TraceLog(E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_SPLASH_SCREEN);
1803 }
1804 else
1805 {
1806 hr = StrStringToUInt64(wzParam, 0, &qw);
1807 if (FAILED(hr))
1808 {
1809 TraceLog(hr, "Failed to parse splash screen window: '%ls'", wzParam);
1810 hr = S_OK;
1811 }
1812 else
1813 {
1814 pCommand->hwndSplashScreen = (HWND)qw;
1815 }
1816 }
1817 }
1818 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)))
1819 {
1820 // Skip (but log) any other private burn switches we don't recognize, so that
1821 // adding future private variables doesn't break old bundles
1822 LogId(REPORT_STANDARD, MSG_BURN_UNKNOWN_PRIVATE_SWITCH, &argv[i][1]);
1823 }
1824 else
1825 {
1826 fUnknownArg = TRUE;
1827 }
1828 }
1829 else
1830 {
1831 fUnknownArg = TRUE;
1832 }
1833
1834 if (fUnknownArg)
1835 {
1836 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(&pInternalCommand->rgUnknownArgs), pInternalCommand->cUnknownArgs, 1, sizeof(int), 5);
1837 ExitOnFailure(hr, "Failed to ensure size for unknown args.");
1838
1839 pInternalCommand->rgUnknownArgs[pInternalCommand->cUnknownArgs] = i;
1840 pInternalCommand->cUnknownArgs += 1;
1841 }
1842 }
1843
1844 if (BURN_MODE_EMBEDDED == pInternalCommand->mode)
1845 {
1846 // Ensure the display goes embedded as well.
1847 pCommand->display = BOOTSTRAPPER_DISPLAY_EMBEDDED;
1848
1849 // Disable system restore since the parent bundle may have done it.
1850 pInternalCommand->fDisableSystemRestore = TRUE;
1851 }
1852
1853 // Set the defaults if nothing was set above.
1854 if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action)
1855 {
1856 pCommand->action = BOOTSTRAPPER_ACTION_INSTALL;
1857 }
1858
1859 if (BOOTSTRAPPER_DISPLAY_UNKNOWN == pCommand->display)
1860 {
1861 pCommand->display = BOOTSTRAPPER_DISPLAY_FULL;
1862 }
1863
1864 if (BURN_MODE_UNKNOWN == pInternalCommand->mode)
1865 {
1866 pInternalCommand->mode = BURN_MODE_NORMAL;
1867 }
1868
1869 LExit:
1870 if (fInvalidCommandLine)
1871 {
1872 hr = S_OK;
1873 pInternalCommand->fInvalidCommandLine = TRUE;
1874 }
1875
1876 return hr;
1877 }
1878
1879 extern "C" void CoreUpdateRestartState(
1880 __in BURN_ENGINE_STATE* pEngineState,
1881 __in BURN_RESTART_STATE restartState
1882 )
1883 {
1884 ::EnterCriticalSection(&pEngineState->csRestartState);
1885
1886 if (pEngineState->fRestarting && restartState > pEngineState->restartState)
1887 {
1888 pEngineState->restartState = restartState;
1889 }
1890
1891 ::LeaveCriticalSection(&pEngineState->csRestartState);
1892 }
1893
1894 extern "C" void CoreFunctionOverride(
1895 __in_opt PFN_CREATEPROCESSW pfnCreateProcessW,
1896 __in_opt PFN_PROCWAITFORCOMPLETION pfnProcWaitForCompletion
1897 )
1898 {
1899 vpfnCreateProcessW = pfnCreateProcessW;
1900 vpfnProcWaitForCompletion = pfnProcWaitForCompletion;
1901 }
1902
1903 extern "C" HRESULT CoreCreateProcess(
1904 __in_opt LPCWSTR wzApplicationName,
1905 __inout_opt LPWSTR sczCommandLine,
1906 __in BOOL fInheritHandles,
1907 __in DWORD dwCreationFlags,
1908 __in_opt LPCWSTR wzCurrentDirectory,
1909 __in WORD wShowWindow,
1910 __out LPPROCESS_INFORMATION pProcessInformation
1911 )
1912 {
1913 HRESULT hr = S_OK;
1914 STARTUPINFOW si = { };
1915 size_t cchCurrentDirectory = 0;
1916
1917 // CreateProcessW has undocumented MAX_PATH restriction for lpCurrentDirectory even when long path support is enabled.
1918 if (wzCurrentDirectory && FAILED(::StringCchLengthW(wzCurrentDirectory, MAX_PATH - 1, &cchCurrentDirectory)))
1919 {
1920 wzCurrentDirectory = NULL;
1921 }
1922
1923 si.cb = sizeof(si);
1924 si.wShowWindow = wShowWindow;
1925
1926 if (!vpfnCreateProcessW(wzApplicationName, sczCommandLine, NULL, NULL, fInheritHandles, dwCreationFlags, NULL, wzCurrentDirectory, &si, pProcessInformation))
1927 {
1928 ExitWithLastError(hr, "CreateProcessW failed with return code: %d", Dutil_er);
1929 }
1930
1931 LExit:
1932 return hr;
1933 }
1934
1935 extern "C" HRESULT DAPI CoreWaitForProcCompletion(
1936 __in HANDLE hProcess,
1937 __in DWORD dwTimeout,
1938 __out DWORD* pdwReturnCode
1939 )
1940 {
1941 return vpfnProcWaitForCompletion(hProcess, dwTimeout, pdwReturnCode);
1942 }
1943
1944 extern "C" HRESULT DAPI CoreCloseElevatedLoggingThread(
1945 __in BURN_ENGINE_STATE* pEngineState
1946 )
1947 {
1948 HRESULT hr = S_OK;
1949
1950 if (INVALID_HANDLE_VALUE == pEngineState->elevatedLoggingContext.hThread)
1951 {
1952 ExitFunction();
1953 }
1954
1955 if (!::SetEvent(pEngineState->elevatedLoggingContext.hFinishedEvent))
1956 {
1957 ExitWithLastError(hr, "Failed to set log finished event.");
1958 }
1959
1960 hr = AppWaitForSingleObject(pEngineState->elevatedLoggingContext.hThread, 5 * 60 * 1000); // TODO: is 5 minutes good?
1961 ExitOnFailure(hr, "Failed to wait for elevated logging thread.");
1962
1963 LExit:
1964 return hr;
1965 }
1966
1967 extern "C" HRESULT DAPI CoreWaitForUnelevatedLoggingThread(
1968 __in HANDLE hUnelevatedLoggingThread
1969 )
1970 {
1971 HRESULT hr = S_OK;
1972
1973 if (INVALID_HANDLE_VALUE == hUnelevatedLoggingThread)
1974 {
1975 ExitFunction();
1976 }
1977
1978 // Give the thread 15 seconds to exit.
1979 hr = AppWaitForSingleObject(hUnelevatedLoggingThread, 15 * 1000);
1980 ExitOnFailure(hr, "Failed to wait for unelevated logging thread.");
1981
1982 LExit:
1983 return hr;
1984 }
1985
1986 // internal helper functions
1987
1988 static HRESULT AppendEscapedArgumentToCommandLine(
1989 __in_z LPCWSTR wzEscapedArgument,
1990 __deref_inout_z LPWSTR* psczCommandLine,
1991 __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine
1992 )
1993 {
1994 HRESULT hr = S_OK;
1995
1996 // If there is already data in the command line,
1997 // append a space before appending the argument.
1998 if (*psczCommandLine && **psczCommandLine)
1999 {
2000 hr = StrAllocConcatSecure(psczCommandLine, L" ", 0);
2001 ExitOnFailure(hr, "Failed to append space to command line with existing data.");
2002 }
2003
2004 hr = StrAllocConcatSecure(psczCommandLine, wzEscapedArgument, 0);
2005 ExitOnFailure(hr, "Failed to append escaped command line argument.");
2006
2007 if (psczObfuscatedCommandLine)
2008 {
2009 if (*psczObfuscatedCommandLine && **psczObfuscatedCommandLine)
2010 {
2011 hr = StrAllocConcat(psczObfuscatedCommandLine, L" ", 0);
2012 ExitOnFailure(hr, "Failed to append space to obfuscated command line with existing data.");
2013 }
2014
2015 hr = StrAllocConcat(psczObfuscatedCommandLine, wzEscapedArgument, 0);
2016 ExitOnFailure(hr, "Failed to append escaped argument to obfuscated command line.");
2017 }
2018
2019 LExit:
2020 return hr;
2021 }
2022
2023 static HRESULT EscapeAndAppendArgumentToCommandLineFormatted(
2024 __deref_inout_z LPWSTR* psczCommandLine,
2025 __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine,
2026 __in __format_string LPCWSTR wzFormat,
2027 ...
2028 )
2029 {
2030 HRESULT hr = S_OK;
2031 va_list args;
2032
2033 va_start(args, wzFormat);
2034 hr = EscapeAndAppendArgumentToCommandLineFormattedArgs(psczCommandLine, psczObfuscatedCommandLine, wzFormat, args);
2035 va_end(args);
2036
2037 return hr;
2038 }
2039
2040 static HRESULT EscapeAndAppendArgumentToCommandLineFormattedArgs(
2041 __deref_inout_z LPWSTR* psczCommandLine,
2042 __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine,
2043 __in __format_string LPCWSTR wzFormat,
2044 __in va_list args
2045 )
2046 {
2047 HRESULT hr = S_OK;
2048 LPWSTR sczArgument = NULL;
2049
2050 hr = AppEscapeCommandLineArgumentFormattedArgs(&sczArgument, wzFormat, args);
2051 ExitOnFailure(hr, "Failed to escape the argument for the command line.");
2052
2053 hr = AppendEscapedArgumentToCommandLine(sczArgument, psczCommandLine, psczObfuscatedCommandLine);
2054
2055 LExit:
2056 ReleaseStr(sczArgument);
2057
2058 return hr;
2059 }
2060
2061 static HRESULT GetSanitizedCommandLine(
2062 __in BURN_ENGINE_COMMAND* pInternalCommand,
2063 __in BOOTSTRAPPER_COMMAND* pCommand,
2064 __in BURN_VARIABLES* pVariables,
2065 __inout_z LPWSTR* psczSanitizedCommandLine
2066 )
2067 {
2068 HRESULT hr = S_OK;
2069 DWORD dwUnknownArgIndex = 0;
2070 DWORD dwSecretArgIndex = 0;
2071 BOOL fHidden = FALSE;
2072 LPWSTR sczSanitizedArgument = NULL;
2073 LPWSTR sczVariableName = NULL;
2074 int argc = pInternalCommand->argc;
2075 LPWSTR* argv = pInternalCommand->argv;
2076 DWORD cUnknownArgs = pInternalCommand->cUnknownArgs;
2077 int* rgUnknownArgs = pInternalCommand->rgUnknownArgs;
2078 DWORD cSecretArgs = pInternalCommand->cSecretArgs;
2079 int* rgSecretArgs = pInternalCommand->rgSecretArgs;
2080
2081 for (int i = 0; i < argc; ++i)
2082 {
2083 fHidden = FALSE;
2084
2085 if (dwUnknownArgIndex < cUnknownArgs && rgUnknownArgs[dwUnknownArgIndex] == i)
2086 {
2087 ++dwUnknownArgIndex;
2088
2089 if (argv[i][0] != L'-' && argv[i][0] != L'/')
2090 {
2091 const wchar_t* pwc = wcschr(argv[i], L'=');
2092 if (pwc)
2093 {
2094 hr = StrAllocString(&sczVariableName, argv[i], pwc - argv[i]);
2095 ExitOnFailure(hr, "Failed to copy variable name.");
2096
2097 fHidden = VariableIsHiddenCommandLine(pVariables, sczVariableName);
2098
2099 if (fHidden)
2100 {
2101 hr = StrAllocFormatted(&sczSanitizedArgument, L"%ls=*****", sczVariableName);
2102 ExitOnFailure(hr, "Failed to copy sanitized unknown argument.");
2103 }
2104 }
2105 }
2106
2107 // Remember command-line switch to pass off to BA.
2108 AppAppendCommandLineArgument(&pCommand->wzCommandLine, argv[i]);
2109 }
2110 else if (dwSecretArgIndex < cSecretArgs && rgSecretArgs[dwSecretArgIndex] == i)
2111 {
2112 ++dwSecretArgIndex;
2113 fHidden = TRUE;
2114
2115 hr = StrAllocString(&sczSanitizedArgument, L"*****", 0);
2116 ExitOnFailure(hr, "Failed to copy sanitized secret argument.");
2117 }
2118
2119 if (fHidden)
2120 {
2121 AppAppendCommandLineArgument(psczSanitizedCommandLine, sczSanitizedArgument);
2122 }
2123 else
2124 {
2125 AppAppendCommandLineArgument(psczSanitizedCommandLine, argv[i]);
2126 }
2127 }
2128
2129 LExit:
2130 ReleaseStr(sczVariableName);
2131 ReleaseStr(sczSanitizedArgument);
2132
2133 return hr;
2134 }
2135
2136 static HRESULT ParsePipeConnection(
2137 __in_ecount(3) LPWSTR* rgArgs,
2138 __in BURN_PIPE_CONNECTION* pConnection
2139 )
2140 {
2141 HRESULT hr = S_OK;
2142
2143 hr = StrAllocString(&pConnection->sczName, rgArgs[0], 0);
2144 ExitOnFailure(hr, "Failed to copy connection name from command line.");
2145
2146 hr = StrAllocString(&pConnection->sczSecret, rgArgs[1], 0);
2147 ExitOnFailure(hr, "Failed to copy connection secret from command line.");
2148
2149 hr = StrStringToUInt32(rgArgs[2], 0, reinterpret_cast<UINT*>(&pConnection->dwProcessId));
2150 ExitOnFailure(hr, "Failed to copy parent process id from command line.");
2151
2152 LExit:
2153 return hr;
2154 }
2155
2156 static HRESULT DetectPackage(
2157 __in BURN_ENGINE_STATE* pEngineState,
2158 __in BURN_PACKAGE* pPackage
2159 )
2160 {
2161 HRESULT hr = S_OK;
2162 BOOL fBegan = FALSE;
2163
2164 fBegan = TRUE;
2165 hr = BACallbackOnDetectPackageBegin(&pEngineState->userExperience, pPackage->sczId);
2166 ExitOnRootFailure(hr, "BA aborted detect package begin.");
2167
2168 // Detect the cache state of the package.
2169 hr = DetectPackagePayloadsCached(&pEngineState->cache, pPackage);
2170 ExitOnFailure(hr, "Failed to detect if payloads are all cached for package: %ls", pPackage->sczId);
2171
2172 // Use the correct engine to detect the package.
2173 switch (pPackage->type)
2174 {
2175 case BURN_PACKAGE_TYPE_BUNDLE:
2176 hr = BundlePackageEngineDetectPackage(pPackage, &pEngineState->registration, &pEngineState->userExperience);
2177 break;
2178
2179 case BURN_PACKAGE_TYPE_EXE:
2180 hr = ExeEngineDetectPackage(pPackage, &pEngineState->registration, &pEngineState->variables);
2181 break;
2182
2183 case BURN_PACKAGE_TYPE_MSI:
2184 hr = MsiEngineDetectPackage(pPackage, &pEngineState->registration, &pEngineState->userExperience);
2185 break;
2186
2187 case BURN_PACKAGE_TYPE_MSP:
2188 hr = MspEngineDetectPackage(pPackage, &pEngineState->registration, &pEngineState->userExperience);
2189 break;
2190
2191 case BURN_PACKAGE_TYPE_MSU:
2192 hr = MsuEngineDetectPackage(pPackage, &pEngineState->registration, &pEngineState->variables);
2193 break;
2194
2195 default:
2196 ExitWithRootFailure(hr, E_NOTIMPL, "Package type not supported by detect yet.");
2197 }
2198
2199 LExit:
2200 if (FAILED(hr))
2201 {
2202 LogErrorId(hr, MSG_FAILED_DETECT_PACKAGE, pPackage->sczId, NULL, NULL);
2203 }
2204
2205 if (fBegan)
2206 {
2207 BACallbackOnDetectPackageComplete(&pEngineState->userExperience, pPackage->sczId, hr, pPackage->currentState, pPackage->fCached);
2208 }
2209
2210 return hr;
2211 }
2212
2213 static HRESULT DetectPackagePayloadsCached(
2214 __in BURN_CACHE* pCache,
2215 __in BURN_PACKAGE* pPackage
2216 )
2217 {
2218 HRESULT hr = S_OK;
2219 LPWSTR sczCachePath = NULL;
2220 BOOL fCached = FALSE; // assume the package is not cached.
2221 LPWSTR sczPayloadCachePath = NULL;
2222
2223 if (pPackage->sczCacheId && *pPackage->sczCacheId)
2224 {
2225 hr = CacheGetCompletedPath(pCache, pPackage->fPerMachine, pPackage->sczCacheId, &sczCachePath);
2226 ExitOnFailure(hr, "Failed to get completed cache path.");
2227
2228 // If the cached directory exists, we have something.
2229 if (DirExists(sczCachePath, NULL))
2230 {
2231 // Check all payloads to see if any exist.
2232 for (DWORD i = 0; i < pPackage->payloads.cItems; ++i)
2233 {
2234 BURN_PAYLOAD* pPayload = pPackage->payloads.rgItems[i].pPayload;
2235
2236 hr = PathConcatRelativeToFullyQualifiedBase(sczCachePath, pPayload->sczFilePath, &sczPayloadCachePath);
2237 ExitOnFailure(hr, "Failed to concat payload cache path.");
2238
2239 if (FileExistsEx(sczPayloadCachePath, NULL))
2240 {
2241 fCached = TRUE;
2242 break;
2243 }
2244 else
2245 {
2246 LogId(REPORT_STANDARD, MSG_DETECT_PACKAGE_NOT_FULLY_CACHED, pPackage->sczId, pPayload->sczKey);
2247 }
2248 }
2249 }
2250 }
2251
2252 pPackage->fCached = fCached;
2253
2254 if (pPackage->fCanAffectRegistration)
2255 {
2256 pPackage->cacheRegistrationState = pPackage->fCached ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
2257 }
2258
2259 LExit:
2260 ReleaseStr(sczPayloadCachePath);
2261 ReleaseStr(sczCachePath);
2262 return hr;
2263 }
2264
2265 static DWORD WINAPI CacheThreadProc(
2266 __in LPVOID lpThreadParameter
2267 )
2268 {
2269 HRESULT hr = S_OK;
2270 BURN_CACHE_THREAD_CONTEXT* pContext = reinterpret_cast<BURN_CACHE_THREAD_CONTEXT*>(lpThreadParameter);
2271 BURN_ENGINE_STATE* pEngineState = pContext->pEngineState;
2272 BOOL fComInitialized = FALSE;
2273
2274 // initialize COM
2275 hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
2276 ExitOnFailure(hr, "Failed to initialize COM on cache thread.");
2277 fComInitialized = TRUE;
2278
2279 // cache packages
2280 hr = ApplyCache(pEngineState->section.hSourceEngineFile, &pEngineState->userExperience, &pEngineState->variables, &pEngineState->plan, pEngineState->companionConnection.hCachePipe, pContext->pApplyContext);
2281
2282 LExit:
2283 BootstrapperApplicationExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that cache completed.
2284
2285 if (fComInitialized)
2286 {
2287 ::CoUninitialize();
2288 }
2289
2290 return (DWORD)hr;
2291 }
2292
2293 static DWORD WINAPI LoggingThreadProc(
2294 __in LPVOID lpThreadParameter
2295 )
2296 {
2297 HRESULT hr = S_OK;
2298 BURN_ENGINE_STATE* pEngineState = reinterpret_cast<BURN_ENGINE_STATE*>(lpThreadParameter);
2299 BURN_PIPE_RESULT result = { };
2300
2301 hr = BurnPipePumpMessages(pEngineState->companionConnection.hLoggingPipe, NULL, NULL, &result);
2302 ExitOnFailure(hr, "Failed to pump logging messages for elevated process.");
2303
2304 hr = (HRESULT)result.dwResult;
2305
2306 LExit:
2307
2308 return (DWORD)hr;
2309 }
2310
2311 static void LogPackages(
2312 __in_opt const BURN_PACKAGE* pUpgradeBundlePackage,
2313 __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage,
2314 __in const BURN_PACKAGES* pPackages,
2315 __in const BURN_RELATED_BUNDLES* pRelatedBundles,
2316 __in const BOOTSTRAPPER_ACTION action
2317 )
2318 {
2319 BOOL fUninstalling = BOOTSTRAPPER_ACTION_UNINSTALL == action || BOOTSTRAPPER_ACTION_UNSAFE_UNINSTALL == action;
2320
2321 if (pUpgradeBundlePackage)
2322 {
2323 LogId(REPORT_STANDARD, MSG_PLANNED_UPGRADE_BUNDLE, pUpgradeBundlePackage->sczId, LoggingRequestStateToString(pUpgradeBundlePackage->defaultRequested), LoggingRequestStateToString(pUpgradeBundlePackage->requested), LoggingActionStateToString(pUpgradeBundlePackage->execute), LoggingActionStateToString(pUpgradeBundlePackage->rollback), LoggingDependencyActionToString(pUpgradeBundlePackage->dependencyExecute));
2324 }
2325 else if (pForwardCompatibleBundlePackage)
2326 {
2327 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));
2328 }
2329 else
2330 {
2331 // Display related bundles first if uninstalling.
2332 if (fUninstalling)
2333 {
2334 LogRelatedBundles(pRelatedBundles, TRUE);
2335 }
2336
2337 // Display all the packages in the log.
2338 for (DWORD i = 0; i < pPackages->cPackages; ++i)
2339 {
2340 const DWORD iPackage = fUninstalling ? pPackages->cPackages - 1 - i : i;
2341 const BURN_PACKAGE* pPackage = &pPackages->rgPackages[iPackage];
2342
2343 if (!fUninstalling && pPackage->pRollbackBoundaryForward)
2344 {
2345 LogRollbackBoundary(pPackage->pRollbackBoundaryForward);
2346 }
2347 else if (fUninstalling && pPackage->pRollbackBoundaryBackward)
2348 {
2349 LogRollbackBoundary(pPackage->pRollbackBoundaryBackward);
2350 }
2351
2352 LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingCacheTypeToString(pPackage->authoredCacheType), LoggingCacheTypeToString(pPackage->cacheType), LoggingPlannedCacheToString(pPackage), LoggingBoolToString(pPackage->fPlannedUncache), LoggingDependencyActionToString(pPackage->dependencyExecute), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedInstallRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedCacheRegistrationState));
2353
2354 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
2355 {
2356 if (pPackage->Msi.cFeatures)
2357 {
2358 LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURES, pPackage->Msi.cFeatures, pPackage->sczId);
2359
2360 for (DWORD j = 0; j < pPackage->Msi.cFeatures; ++j)
2361 {
2362 const BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[j];
2363
2364 LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURE, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState), LoggingMsiFeatureStateToString(pFeature->defaultRequested), LoggingMsiFeatureStateToString(pFeature->requested), LoggingMsiFeatureActionToString(pFeature->execute), LoggingMsiFeatureActionToString(pFeature->rollback));
2365 }
2366 }
2367
2368 if (pPackage->Msi.cSlipstreamMspPackages)
2369 {
2370 LogId(REPORT_STANDARD, MSG_PLANNED_SLIPSTREAMED_MSP_TARGETS, pPackage->Msi.cSlipstreamMspPackages, pPackage->sczId);
2371
2372 for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j)
2373 {
2374 const BURN_SLIPSTREAM_MSP* pSlipstreamMsp = &pPackage->Msi.rgSlipstreamMsps[j];
2375
2376 LogId(REPORT_STANDARD, MSG_PLANNED_SLIPSTREAMED_MSP_TARGET, pSlipstreamMsp->pMspPackage->sczId, LoggingActionStateToString(pSlipstreamMsp->execute), LoggingActionStateToString(pSlipstreamMsp->rollback));
2377 }
2378 }
2379
2380 if (pPackage->compatiblePackage.fRemove)
2381 {
2382 LogId(REPORT_STANDARD, MSG_PLANNED_ORPHAN_PACKAGE_FROM_PROVIDER, pPackage->sczId, pPackage->compatiblePackage.compatibleEntry.sczId, pPackage->Msi.sczProductCode);
2383 }
2384 }
2385 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.cTargetProductCodes)
2386 {
2387 LogId(REPORT_STANDARD, MSG_PLANNED_MSP_TARGETS, pPackage->Msp.cTargetProductCodes, pPackage->sczId);
2388
2389 for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j)
2390 {
2391 const BURN_MSPTARGETPRODUCT* pTargetProduct = &pPackage->Msp.rgTargetProducts[j];
2392
2393 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));
2394 }
2395 }
2396 }
2397
2398 // Display related bundles last if not uninstalling.
2399 if (!fUninstalling)
2400 {
2401 LogRelatedBundles(pRelatedBundles, FALSE);
2402 }
2403 }
2404 }
2405
2406 static void LogRelatedBundles(
2407 __in const BURN_RELATED_BUNDLES* pRelatedBundles,
2408 __in BOOL fReverse
2409 )
2410 {
2411 if (0 < pRelatedBundles->cRelatedBundles)
2412 {
2413 for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i)
2414 {
2415 const DWORD iRelatedBundle = fReverse ? pRelatedBundles->cRelatedBundles - 1 - i : i;
2416 const BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + iRelatedBundle;
2417 const BURN_PACKAGE* pPackage = &pRelatedBundle->package;
2418
2419 if (pRelatedBundle->fPlannable)
2420 {
2421 LogId(REPORT_STANDARD, MSG_PLANNED_RELATED_BUNDLE, pPackage->sczId, LoggingRelationTypeToString(pRelatedBundle->detectRelationType), LoggingPlanRelationTypeToString(pRelatedBundle->defaultPlanRelationType), LoggingPlanRelationTypeToString(pRelatedBundle->planRelationType), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingRequestStateToString(pRelatedBundle->defaultRequestedRestore), LoggingRequestStateToString(pRelatedBundle->requestedRestore), LoggingActionStateToString(pRelatedBundle->restore), LoggingDependencyActionToString(pPackage->dependencyExecute));
2422 }
2423 }
2424 }
2425 }
2426
2427 static void LogRollbackBoundary(
2428 __in const BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
2429 )
2430 {
2431 LogId(REPORT_STANDARD, MSG_PLANNED_ROLLBACK_BOUNDARY, pRollbackBoundary->sczId, LoggingBoolToString(pRollbackBoundary->fVital), LoggingBoolToString(pRollbackBoundary->fTransaction), LoggingBoolToString(pRollbackBoundary->fTransactionAuthored));
2432 }