main
cpp 406 lines 13.8 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 typedef struct _BUNDLE_QUERY_CONTEXT
6 {
7 BURN_REGISTRATION* pRegistration;
8 BURN_RELATED_BUNDLES* pRelatedBundles;
9 } BUNDLE_QUERY_CONTEXT;
10
11 // internal function declarations
12
13 static __callback int __cdecl CompareRelatedBundlesDetect(
14 __in void* pvContext,
15 __in const void* pvLeft,
16 __in const void* pvRight
17 );
18 static __callback int __cdecl CompareRelatedBundlesPlan(
19 __in void* /*pvContext*/,
20 __in const void* pvLeft,
21 __in const void* pvRight
22 );
23 static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback(
24 __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
25 __in_opt LPVOID pvContext
26 );
27 static HRESULT LoadIfRelatedBundle(
28 __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
29 __in BURN_REGISTRATION* pRegistration,
30 __in BURN_RELATED_BUNDLES* pRelatedBundles
31 );
32 static HRESULT LoadRelatedBundleFromKey(
33 __in_z LPCWSTR wzRelatedBundleId,
34 __in HKEY hkBundleId,
35 __in BOOL fPerMachine,
36 __in BOOTSTRAPPER_RELATION_TYPE relationType,
37 __in BURN_RELATED_BUNDLE *pRelatedBundle
38 );
39
40
41 // function definitions
42
43 extern "C" HRESULT RelatedBundlesInitializeForScope(
44 __in BOOL fPerMachine,
45 __in BURN_REGISTRATION* pRegistration,
46 __in BURN_RELATED_BUNDLES* pRelatedBundles
47 )
48 {
49 HRESULT hr = S_OK;
50 BUNDLE_INSTALL_CONTEXT installContext = fPerMachine ? BUNDLE_INSTALL_CONTEXT_MACHINE : BUNDLE_INSTALL_CONTEXT_USER;
51 BUNDLE_QUERY_CONTEXT queryContext = { };
52
53 queryContext.pRegistration = pRegistration;
54 queryContext.pRelatedBundles = pRelatedBundles;
55
56 hr = BundleQueryRelatedBundles(
57 installContext,
58 const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes),
59 pRegistration->cDetectCodes,
60 const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes),
61 pRegistration->cUpgradeCodes,
62 const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes),
63 pRegistration->cAddonCodes,
64 const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes),
65 pRegistration->cPatchCodes,
66 QueryRelatedBundlesCallback,
67 &queryContext);
68 ExitOnFailure(hr, "Failed to initialize related bundles for scope.");
69
70 LExit:
71 return hr;
72 }
73
74 extern "C" void RelatedBundlesUninitialize(
75 __in BURN_RELATED_BUNDLES* pRelatedBundles
76 )
77 {
78 if (pRelatedBundles->rgRelatedBundles)
79 {
80 for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i)
81 {
82 BURN_PACKAGE* pPackage = &pRelatedBundles->rgRelatedBundles[i].package;
83
84 for (DWORD j = 0; j < pPackage->payloads.cItems; ++j)
85 {
86 PayloadUninitialize(pPackage->payloads.rgItems[j].pPayload);
87 }
88
89 PackageUninitialize(pPackage);
90 ReleaseStr(pRelatedBundles->rgRelatedBundles[i].sczTag);
91 }
92
93 MemFree(pRelatedBundles->rgRelatedBundles);
94 }
95
96 ReleaseMem(pRelatedBundles->rgpPlanSortedRelatedBundles);
97
98 memset(pRelatedBundles, 0, sizeof(BURN_RELATED_BUNDLES));
99 }
100
101
102 extern "C" HRESULT RelatedBundleFindById(
103 __in BURN_RELATED_BUNDLES* pRelatedBundles,
104 __in_z LPCWSTR wzId,
105 __out BURN_RELATED_BUNDLE** ppRelatedBundle
106 )
107 {
108 HRESULT hr = S_OK;
109 BURN_RELATED_BUNDLE* pRelatedBundle = NULL;
110 BURN_PACKAGE* pPackage = NULL;
111
112 *ppRelatedBundle = NULL;
113
114 for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i)
115 {
116 pRelatedBundle = pRelatedBundles->rgRelatedBundles + i;
117 pPackage = &pRelatedBundle->package;
118
119 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1))
120 {
121 *ppRelatedBundle = pRelatedBundle;
122 ExitFunction1(hr = S_OK);
123 }
124 }
125
126 hr = E_NOTFOUND;
127
128 LExit:
129 return hr;
130 }
131
132 extern "C" void RelatedBundlesSortDetect(
133 __in BURN_RELATED_BUNDLES* pRelatedBundles
134 )
135 {
136 qsort_s(pRelatedBundles->rgRelatedBundles, pRelatedBundles->cRelatedBundles, sizeof(BURN_RELATED_BUNDLE), CompareRelatedBundlesDetect, NULL);
137 }
138
139 extern "C" void RelatedBundlesSortPlan(
140 __in BURN_RELATED_BUNDLES* pRelatedBundles
141 )
142 {
143 qsort_s(pRelatedBundles->rgpPlanSortedRelatedBundles, pRelatedBundles->cRelatedBundles, sizeof(BURN_RELATED_BUNDLE*), CompareRelatedBundlesPlan, NULL);
144 }
145
146 extern "C" BOOTSTRAPPER_RELATION_TYPE RelatedBundleConvertRelationType(
147 __in BUNDLE_RELATION_TYPE relationType
148 )
149 {
150 switch (relationType)
151 {
152 case BUNDLE_RELATION_DETECT:
153 return BOOTSTRAPPER_RELATION_DETECT;
154 case BUNDLE_RELATION_UPGRADE:
155 return BOOTSTRAPPER_RELATION_UPGRADE;
156 case BUNDLE_RELATION_ADDON:
157 return BOOTSTRAPPER_RELATION_ADDON;
158 case BUNDLE_RELATION_PATCH:
159 return BOOTSTRAPPER_RELATION_PATCH;
160 case BUNDLE_RELATION_DEPENDENT_ADDON:
161 return BOOTSTRAPPER_RELATION_DEPENDENT_ADDON;
162 case BUNDLE_RELATION_DEPENDENT_PATCH:
163 return BOOTSTRAPPER_RELATION_DEPENDENT_PATCH;
164 default:
165 AssertSz(BUNDLE_RELATION_NONE == relationType, "Unknown BUNDLE_RELATION_TYPE");
166 return BOOTSTRAPPER_RELATION_NONE;
167 }
168 }
169
170
171 // internal helper functions
172
173 static __callback int __cdecl CompareRelatedBundlesDetect(
174 __in void* /*pvContext*/,
175 __in const void* pvLeft,
176 __in const void* pvRight
177 )
178 {
179 int ret = 0;
180 const BURN_RELATED_BUNDLE* pBundleLeft = static_cast<const BURN_RELATED_BUNDLE*>(pvLeft);
181 const BURN_RELATED_BUNDLE* pBundleRight = static_cast<const BURN_RELATED_BUNDLE*>(pvRight);
182
183 // Sort by relation type, then version, then bundle id.
184 if (pBundleLeft->detectRelationType != pBundleRight->detectRelationType)
185 {
186 // Upgrade bundles last, everything else according to the enum.
187 if (BOOTSTRAPPER_RELATION_UPGRADE == pBundleLeft->detectRelationType)
188 {
189 ret = 1;
190 }
191 else if (BOOTSTRAPPER_RELATION_UPGRADE == pBundleRight->detectRelationType)
192 {
193 ret = -1;
194 }
195 else if (pBundleLeft->detectRelationType < pBundleRight->detectRelationType)
196 {
197 ret = -1;
198 }
199 else
200 {
201 ret = 1;
202 }
203 }
204 else
205 {
206 VerCompareParsedVersions(pBundleLeft->pVersion, pBundleRight->pVersion, &ret);
207 if (0 == ret)
208 {
209 ret = ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pBundleLeft->package.sczId, -1, pBundleRight->package.sczId, -1) - 2;
210 }
211 }
212
213 return ret;
214 }
215
216 static __callback int __cdecl CompareRelatedBundlesPlan(
217 __in void* /*pvContext*/,
218 __in const void* pvLeft,
219 __in const void* pvRight
220 )
221 {
222 int ret = 0;
223 const BURN_RELATED_BUNDLE* pBundleLeft = *reinterpret_cast<BURN_RELATED_BUNDLE**>(const_cast<void*>(pvLeft));
224 const BURN_RELATED_BUNDLE* pBundleRight = *reinterpret_cast<BURN_RELATED_BUNDLE**>(const_cast<void*>(pvRight));
225
226 // Sort by relation type, then version, then bundle id.
227 if (pBundleLeft->planRelationType != pBundleRight->planRelationType)
228 {
229 // Upgrade bundles last, everything else according to the enum.
230 if (BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE_UPGRADE == pBundleLeft->planRelationType)
231 {
232 ret = 1;
233 }
234 else if (BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE_UPGRADE == pBundleRight->planRelationType)
235 {
236 ret = -1;
237 }
238 else if (pBundleLeft->planRelationType < pBundleRight->planRelationType)
239 {
240 ret = -1;
241 }
242 else
243 {
244 ret = 1;
245 }
246 }
247 else
248 {
249 VerCompareParsedVersions(pBundleLeft->pVersion, pBundleRight->pVersion, &ret);
250 if (0 == ret)
251 {
252 ret = ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pBundleLeft->package.sczId, -1, pBundleRight->package.sczId, -1) - 2;
253 }
254 }
255
256 return ret;
257 }
258
259 static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback(
260 __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
261 __in_opt LPVOID pvContext
262 )
263 {
264 HRESULT hr = S_OK;
265 BUNDLE_QUERY_CALLBACK_RESULT result = BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE;
266 BUNDLE_QUERY_CONTEXT* pContext = reinterpret_cast<BUNDLE_QUERY_CONTEXT*>(pvContext);
267
268 hr = LoadIfRelatedBundle(pBundle, pContext->pRegistration, pContext->pRelatedBundles);
269 ExitOnFailure(hr, "Failed to load related bundle: %ls", pBundle->wzBundleId);
270
271 LExit:
272 return result;
273 }
274
275 static HRESULT LoadIfRelatedBundle(
276 __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
277 __in BURN_REGISTRATION* pRegistration,
278 __in BURN_RELATED_BUNDLES* pRelatedBundles
279 )
280 {
281 HRESULT hr = S_OK;
282 BOOL fPerMachine = BUNDLE_INSTALL_CONTEXT_MACHINE == pBundle->installContext;
283 BOOTSTRAPPER_RELATION_TYPE relationType = RelatedBundleConvertRelationType(pBundle->relationType);
284 BURN_RELATED_BUNDLE* pRelatedBundle = NULL;
285
286 // If we found our bundle id, it's not a related bundle.
287 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pBundle->wzBundleId, -1, pRegistration->sczId, -1))
288 {
289 ExitFunction1(hr = S_FALSE);
290 }
291
292 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5);
293 ExitOnFailure(hr, "Failed to ensure there is space for related bundles.");
294
295 pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles;
296
297 hr = LoadRelatedBundleFromKey(pBundle->wzBundleId, pBundle->hkBundle, fPerMachine, relationType, pRelatedBundle);
298 ExitOnFailure(hr, "Failed to initialize package from related bundle id: %ls", pBundle->wzBundleId);
299
300 hr = DependencyDetectRelatedBundle(pRelatedBundle, pRegistration);
301 ExitOnFailure(hr, "Failed to detect dependencies for related bundle.");
302
303 ++pRelatedBundles->cRelatedBundles;
304
305 LExit:
306 return hr;
307 }
308
309 static HRESULT LoadRelatedBundleFromKey(
310 __in_z LPCWSTR wzRelatedBundleId,
311 __in HKEY hkBundleId,
312 __in BOOL fPerMachine,
313 __in BOOTSTRAPPER_RELATION_TYPE relationType,
314 __in BURN_RELATED_BUNDLE* pRelatedBundle
315 )
316 {
317 HRESULT hr = S_OK;
318 DWORD64 qwEngineVersion = 0;
319 DWORD dwEngineProtocolVersion = 0;
320 BOOL fSupportsBurnProtocol = FALSE;
321 LPWSTR sczBundleVersion = NULL;
322 LPWSTR sczCachePath = NULL;
323 BOOL fCached = FALSE;
324 DWORD64 qwFileSize = 0;
325 BOOL fExists = FALSE;
326 BURN_DEPENDENCY_PROVIDER dependencyProvider = { };
327 BURN_DEPENDENCY_PROVIDER* pBundleDependencyProvider = NULL;
328
329 // Only support progress from engines that are compatible.
330 hr = RegReadNumber(hkBundleId, BURN_REGISTRATION_REGISTRY_ENGINE_PROTOCOL_VERSION, &dwEngineProtocolVersion);
331 if (SUCCEEDED(hr))
332 {
333 fSupportsBurnProtocol = BURN_PROTOCOL_VERSION == dwEngineProtocolVersion;
334 }
335 else
336 {
337 // Rely on version checking (aka: version greater than or equal to last protocol breaking change *and* versions that are older or the same as this engine)
338 hr = RegReadVersion(hkBundleId, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, &qwEngineVersion);
339 if (SUCCEEDED(hr))
340 {
341 fSupportsBurnProtocol = (FILEMAKEVERSION(3, 6, 2221, 0) <= qwEngineVersion && qwEngineVersion <= FILEMAKEVERSION(rmj, rmm, rup, rpr));
342 }
343
344 hr = S_OK;
345 }
346
347 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, &sczBundleVersion);
348 ExitOnFailure(hr, "Failed to read version from registry for bundle: %ls", wzRelatedBundleId);
349
350 hr = VerParseVersion(sczBundleVersion, 0, FALSE, &pRelatedBundle->pVersion);
351 ExitOnFailure(hr, "Failed to parse pseudo bundle version: %ls", sczBundleVersion);
352
353 if (pRelatedBundle->pVersion->fInvalid)
354 {
355 LogId(REPORT_WARNING, MSG_RELATED_PACKAGE_INVALID_VERSION, wzRelatedBundleId, sczBundleVersion);
356 }
357
358 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, &sczCachePath);
359 ExitOnFailure(hr, "Failed to read cache path from registry for bundle: %ls", wzRelatedBundleId);
360
361 if (FileExistsEx(sczCachePath, NULL))
362 {
363 fCached = TRUE;
364 }
365 else
366 {
367 LogId(REPORT_STANDARD, MSG_DETECT_RELATED_BUNDLE_NOT_CACHED, wzRelatedBundleId, sczCachePath);
368 }
369
370 pRelatedBundle->fPlannable = fCached;
371
372 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, &dependencyProvider.sczKey);
373 ExitOnPathFailure(hr, fExists, "Failed to read provider key from registry for bundle: %ls", wzRelatedBundleId);
374
375 if (dependencyProvider.sczKey && *dependencyProvider.sczKey)
376 {
377 pBundleDependencyProvider = &dependencyProvider;
378
379 dependencyProvider.fImported = TRUE;
380
381 hr = StrAllocString(&dependencyProvider.sczVersion, pRelatedBundle->pVersion->sczVersion, 0);
382 ExitOnFailure(hr, "Failed to copy version for bundle: %ls", wzRelatedBundleId);
383
384 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, &dependencyProvider.sczDisplayName);
385 ExitOnPathFailure(hr, fExists, "Failed to copy display name for bundle: %ls", wzRelatedBundleId);
386 }
387
388 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, &pRelatedBundle->sczTag);
389 ExitOnPathFailure(hr, fExists, "Failed to read tag from registry for bundle: %ls", wzRelatedBundleId);
390
391 pRelatedBundle->detectRelationType = relationType;
392
393 hr = PseudoBundleInitializeRelated(&pRelatedBundle->package, fSupportsBurnProtocol, fPerMachine, wzRelatedBundleId,
394 #ifdef DEBUG
395 pRelatedBundle->detectRelationType,
396 #endif
397 fCached, sczCachePath, qwFileSize, pBundleDependencyProvider);
398 ExitOnFailure(hr, "Failed to initialize related bundle to represent bundle: %ls", wzRelatedBundleId);
399
400 LExit:
401 DependencyUninitializeProvider(&dependencyProvider);
402 ReleaseStr(sczCachePath);
403 ReleaseStr(sczBundleVersion);
404
405 return hr;
406 }