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