From cc5fe7c79aad14819df1b4cb134884b80a945141 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 1 Feb 2021 20:36:39 -0600 Subject: Move registry checks for dependency ref-counting into Detect. --- src/engine/core.cpp | 12 +- src/engine/dependency.cpp | 349 +++++++++++++++++++++---------------- src/engine/dependency.h | 29 +-- src/engine/detect.cpp | 28 +++ src/engine/engine.mc | 11 +- src/engine/package.cpp | 2 +- src/engine/package.h | 4 + src/engine/plan.cpp | 104 ++++++----- src/engine/plan.h | 1 - src/engine/registration.h | 9 +- src/engine/relatedbundle.cpp | 2 +- src/test/BurnUnitTest/PlanTest.cpp | 23 ++- 12 files changed, 354 insertions(+), 220 deletions(-) diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 77e2dd82..ae09ea65 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -97,7 +97,7 @@ extern "C" HRESULT CoreInitialize( ExitOnFailure(hr, "Failed to load manifest."); hr = ContainersInitialize(&pEngineState->containers, &pEngineState->section); - ExitOnFailure(hr, "Failed to intialize containers."); + ExitOnFailure(hr, "Failed to initialize containers."); // Parse command line. 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); @@ -105,6 +105,9 @@ extern "C" HRESULT CoreInitialize( LogId(REPORT_STANDARD, MSG_BURN_COMMAND_LINE, sczSanitizedCommandLine ? sczSanitizedCommandLine : L""); + hr = DependencyInitialize(&pEngineState->registration, pEngineState->sczIgnoreDependencies); + ExitOnFailure(hr, "Failed to initialize dependency data."); + // Retain whether bundle was initially run elevated. ProcElevated(::GetCurrentProcess(), &fElevated); @@ -332,6 +335,9 @@ extern "C" HRESULT CoreDetect( } } + hr = DependencyDetect(pEngineState); + ExitOnFailure(hr, "Failed to detect the dependencies."); + // Log the detected states. for (DWORD iPackage = 0; iPackage < pEngineState->packages.cPackages; ++iPackage) { @@ -419,7 +425,7 @@ extern "C" HRESULT CorePlan( hr = PlanSetResumeCommand(&pEngineState->registration, action, &pEngineState->command, &pEngineState->log); ExitOnFailure(hr, "Failed to set resume command"); - hr = DependencyPlanInitialize(pEngineState, &pEngineState->plan); + hr = DependencyPlanInitialize(&pEngineState->registration, &pEngineState->plan); ExitOnFailure(hr, "Failed to initialize the dependencies for the plan."); if (BOOTSTRAPPER_ACTION_LAYOUT == action) @@ -457,7 +463,7 @@ extern "C" HRESULT CorePlan( BOOL fContinuePlanning = TRUE; // assume we'll be able to keep planning after registration. pEngineState->plan.fPerMachine = pEngineState->registration.fPerMachine; // default the scope of the plan to the per-machine state of the bundle. - hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, pEngineState->command.resumeType, pEngineState->command.relationType, pEngineState->sczIgnoreDependencies, &fContinuePlanning); + hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, pEngineState->command.resumeType, pEngineState->command.relationType, &fContinuePlanning); ExitOnFailure(hr, "Failed to plan registration."); if (fContinuePlanning) diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp index af4ab0a1..1c33aaf2 100644 --- a/src/engine/dependency.cpp +++ b/src/engine/dependency.cpp @@ -10,6 +10,12 @@ const LPCWSTR vcszIgnoreDependenciesDelim = L";"; // internal function declarations +static HRESULT DetectPackageDependents( + __in BURN_PACKAGE* pPackage, + __in STRINGDICT_HANDLE sdIgnoredDependents, + __in const BURN_REGISTRATION* pRegistration + ); + static HRESULT SplitIgnoreDependencies( __in_z LPCWSTR wzIgnoreDependencies, __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, @@ -28,11 +34,9 @@ static HRESULT GetIgnoredDependents( __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents ); -static HRESULT GetProviderInformation( +static BOOL GetProviderExists( __in HKEY hkRoot, - __in_z LPCWSTR wzProviderKey, - __deref_opt_out_z_opt LPWSTR* psczProviderKey, - __deref_opt_out_z_opt LPWSTR* psczId + __in_z LPCWSTR wzProviderKey ); static void CalculateDependencyActionStates( @@ -70,20 +74,18 @@ static void UnregisterPackageDependency( __in_z LPCWSTR wzDependentProviderKey ); -static BOOL PackageProviderExists( - __in const BURN_PACKAGE* pPackage - ); - // functions -extern "C" void DependencyUninitialize( +extern "C" void DependencyUninitializeProvider( __in BURN_DEPENDENCY_PROVIDER* pProvider ) { ReleaseStr(pProvider->sczKey); ReleaseStr(pProvider->sczVersion); ReleaseStr(pProvider->sczDisplayName); + ReleaseDependencyArray(pProvider->rgDependents, pProvider->cDependents); + memset(pProvider, 0, sizeof(BURN_DEPENDENCY_PROVIDER)); } @@ -167,45 +169,33 @@ LExit: return hr; } -extern "C" HRESULT DependencyDetectProviderKeyPackageId( - __in const BURN_PACKAGE* pPackage, - __deref_opt_out_z_opt LPWSTR* psczProviderKey, - __deref_opt_out_z_opt LPWSTR* psczId +extern "C" HRESULT DependencyInitialize( + __in BURN_REGISTRATION* pRegistration, + __in_z_opt LPCWSTR wzIgnoreDependencies ) { - HRESULT hr = E_NOTFOUND; - LPWSTR wzProviderKey = NULL; - HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + HRESULT hr = S_OK; - for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + // If no parent was specified at all, use the bundle id as the self dependent. + if (!pRegistration->sczActiveParent) { - const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; - - // Find the first package id registered for the provider key. - hr = GetProviderInformation(hkRoot, pProvider->sczKey, psczProviderKey, psczId); - if (E_NOTFOUND == hr) - { - continue; - } - ExitOnFailure(hr, "Failed to get the package provider information."); - - ExitFunction(); + pRegistration->wzSelfDependent = pRegistration->sczId; } - - // Older bundles may not have written the id so try the default. - if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + else if (*pRegistration->sczActiveParent) // if parent was specified use that as the self dependent. { - wzProviderKey = pPackage->Msi.sczProductCode; + pRegistration->wzSelfDependent = pRegistration->sczActiveParent; } + // else parent:none was used which means we should not register a dependency on ourself. + + // The current bundle provider key should always be ignored for dependency checks. + hr = DepDependencyArrayAlloc(&pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, pRegistration->sczProviderKey, NULL); + ExitOnFailure(hr, "Failed to add the bundle provider key to the list of dependencies to ignore."); - if (wzProviderKey) + // Add the list of dependencies to ignore. + if (wzIgnoreDependencies) { - hr = GetProviderInformation(hkRoot, wzProviderKey, psczProviderKey, psczId); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get the package default provider information."); + hr = SplitIgnoreDependencies(wzIgnoreDependencies, &pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies); + ExitOnFailure(hr, "Failed to split the list of dependencies to ignore."); } LExit: @@ -236,23 +226,77 @@ LExit: return hr; } +extern "C" HRESULT DependencyDetect( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + BURN_REGISTRATION* pRegistration = &pEngineState->registration; + STRINGDICT_HANDLE sdIgnoredDependents = NULL; + BURN_PACKAGE* pPackage = NULL; + + // Always leave this empty so that all dependents get detected. Plan will ignore dependents based on its own logic. + hr = DictCreateStringList(&sdIgnoredDependents, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create the string dictionary."); + + hr = DepCheckDependents(pRegistration->hkRoot, pRegistration->sczProviderKey, 0, sdIgnoredDependents, &pRegistration->rgDependents, &pRegistration->cDependents); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed dependents check on bundle"); + } + else + { + hr = S_OK; + } + + for (DWORD iPackage = 0; iPackage < pEngineState->packages.cPackages; ++iPackage) + { + pPackage = pEngineState->packages.rgPackages + iPackage; + hr = DetectPackageDependents(pPackage, sdIgnoredDependents, pRegistration); + ExitOnFailure(hr, "Failed to detect dependents for package '%ls'", pPackage->sczId); + } + + for (DWORD iRelatedBundle = 0; iRelatedBundle < pEngineState->registration.relatedBundles.cRelatedBundles; ++iRelatedBundle) + { + pPackage = &pEngineState->registration.relatedBundles.rgRelatedBundles[iRelatedBundle].package; + hr = DetectPackageDependents(pPackage, sdIgnoredDependents, pRegistration); + ExitOnFailure(hr, "Failed to detect dependents for related bundle '%ls'", pPackage->sczId); + } + + if (pRegistration->wzSelfDependent) + { + for (DWORD i = 0; i < pRegistration->cDependents; ++i) + { + DEPENDENCY* pDependent = pRegistration->rgDependents + i; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->wzSelfDependent, -1, pDependent->sczKey, -1)) + { + pRegistration->fSelfRegisteredAsDependent = TRUE; + break; + } + } + } + +LExit: + ReleaseDict(sdIgnoredDependents); + + return hr; +} + extern "C" HRESULT DependencyPlanInitialize( - __in const BURN_ENGINE_STATE* pEngineState, + __in const BURN_REGISTRATION* pRegistration, __in BURN_PLAN* pPlan ) { HRESULT hr = S_OK; - // The current bundle provider key should always be ignored for dependency checks. - hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pEngineState->registration.sczProviderKey, NULL); - ExitOnFailure(hr, "Failed to add the bundle provider key to the list of dependencies to ignore."); - - // Add the list of dependencies to ignore to the plan. - if (pEngineState->sczIgnoreDependencies) + // TODO: After adding enumeration to STRINGDICT, a single STRINGDICT_HANDLE can be used everywhere. + for (DWORD i = 0; i < pRegistration->cIgnoredDependencies; ++i) { - // TODO: After adding enumeration to STRINGDICT, a single STRINGDICT_HANDLE can be used everywhere. - hr = SplitIgnoreDependencies(pEngineState->sczIgnoreDependencies, &pPlan->rgPlannedProviders, &pPlan->cPlannedProviders); - ExitOnFailure(hr, "Failed to split the list of dependencies to ignore."); + DEPENDENCY* pDependency = pRegistration->rgIgnoredDependencies + i; + + hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pDependency->sczKey, pDependency->sczName); + ExitOnFailure(hr, "Failed to add the detected provider to the list of dependencies to ignore."); } LExit: @@ -323,9 +367,6 @@ extern "C" HRESULT DependencyPlanPackageBegin( { HRESULT hr = S_OK; STRINGDICT_HANDLE sdIgnoredDependents = NULL; - DEPENDENCY* rgDependents = NULL; - UINT cDependents = 0; - HKEY hkHive = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; BURN_DEPENDENCY_ACTION dependencyExecuteAction = BURN_DEPENDENCY_ACTION_NONE; BURN_DEPENDENCY_ACTION dependencyRollbackAction = BURN_DEPENDENCY_ACTION_NONE; @@ -361,18 +402,31 @@ extern "C" HRESULT DependencyPlanPackageBegin( } else { + hr = S_OK; + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) { - const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + const BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i; - hr = DepCheckDependents(hkHive, pProvider->sczKey, 0, sdIgnoredDependents, &rgDependents, &cDependents); - if (E_FILENOTFOUND != hr) - { - ExitOnFailure(hr, "Failed dependents check on package provider: %ls", pProvider->sczKey); - } - else + for (DWORD j = 0; j < pProvider->cDependents; ++j) { - hr = S_OK; + const DEPENDENCY* pDependency = pProvider->rgDependents + j; + + hr = DictKeyExists(sdIgnoredDependents, pDependency->sczKey); + if (E_NOTFOUND == hr) + { + hr = S_OK; + + if (!pPackage->fDependencyManagerWasHere) + { + pPackage->fDependencyManagerWasHere = TRUE; + + LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId); + } + + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_DEPENDENT, pDependency->sczKey, LoggingStringOrUnknownIfNull(pDependency->sczName)); + } + ExitOnFailure(hr, "Failed to check the dictionary of ignored dependents."); } } } @@ -382,52 +436,46 @@ extern "C" HRESULT DependencyPlanPackageBegin( CalculateDependencyActionStates(pPackage, pPlan->action, &dependencyExecuteAction, &dependencyRollbackAction); // If dependents were found, change the action to not uninstall the package. - if (0 < cDependents) + if (pPackage->fDependencyManagerWasHere) { - LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId, cDependents); - - for (DWORD i = 0; i < cDependents; ++i) - { - const DEPENDENCY* pDependency = &rgDependents[i]; - LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_DEPENDENT, pDependency->sczKey, LoggingStringOrUnknownIfNull(pDependency->sczName)); - } - - pPackage->fDependencyManagerWasHere = TRUE; pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; } - // Use the calculated dependency actions as the provider actions if there - // are any non-imported providers that need to be registered and the package - // is current (not obsolete). - else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE != pPackage->currentState) + else { - BOOL fAllImportedProviders = TRUE; // assume all providers were imported. - for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + // Use the calculated dependency actions as the provider actions if there + // are any non-imported providers that need to be registered and the package + // is current (not obsolete). + if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE != pPackage->currentState) { - const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; - if (!pProvider->fImported) + BOOL fAllImportedProviders = TRUE; // assume all providers were imported. + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) { - fAllImportedProviders = FALSE; - break; + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + if (!pProvider->fImported) + { + fAllImportedProviders = FALSE; + break; + } } - } - if (!fAllImportedProviders) - { - pPackage->providerExecute = dependencyExecuteAction; - pPackage->providerRollback = dependencyRollbackAction; + if (!fAllImportedProviders) + { + pPackage->providerExecute = dependencyExecuteAction; + pPackage->providerRollback = dependencyRollbackAction; + } } - } - // If the package will be removed, add its providers to the growing list in the plan. - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) - { - for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + // If the package will be removed, add its providers to the growing list in the plan. + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) { - const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; - hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, NULL); - ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey); + hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, NULL); + ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey); + } } } @@ -435,7 +483,6 @@ extern "C" HRESULT DependencyPlanPackageBegin( pPackage->dependencyRollback = dependencyRollbackAction; LExit: - ReleaseDependencyArray(rgDependents, cDependents); ReleaseDict(sdIgnoredDependents); return hr; @@ -652,6 +699,58 @@ extern "C" void DependencyUnregisterBundle( // internal functions + +static HRESULT DetectPackageDependents( + __in BURN_PACKAGE* pPackage, + __in STRINGDICT_HANDLE sdIgnoredDependents, + __in const BURN_REGISTRATION* pRegistration + ) +{ + HRESULT hr = S_OK; + HKEY hkHive = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + // There's currently no point in getting the dependents if the scope doesn't match, + // because they will just get ignored. + if (pRegistration->fPerMachine != pPackage->fPerMachine) + { + ExitFunction(); + } + + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + + hr = DepCheckDependents(hkHive, pProvider->sczKey, 0, sdIgnoredDependents, &pProvider->rgDependents, &pProvider->cDependents); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed dependents check on package provider: %ls", pProvider->sczKey); + + if (!pPackage->fPackageProviderExists && (0 < pProvider->cDependents || GetProviderExists(hkHive, pProvider->sczKey))) + { + pPackage->fPackageProviderExists = TRUE; + } + } + else + { + hr = S_OK; + + if (!pPackage->fPackageProviderExists && GetProviderExists(hkHive, pProvider->sczKey)) + { + pPackage->fPackageProviderExists = TRUE; + } + } + } + + // Older bundles may not have written the id so try the default. + if (!pPackage->fPackageProviderExists && BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.sczProductCode && GetProviderExists(hkHive, pPackage->Msi.sczProductCode)) + { + pPackage->fPackageProviderExists = TRUE; + } + +LExit: + return hr; +} + /******************************************************************** SplitIgnoreDependencies - Splits a semicolon-delimited string into a list of unique dependencies to ignore. @@ -803,52 +902,16 @@ LExit: } /******************************************************************** - GetProviderId - Gets the ID of the package given the provider key. + GetProviderExists - Gets whether the provider key is registered. *********************************************************************/ -static HRESULT GetProviderInformation( +static BOOL GetProviderExists( __in HKEY hkRoot, - __in_z LPCWSTR wzProviderKey, - __deref_opt_out_z_opt LPWSTR* psczProviderKey, - __deref_opt_out_z_opt LPWSTR* psczId + __in_z LPCWSTR wzProviderKey ) { - HRESULT hr = S_OK; - LPWSTR sczId = NULL; - - hr = DepGetProviderInformation(hkRoot, wzProviderKey, &sczId, NULL, NULL); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get the provider key package id."); - - // If the id was registered return it and exit. - if (sczId && *sczId) - { - if (psczProviderKey) - { - hr = StrAllocString(psczProviderKey, wzProviderKey, 0); - ExitOnFailure(hr, "Failed to copy the provider key."); - } - - if (psczId) - { - *psczId = sczId; - sczId = NULL; - } - - ExitFunction(); - } - else - { - hr = E_NOTFOUND; - } - -LExit: - ReleaseStr(sczId); - - return hr; + HRESULT hr = DepGetProviderInformation(hkRoot, wzProviderKey, NULL, NULL, NULL); + return SUCCEEDED(hr); } /******************************************************************** @@ -886,7 +949,7 @@ static void CalculateDependencyActionStates( switch (pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: - if (!PackageProviderExists(pPackage)) + if (!pPackage->fPackageProviderExists) { break; } @@ -902,7 +965,7 @@ static void CalculateDependencyActionStates( switch (pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: - if (!PackageProviderExists(pPackage)) + if (!pPackage->fPackageProviderExists) { break; } @@ -1181,15 +1244,3 @@ static void UnregisterPackageDependency( } } } - -/******************************************************************** - PackageProviderExists - Checks if a package provider is registered. - -*********************************************************************/ -static BOOL PackageProviderExists( - __in const BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = DependencyDetectProviderKeyPackageId(pPackage, NULL, NULL); - return SUCCEEDED(hr); -} diff --git a/src/engine/dependency.h b/src/engine/dependency.h index 905857e0..5390bede 100644 --- a/src/engine/dependency.h +++ b/src/engine/dependency.h @@ -14,11 +14,11 @@ const LPCWSTR DEPENDENCY_IGNOREDEPENDENCIES = L"IGNOREDEPENDENCIES"; // function declarations /******************************************************************** - DependencyUninitialize - Frees and zeros memory allocated in the - dependency. + DependencyUninitializeProvider - Frees and zeros memory allocated in + the dependency provider. *********************************************************************/ -void DependencyUninitialize( +void DependencyUninitializeProvider( __in BURN_DEPENDENCY_PROVIDER* pProvider ); @@ -32,16 +32,9 @@ HRESULT DependencyParseProvidersFromXml( __in IXMLDOMNode* pixnPackage ); -/******************************************************************** - DependencyDetectProviderKeyPackageId - Detect if the provider key is - registered and if so what package code is registered. - - Note: Returns E_NOTFOUND if the provider key is not registered. -*********************************************************************/ -HRESULT DependencyDetectProviderKeyPackageId( - __in const BURN_PACKAGE* pPackage, - __deref_opt_out_z_opt LPWSTR* psczProviderKey, - __deref_opt_out_z_opt LPWSTR* psczId +HRESULT DependencyInitialize( + __in BURN_REGISTRATION* pRegistration, + __in_z_opt LPCWSTR wzIgnoreDependencies ); /******************************************************************** @@ -54,12 +47,20 @@ HRESULT DependencyDetectProviderKeyBundleId( __in BURN_REGISTRATION* pRegistration ); +/******************************************************************** + DependencyDetect - Detects dependency information. + +*********************************************************************/ +HRESULT DependencyDetect( + __in BURN_ENGINE_STATE* pEngineState + ); + /******************************************************************** DependencyPlanInitialize - Initializes the plan. *********************************************************************/ HRESULT DependencyPlanInitialize( - __in const BURN_ENGINE_STATE* pEngineState, + __in const BURN_REGISTRATION* pRegistration, __in BURN_PLAN* pPlan ); diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp index 63e66539..3b8d63e2 100644 --- a/src/engine/detect.cpp +++ b/src/engine/detect.cpp @@ -41,12 +41,28 @@ extern "C" void DetectReset( ReleaseNullStr(pRegistration->sczDetectedProviderKeyBundleId); pRegistration->fEnabledForwardCompatibleBundle = FALSE; PackageUninitialize(&pRegistration->forwardCompatibleBundle); + pRegistration->fSelfRegisteredAsDependent = FALSE; + + if (pRegistration->rgIgnoredDependencies) + { + ReleaseDependencyArray(pRegistration->rgIgnoredDependencies, pRegistration->cIgnoredDependencies); + } + pRegistration->rgIgnoredDependencies = NULL; + pRegistration->cIgnoredDependencies = 0; + + if (pRegistration->rgDependents) + { + ReleaseDependencyArray(pRegistration->rgDependents, pRegistration->cDependents); + } + pRegistration->rgDependents = NULL; + pRegistration->cDependents = 0; for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) { BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; + pPackage->fPackageProviderExists = FALSE; pPackage->cache = BURN_CACHE_STATE_NONE; for (DWORD iPayload = 0; iPayload < pPackage->cPayloads; ++iPayload) @@ -69,6 +85,18 @@ extern "C" void DetectReset( ReleaseNullMem(pPackage->Msp.rgTargetProducts); pPackage->Msp.cTargetProductCodes = 0; } + + for (DWORD iProvider = 0; iProvider < pPackage->cDependencyProviders; ++iProvider) + { + BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + iProvider; + + if (pProvider->rgDependents) + { + ReleaseDependencyArray(pProvider->rgDependents, pProvider->cDependents); + } + pProvider->rgDependents = NULL; + pProvider->cDependents = 0; + } } for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) diff --git a/src/engine/engine.mc b/src/engine/engine.mc index ad86308c..d6e9751a 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -356,7 +356,7 @@ MessageId=210 Severity=Warning SymbolicName=MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS Language=English -Plan skipped due to %1!u! remaining dependents +Plan skipped due to remaining dependents: . MessageId=211 @@ -595,7 +595,7 @@ MessageId=327 Severity=Warning SymbolicName=MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS Language=English -Will not uninstall package: %1!ls!, found dependents: %2!d! +Will not uninstall package: %1!ls!, found dependents: . MessageId=328 @@ -640,6 +640,13 @@ Language=English Could not remove bundle dependency provider: %1!ls!, error: 0x%2!x! . +MessageId=334 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_BUNDLE_DEPENDENT +Language=English +Found dependent: %1!ls!, name: %2!ls! +. + MessageId=335 Severity=Success SymbolicName=MSG_ACQUIRE_BUNDLE_PAYLOAD diff --git a/src/engine/package.cpp b/src/engine/package.cpp index 527766eb..701dda08 100644 --- a/src/engine/package.cpp +++ b/src/engine/package.cpp @@ -323,7 +323,7 @@ extern "C" void PackageUninitialize( { for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) { - DependencyUninitialize(pPackage->rgDependencyProviders + i); + DependencyUninitializeProvider(pPackage->rgDependencyProviders + i); } MemFree(pPackage->rgDependencyProviders); } diff --git a/src/engine/package.h b/src/engine/package.h index 1a4f7060..8f801e85 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -159,6 +159,9 @@ typedef struct _BURN_DEPENDENCY_PROVIDER LPWSTR sczVersion; LPWSTR sczDisplayName; BOOL fImported; + + DEPENDENCY* rgDependents; // only valid after Detect. + UINT cDependents; // only valid after Detect. } BURN_DEPENDENCY_PROVIDER; typedef struct _BURN_ROLLBACK_BOUNDARY @@ -199,6 +202,7 @@ typedef struct _BURN_PACKAGE BOOTSTRAPPER_PACKAGE_STATE currentState; // only valid after Detect. BURN_CACHE_STATE cache; // only valid after Detect. + BOOL fPackageProviderExists; // only valid after Detect. BOOTSTRAPPER_REQUEST_STATE defaultRequested;// only valid during Plan. BOOTSTRAPPER_REQUEST_STATE requested; // only valid during Plan. BOOL fAcquire; // only valid during Plan. diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index a45eab62..6f5407b9 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -574,15 +574,12 @@ extern "C" HRESULT PlanRegistration( __in BURN_REGISTRATION* pRegistration, __in BOOTSTRAPPER_RESUME_TYPE resumeType, __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z_opt LPCWSTR wzIgnoreDependencies, __out BOOL* pfContinuePlanning ) { HRESULT hr = S_OK; - LPCWSTR wzSelfDependent = NULL; + STRINGDICT_HANDLE sdBundleDependents = NULL; STRINGDICT_HANDLE sdIgnoreDependents = NULL; - DEPENDENCY* rgDependencies = NULL; - UINT cDependencies = 0; pPlan->fRegister = TRUE; // register the bundle since we're modifying machine state. @@ -591,17 +588,6 @@ extern "C" HRESULT PlanRegistration( pPlan->fDisallowRemoval = FALSE; // by default the bundle can be planned to be removed - // If no parent was specified at all, use the bundle id as the self dependent. - if (!pRegistration->sczActiveParent) - { - wzSelfDependent = pRegistration->sczId; - } - else if (*pRegistration->sczActiveParent) // if parent was specified use that as the self dependent. - { - wzSelfDependent = pRegistration->sczActiveParent; - } - // else parent:none was used which means we should not register a dependency on ourself. - if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) { // If our provider key was detected and it points to our current bundle then we can @@ -622,12 +608,12 @@ extern "C" HRESULT PlanRegistration( // If the self-dependent dependent exists, plan its removal. If we did not do this, we // would prevent self-removal. - if (wzSelfDependent && DependencyDependentExists(pRegistration, wzSelfDependent)) + if (pRegistration->fSelfRegisteredAsDependent) { - hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER, wzSelfDependent, pRegistration->sczId); + hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER, pRegistration->wzSelfDependent, pRegistration->sczId); ExitOnFailure(hr, "Failed to allocate registration action."); - hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, wzSelfDependent); + hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pRegistration->wzSelfDependent); ExitOnFailure(hr, "Failed to add self-dependent to ignore dependents."); } @@ -637,10 +623,20 @@ extern "C" HRESULT PlanRegistration( if (BOOTSTRAPPER_RELATION_UPGRADE != relationType) { // If there were other dependencies to ignore, add them. - if (wzIgnoreDependencies && *wzIgnoreDependencies) + for (DWORD iDependency = 0; iDependency < pRegistration->cIgnoredDependencies; ++iDependency) { - hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, wzIgnoreDependencies); - ExitOnFailure(hr, "Failed to add dependents ignored from command-line."); + DEPENDENCY* pDependency = pRegistration->rgIgnoredDependencies + iDependency; + + hr = DictKeyExists(sdIgnoreDependents, pDependency->sczKey); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the dictionary of ignored dependents."); + } + else + { + hr = DictAddKey(sdIgnoreDependents, pDependency->sczKey); + ExitOnFailure(hr, "Failed to add dependent key to ignored dependents."); + } } // For addon or patch bundles, dependent related bundles should be ignored. This allows @@ -661,22 +657,29 @@ extern "C" HRESULT PlanRegistration( } } - // If there are any (non-ignored and not-planned-to-be-removed) dependents left, uninstall. - hr = DepCheckDependents(pRegistration->hkRoot, pRegistration->sczProviderKey, 0, sdIgnoreDependents, &rgDependencies, &cDependencies); - if (E_FILENOTFOUND == hr) + // If there are any (non-ignored and not-planned-to-be-removed) dependents left, skip planning. + for (DWORD iDependent = 0; iDependent < pRegistration->cDependents; ++iDependent) { - hr = S_OK; - } - else if (SUCCEEDED(hr) && cDependencies) - { - // TODO: callback to the BA and let it have the option to ignore any of these dependents? + DEPENDENCY* pDependent = pRegistration->rgDependents + iDependent; + + hr = DictKeyExists(sdIgnoreDependents, pDependent->sczKey); + if (E_NOTFOUND == hr) + { + hr = S_OK; - pPlan->fDisallowRemoval = TRUE; // ensure the registration stays - *pfContinuePlanning = FALSE; // skip the rest of planning. + // TODO: callback to the BA and let it have the option to ignore this dependent? + if (!pPlan->fDisallowRemoval) + { + pPlan->fDisallowRemoval = TRUE; // ensure the registration stays + *pfContinuePlanning = FALSE; // skip the rest of planning. + + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS); + } - LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS, cDependencies); + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_DEPENDENT, pDependent->sczKey, LoggingStringOrUnknownIfNull(pDependent->sczName)); + } + ExitOnFailure(hr, "Failed to check for remaining dependents during planning."); } - ExitOnFailure(hr, "Failed to check for remaining dependents during planning."); } } else @@ -707,6 +710,23 @@ extern "C" HRESULT PlanRegistration( // if broken. pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER; + // Create the dictionary of bundle dependents. + hr = DictCreateStringList(&sdBundleDependents, 5, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create the string dictionary."); + + for (DWORD iDependent = 0; iDependent < pRegistration->cDependents; ++iDependent) + { + DEPENDENCY* pDependent = pRegistration->rgDependents + iDependent; + + hr = DictKeyExists(sdBundleDependents, pDependent->sczKey); + if (E_NOTFOUND == hr) + { + hr = DictAddKey(sdBundleDependents, pDependent->sczKey); + ExitOnFailure(hr, "Failed to add dependent key to bundle dependents."); + } + ExitOnFailure(hr, "Failed to check the dictionary of bundle dependents."); + } + // Register each dependent related bundle. The ensures that addons and patches are reference // counted and stick around until the last targeted bundle is removed. for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) @@ -719,11 +739,16 @@ extern "C" HRESULT PlanRegistration( { const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j; - if (!DependencyDependentExists(pRegistration, pProvider->sczKey)) + hr = DictKeyExists(sdBundleDependents, pProvider->sczKey); + if (E_NOTFOUND == hr) { + hr = DictAddKey(sdBundleDependents, pProvider->sczKey); + ExitOnFailure(hr, "Failed to add new dependent key to bundle dependents."); + hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, pProvider->sczKey, pRelatedBundle->package.sczId); ExitOnFailure(hr, "Failed to add registration action for dependent related bundle."); } + ExitOnFailure(hr, "Failed to check the dictionary of bundle dependents."); } } } @@ -731,19 +756,16 @@ extern "C" HRESULT PlanRegistration( // Only do the following if we decided there was a dependent self to register. If so and and an explicit parent was // provided, register dependent self. Otherwise, if this bundle is not an addon or patch bundle then self-regisiter // as our own dependent. - if (wzSelfDependent && (pRegistration->sczActiveParent || !fAddonOrPatchBundle)) + if (pRegistration->wzSelfDependent && !pRegistration->fSelfRegisteredAsDependent && (pRegistration->sczActiveParent || !fAddonOrPatchBundle)) { - if (!DependencyDependentExists(pRegistration, wzSelfDependent)) - { - hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, wzSelfDependent, pRegistration->sczId); - ExitOnFailure(hr, "Failed to add registration action for self dependent."); - } + hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, pRegistration->wzSelfDependent, pRegistration->sczId); + ExitOnFailure(hr, "Failed to add registration action for self dependent."); } } LExit: + ReleaseDict(sdBundleDependents); ReleaseDict(sdIgnoreDependents); - ReleaseDependencyArray(rgDependencies, cDependencies); return hr; } diff --git a/src/engine/plan.h b/src/engine/plan.h index cb64f891..54189973 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -418,7 +418,6 @@ HRESULT PlanRegistration( __in BURN_REGISTRATION* pRegistration, __in BOOTSTRAPPER_RESUME_TYPE resumeType, __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z_opt LPCWSTR wzIgnoreDependencies, __out BOOL* pfContinuePlanning ); HRESULT PlanPassThroughBundle( diff --git a/src/engine/registration.h b/src/engine/registration.h index dc9bc5b7..c1e52ac9 100644 --- a/src/engine/registration.h +++ b/src/engine/registration.h @@ -139,8 +139,13 @@ typedef struct _BURN_REGISTRATION // Update registration BURN_UPDATE_REGISTRATION update; - // Only valid after detect. - BURN_RELATED_BUNDLES relatedBundles; + BURN_RELATED_BUNDLES relatedBundles; // Only valid after detect. + DEPENDENCY* rgIgnoredDependencies; // Only valid after detect. + UINT cIgnoredDependencies; // Only valid after detect. + DEPENDENCY* rgDependents; // Only valid after detect. + UINT cDependents; // Only valid after detect. + LPCWSTR wzSelfDependent; // Only valid after detect. + BOOL fSelfRegisteredAsDependent; // Only valid after detect. LPWSTR sczDetectedProviderKeyBundleId; LPWSTR sczAncestors; diff --git a/src/engine/relatedbundle.cpp b/src/engine/relatedbundle.cpp index 7b0da4a4..bc79b954 100644 --- a/src/engine/relatedbundle.cpp +++ b/src/engine/relatedbundle.cpp @@ -459,7 +459,7 @@ static HRESULT LoadRelatedBundleFromKey( ExitOnFailure(hr, "Failed to initialize related bundle to represent bundle: %ls", wzRelatedBundleId); LExit: - DependencyUninitialize(&dependencyProvider); + DependencyUninitializeProvider(&dependencyProvider); ReleaseStr(sczCachePath); ReleaseStr(sczBundleVersion); diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index 71e455c3..fb4ca246 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -552,9 +552,23 @@ namespace Bootstrapper ReleaseStr(sczFilePath); } + DependencyInitialize(&pEngineState->registration, NULL); + pEngineState->userExperience.pfnBAProc = PlanTestBAProc; } + void PlanTestDetect(BURN_ENGINE_STATE* pEngineState) + { + HRESULT hr = S_OK; + BURN_REGISTRATION* pRegistration = &pEngineState->registration; + + DetectReset(pRegistration, &pEngineState->packages); + PlanReset(&pEngineState->plan, &pEngineState->packages); + + hr = DepDependencyArrayAlloc(&pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, pRegistration->sczProviderKey, NULL); + NativeAssert::Succeeded(hr, "Failed to add the bundle provider key to the list of dependencies to ignore."); + } + void DetectAttachedContainerAsAttached(BURN_ENGINE_STATE* pEngineState) { for (DWORD i = 0; i < pEngineState->containers.cContainers; ++i) @@ -585,8 +599,7 @@ namespace Bootstrapper void DetectPackagesAsAbsent(BURN_ENGINE_STATE* pEngineState) { - DetectReset(&pEngineState->registration, &pEngineState->packages); - PlanReset(&pEngineState->plan, &pEngineState->packages); + PlanTestDetect(pEngineState); for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) { @@ -597,8 +610,7 @@ namespace Bootstrapper void DetectPackagesAsPresentAndCached(BURN_ENGINE_STATE* pEngineState) { - DetectReset(&pEngineState->registration, &pEngineState->packages); - PlanReset(&pEngineState->plan, &pEngineState->packages); + PlanTestDetect(pEngineState); pEngineState->registration.fInstalled = TRUE; @@ -611,8 +623,7 @@ namespace Bootstrapper void DetectPermanentPackagesAsPresentAndCached(BURN_ENGINE_STATE* pEngineState) { - DetectReset(&pEngineState->registration, &pEngineState->packages); - PlanReset(&pEngineState->plan, &pEngineState->packages); + PlanTestDetect(pEngineState); pEngineState->registration.fInstalled = TRUE; -- cgit v1.2.3-55-g6feb