aboutsummaryrefslogtreecommitdiff
path: root/src/burn/engine/relatedbundle.cpp
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-04-22 17:06:54 -0700
committerRob Mensching <rob@firegiant.com>2021-04-29 16:36:06 -0700
commitaf10c45d7b3a44af0b461a557847fe03263dcc10 (patch)
tree6a5c1532304782c36ffe4200b38f3afb76789a43 /src/burn/engine/relatedbundle.cpp
parent9c2aed97299fb96aeee3f1471ce40225437aaecf (diff)
downloadwix-af10c45d7b3a44af0b461a557847fe03263dcc10.tar.gz
wix-af10c45d7b3a44af0b461a557847fe03263dcc10.tar.bz2
wix-af10c45d7b3a44af0b461a557847fe03263dcc10.zip
Move burn into burn
Diffstat (limited to 'src/burn/engine/relatedbundle.cpp')
-rw-r--r--src/burn/engine/relatedbundle.cpp483
1 files changed, 483 insertions, 0 deletions
diff --git a/src/burn/engine/relatedbundle.cpp b/src/burn/engine/relatedbundle.cpp
new file mode 100644
index 00000000..d3c856a6
--- /dev/null
+++ b/src/burn/engine/relatedbundle.cpp
@@ -0,0 +1,483 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5// internal function declarations
6
7static HRESULT LoadIfRelatedBundle(
8 __in BOOL fPerMachine,
9 __in HKEY hkUninstallKey,
10 __in_z LPCWSTR sczRelatedBundleId,
11 __in BURN_REGISTRATION* pRegistration,
12 __in BURN_RELATED_BUNDLES* pRelatedBundles
13 );
14static HRESULT DetermineRelationType(
15 __in HKEY hkBundleId,
16 __in BURN_REGISTRATION* pRegistration,
17 __out BOOTSTRAPPER_RELATION_TYPE* pRelationType
18 );
19static HRESULT LoadRelatedBundleFromKey(
20 __in_z LPCWSTR wzRelatedBundleId,
21 __in HKEY hkBundleId,
22 __in BOOL fPerMachine,
23 __in BOOTSTRAPPER_RELATION_TYPE relationType,
24 __inout BURN_RELATED_BUNDLE *pRelatedBundle
25 );
26
27
28// function definitions
29
30extern "C" HRESULT RelatedBundlesInitializeForScope(
31 __in BOOL fPerMachine,
32 __in BURN_REGISTRATION* pRegistration,
33 __in BURN_RELATED_BUNDLES* pRelatedBundles
34 )
35{
36 HRESULT hr = S_OK;
37 HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
38 HKEY hkUninstallKey = NULL;
39 LPWSTR sczRelatedBundleId = NULL;
40
41 hr = RegOpen(hkRoot, BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstallKey);
42 if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
43 {
44 ExitFunction1(hr = S_OK);
45 }
46 ExitOnFailure(hr, "Failed to open uninstall registry key.");
47
48 for (DWORD dwIndex = 0; /* exit via break below */; ++dwIndex)
49 {
50 hr = RegKeyEnum(hkUninstallKey, dwIndex, &sczRelatedBundleId);
51 if (E_NOMOREITEMS == hr)
52 {
53 hr = S_OK;
54 break;
55 }
56 ExitOnFailure(hr, "Failed to enumerate uninstall key for related bundles.");
57
58 // If we did not find our bundle id, try to load the subkey as a related bundle.
59 if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczRelatedBundleId, -1, pRegistration->sczId, -1))
60 {
61 // Ignore failures here since we'll often find products that aren't actually
62 // related bundles (or even bundles at all).
63 HRESULT hrRelatedBundle = LoadIfRelatedBundle(fPerMachine, hkUninstallKey, sczRelatedBundleId, pRegistration, pRelatedBundles);
64 UNREFERENCED_PARAMETER(hrRelatedBundle);
65 }
66 }
67
68LExit:
69 ReleaseStr(sczRelatedBundleId);
70 ReleaseRegKey(hkUninstallKey);
71
72 return hr;
73}
74
75extern "C" void RelatedBundlesUninitialize(
76 __in BURN_RELATED_BUNDLES* pRelatedBundles
77 )
78{
79 if (pRelatedBundles->rgRelatedBundles)
80 {
81 for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i)
82 {
83 BURN_PACKAGE* pPackage = &pRelatedBundles->rgRelatedBundles[i].package;
84
85 for (DWORD j = 0; j < pPackage->payloads.cItems; ++j)
86 {
87 PayloadUninitialize(pPackage->payloads.rgItems[j].pPayload);
88 }
89
90 PackageUninitialize(pPackage);
91 ReleaseStr(pRelatedBundles->rgRelatedBundles[i].sczTag);
92 }
93
94 MemFree(pRelatedBundles->rgRelatedBundles);
95 }
96
97 memset(pRelatedBundles, 0, sizeof(BURN_RELATED_BUNDLES));
98}
99
100
101// internal helper functions
102
103static HRESULT LoadIfRelatedBundle(
104 __in BOOL fPerMachine,
105 __in HKEY hkUninstallKey,
106 __in_z LPCWSTR sczRelatedBundleId,
107 __in BURN_REGISTRATION* pRegistration,
108 __in BURN_RELATED_BUNDLES* pRelatedBundles
109 )
110{
111 HRESULT hr = S_OK;
112 HKEY hkBundleId = NULL;
113 BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_NONE;
114
115 hr = RegOpen(hkUninstallKey, sczRelatedBundleId, KEY_READ, &hkBundleId);
116 ExitOnFailure(hr, "Failed to open uninstall key for potential related bundle: %ls", sczRelatedBundleId);
117
118 hr = DetermineRelationType(hkBundleId, pRegistration, &relationType);
119 if (FAILED(hr) || BOOTSTRAPPER_RELATION_NONE == relationType)
120 {
121 // Must not be a related bundle.
122 hr = E_NOTFOUND;
123 }
124 else // load the related bundle.
125 {
126 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5);
127 ExitOnFailure(hr, "Failed to ensure there is space for related bundles.");
128
129 BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles;
130
131 hr = LoadRelatedBundleFromKey(sczRelatedBundleId, hkBundleId, fPerMachine, relationType, pRelatedBundle);
132 ExitOnFailure(hr, "Failed to initialize package from related bundle id: %ls", sczRelatedBundleId);
133
134 ++pRelatedBundles->cRelatedBundles;
135 }
136
137LExit:
138 ReleaseRegKey(hkBundleId);
139
140 return hr;
141}
142
143static HRESULT DetermineRelationType(
144 __in HKEY hkBundleId,
145 __in BURN_REGISTRATION* pRegistration,
146 __out BOOTSTRAPPER_RELATION_TYPE* pRelationType
147 )
148{
149 HRESULT hr = S_OK;
150 LPWSTR* rgsczUpgradeCodes = NULL;
151 DWORD cUpgradeCodes = 0;
152 STRINGDICT_HANDLE sdUpgradeCodes = NULL;
153 LPWSTR* rgsczAddonCodes = NULL;
154 DWORD cAddonCodes = 0;
155 STRINGDICT_HANDLE sdAddonCodes = NULL;
156 LPWSTR* rgsczDetectCodes = NULL;
157 DWORD cDetectCodes = 0;
158 STRINGDICT_HANDLE sdDetectCodes = NULL;
159 LPWSTR* rgsczPatchCodes = NULL;
160 DWORD cPatchCodes = 0;
161 STRINGDICT_HANDLE sdPatchCodes = NULL;
162
163 *pRelationType = BOOTSTRAPPER_RELATION_NONE;
164
165 // All remaining operations should treat all related bundles as non-vital.
166 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes, &cUpgradeCodes);
167 if (HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE) == hr)
168 {
169 TraceError(hr, "Failed to read upgrade codes as REG_MULTI_SZ. Trying again as REG_SZ in case of older bundles.");
170
171 rgsczUpgradeCodes = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR), TRUE));
172 ExitOnNull(rgsczUpgradeCodes, hr, E_OUTOFMEMORY, "Failed to allocate list for a single upgrade code from older bundle.");
173
174 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes[0]);
175 if (SUCCEEDED(hr))
176 {
177 cUpgradeCodes = 1;
178 }
179 }
180
181 // Compare upgrade codes.
182 if (SUCCEEDED(hr))
183 {
184 hr = DictCreateStringListFromArray(&sdUpgradeCodes, rgsczUpgradeCodes, cUpgradeCodes, DICT_FLAG_CASEINSENSITIVE);
185 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "upgrade codes");
186
187 // Upgrade relationship: when their upgrade codes match our upgrade codes.
188 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
189 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
190 {
191 hr = S_OK;
192 }
193 else
194 {
195 ExitOnFailure(hr, "Failed to do array search for upgrade code match.");
196
197 *pRelationType = BOOTSTRAPPER_RELATION_UPGRADE;
198 ExitFunction();
199 }
200
201 // Detect relationship: when their upgrade codes match our detect codes.
202 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
203 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
204 {
205 hr = S_OK;
206 }
207 else
208 {
209 ExitOnFailure(hr, "Failed to do array search for detect code match.");
210
211 *pRelationType = BOOTSTRAPPER_RELATION_DETECT;
212 ExitFunction();
213 }
214
215 // Dependent relationship: when their upgrade codes match our addon codes.
216 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes);
217 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
218 {
219 hr = S_OK;
220 }
221 else
222 {
223 ExitOnFailure(hr, "Failed to do array search for addon code match.");
224
225 *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
226 ExitFunction();
227 }
228
229 // Dependent relationship: when their upgrade codes match our patch codes.
230 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes);
231 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
232 {
233 hr = S_OK;
234 }
235 else
236 {
237 ExitOnFailure(hr, "Failed to do array search for addon code match.");
238
239 *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
240 ExitFunction();
241 }
242
243 ReleaseNullDict(sdUpgradeCodes);
244 ReleaseNullStrArray(rgsczUpgradeCodes, cUpgradeCodes);
245 }
246
247 // Compare addon codes.
248 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, &rgsczAddonCodes, &cAddonCodes);
249 if (SUCCEEDED(hr))
250 {
251 hr = DictCreateStringListFromArray(&sdAddonCodes, rgsczAddonCodes, cAddonCodes, DICT_FLAG_CASEINSENSITIVE);
252 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "addon codes");
253
254 // Addon relationship: when their addon codes match our detect codes.
255 hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
256 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
257 {
258 hr = S_OK;
259 }
260 else
261 {
262 ExitOnFailure(hr, "Failed to do array search for addon code match.");
263
264 *pRelationType = BOOTSTRAPPER_RELATION_ADDON;
265 ExitFunction();
266 }
267
268 // Addon relationship: when their addon codes match our upgrade codes.
269 hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
270 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
271 {
272 hr = S_OK;
273 }
274 else
275 {
276 ExitOnFailure(hr, "Failed to do array search for addon code match.");
277
278 *pRelationType = BOOTSTRAPPER_RELATION_ADDON;
279 ExitFunction();
280 }
281
282 ReleaseNullDict(sdAddonCodes);
283 ReleaseNullStrArray(rgsczAddonCodes, cAddonCodes);
284 }
285
286 // Compare patch codes.
287 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, &rgsczPatchCodes, &cPatchCodes);
288 if (SUCCEEDED(hr))
289 {
290 hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE);
291 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "patch codes");
292
293 // Patch relationship: when their patch codes match our detect codes.
294 hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
295 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
296 {
297 hr = S_OK;
298 }
299 else
300 {
301 ExitOnFailure(hr, "Failed to do array search for patch code match.");
302
303 *pRelationType = BOOTSTRAPPER_RELATION_PATCH;
304 ExitFunction();
305 }
306
307 // Patch relationship: when their patch codes match our upgrade codes.
308 hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
309 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
310 {
311 hr = S_OK;
312 }
313 else
314 {
315 ExitOnFailure(hr, "Failed to do array search for patch code match.");
316
317 *pRelationType = BOOTSTRAPPER_RELATION_PATCH;
318 ExitFunction();
319 }
320
321 ReleaseNullDict(sdPatchCodes);
322 ReleaseNullStrArray(rgsczPatchCodes, cPatchCodes);
323 }
324
325 // Compare detect codes.
326 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, &rgsczDetectCodes, &cDetectCodes);
327 if (SUCCEEDED(hr))
328 {
329 hr = DictCreateStringListFromArray(&sdDetectCodes, rgsczDetectCodes, cDetectCodes, DICT_FLAG_CASEINSENSITIVE);
330 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "detect codes");
331
332 // Detect relationship: when their detect codes match our detect codes.
333 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
334 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
335 {
336 hr = S_OK;
337 }
338 else
339 {
340 ExitOnFailure(hr, "Failed to do array search for detect code match.");
341
342 *pRelationType = BOOTSTRAPPER_RELATION_DETECT;
343 ExitFunction();
344 }
345
346 // Dependent relationship: when their detect codes match our addon codes.
347 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes);
348 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
349 {
350 hr = S_OK;
351 }
352 else
353 {
354 ExitOnFailure(hr, "Failed to do array search for addon code match.");
355
356 *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
357 ExitFunction();
358 }
359
360 // Dependent relationship: when their detect codes match our patch codes.
361 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes);
362 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
363 {
364 hr = S_OK;
365 }
366 else
367 {
368 ExitOnFailure(hr, "Failed to do array search for addon code match.");
369
370 *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
371 ExitFunction();
372 }
373
374 ReleaseNullDict(sdDetectCodes);
375 ReleaseNullStrArray(rgsczDetectCodes, cDetectCodes);
376 }
377
378LExit:
379 if (SUCCEEDED(hr) && BOOTSTRAPPER_RELATION_NONE == *pRelationType)
380 {
381 hr = E_NOTFOUND;
382 }
383
384 ReleaseDict(sdUpgradeCodes);
385 ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes);
386 ReleaseDict(sdAddonCodes);
387 ReleaseStrArray(rgsczAddonCodes, cAddonCodes);
388 ReleaseDict(sdDetectCodes);
389 ReleaseStrArray(rgsczDetectCodes, cDetectCodes);
390 ReleaseDict(sdPatchCodes);
391 ReleaseStrArray(rgsczPatchCodes, cPatchCodes);
392
393 return hr;
394}
395
396static HRESULT LoadRelatedBundleFromKey(
397 __in_z LPCWSTR wzRelatedBundleId,
398 __in HKEY hkBundleId,
399 __in BOOL fPerMachine,
400 __in BOOTSTRAPPER_RELATION_TYPE relationType,
401 __inout BURN_RELATED_BUNDLE* pRelatedBundle
402 )
403{
404 HRESULT hr = S_OK;
405 DWORD64 qwEngineVersion = 0;
406 LPWSTR sczBundleVersion = NULL;
407 LPWSTR sczCachePath = NULL;
408 BOOL fCached = FALSE;
409 DWORD64 qwFileSize = 0;
410 BURN_DEPENDENCY_PROVIDER dependencyProvider = { };
411
412 hr = RegReadVersion(hkBundleId, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, &qwEngineVersion);
413 if (FAILED(hr))
414 {
415 qwEngineVersion = 0;
416 hr = S_OK;
417 }
418
419 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, &sczBundleVersion);
420 ExitOnFailure(hr, "Failed to read version from registry for bundle: %ls", wzRelatedBundleId);
421
422 hr = VerParseVersion(sczBundleVersion, 0, FALSE, &pRelatedBundle->pVersion);
423 ExitOnFailure(hr, "Failed to parse pseudo bundle version: %ls", sczBundleVersion);
424
425 if (pRelatedBundle->pVersion->fInvalid)
426 {
427 LogId(REPORT_WARNING, MSG_RELATED_PACKAGE_INVALID_VERSION, wzRelatedBundleId, sczBundleVersion);
428 }
429
430 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, &sczCachePath);
431 ExitOnFailure(hr, "Failed to read cache path from registry for bundle: %ls", wzRelatedBundleId);
432
433 if (FileExistsEx(sczCachePath, NULL))
434 {
435 fCached = TRUE;
436 }
437 else
438 {
439 LogId(REPORT_STANDARD, MSG_DETECT_RELATED_BUNDLE_NOT_CACHED, wzRelatedBundleId, sczCachePath);
440 }
441
442 pRelatedBundle->fPlannable = fCached;
443
444 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, &dependencyProvider.sczKey);
445 if (E_FILENOTFOUND != hr)
446 {
447 ExitOnFailure(hr, "Failed to read provider key from registry for bundle: %ls", wzRelatedBundleId);
448
449 dependencyProvider.fImported = TRUE;
450
451 hr = StrAllocString(&dependencyProvider.sczVersion, pRelatedBundle->pVersion->sczVersion, 0);
452 ExitOnFailure(hr, "Failed to copy version for bundle: %ls", wzRelatedBundleId);
453
454 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, &dependencyProvider.sczDisplayName);
455 if (E_FILENOTFOUND != hr)
456 {
457 ExitOnFailure(hr, "Failed to copy display name for bundle: %ls", wzRelatedBundleId);
458 }
459 }
460
461 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, &pRelatedBundle->sczTag);
462 if (E_FILENOTFOUND == hr)
463 {
464 hr = S_OK;
465 }
466 ExitOnFailure(hr, "Failed to read tag from registry for bundle: %ls", wzRelatedBundleId);
467
468 pRelatedBundle->relationType = relationType;
469
470 hr = PseudoBundleInitialize(qwEngineVersion, &pRelatedBundle->package, fPerMachine, wzRelatedBundleId, pRelatedBundle->relationType,
471 BOOTSTRAPPER_PACKAGE_STATE_PRESENT, fCached, sczCachePath, sczCachePath, NULL, qwFileSize, FALSE,
472 L"-quiet", L"-repair -quiet", L"-uninstall -quiet",
473 (dependencyProvider.sczKey && *dependencyProvider.sczKey) ? &dependencyProvider : NULL,
474 NULL, 0);
475 ExitOnFailure(hr, "Failed to initialize related bundle to represent bundle: %ls", wzRelatedBundleId);
476
477LExit:
478 DependencyUninitializeProvider(&dependencyProvider);
479 ReleaseStr(sczCachePath);
480 ReleaseStr(sczBundleVersion);
481
482 return hr;
483}