From e9a4f673511dd06a8209f3e4037ad20f153d6caa Mon Sep 17 00:00:00 2001
From: Sean Hall <r.sean.hall@gmail.com>
Date: Tue, 30 Mar 2021 19:38:33 -0500
Subject: Skip bundle dependent checking when ignoring dependencies includes
 ALL.

Fixes #6391.
---
 src/engine/dependency.cpp | 14 +++++--
 src/engine/plan.cpp       | 95 ++++++++++++++++++++++++-----------------------
 src/engine/plan.h         |  1 +
 src/engine/registration.h |  1 +
 4 files changed, 62 insertions(+), 49 deletions(-)

(limited to 'src')

diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp
index 51aca239..1bd0c7d4 100644
--- a/src/engine/dependency.cpp
+++ b/src/engine/dependency.cpp
@@ -19,7 +19,8 @@ static HRESULT DetectPackageDependents(
 static HRESULT SplitIgnoreDependencies(
     __in_z LPCWSTR wzIgnoreDependencies,
     __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies,
-    __inout LPUINT pcDependencies
+    __inout LPUINT pcDependencies,
+    __out BOOL* pfIgnoreAll
     );
 
 static HRESULT JoinIgnoreDependencies(
@@ -194,7 +195,7 @@ extern "C" HRESULT DependencyInitialize(
     // Add the list of dependencies to ignore.
     if (wzIgnoreDependencies)
     {
-        hr = SplitIgnoreDependencies(wzIgnoreDependencies, &pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies);
+        hr = SplitIgnoreDependencies(wzIgnoreDependencies, &pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, &pRegistration->fIgnoreAllDependents);
         ExitOnFailure(hr, "Failed to split the list of dependencies to ignore.");
     }
 
@@ -816,12 +817,14 @@ LExit:
 static HRESULT SplitIgnoreDependencies(
     __in_z LPCWSTR wzIgnoreDependencies,
     __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies,
-    __inout LPUINT pcDependencies
+    __inout LPUINT pcDependencies,
+    __out BOOL* pfIgnoreAll
     )
 {
     HRESULT hr = S_OK;
     LPWSTR wzContext = NULL;
     STRINGDICT_HANDLE sdIgnoreDependencies = NULL;
+    *pfIgnoreAll = FALSE;
 
     // Create a dictionary to hold unique dependencies.
     hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE);
@@ -842,6 +845,11 @@ static HRESULT SplitIgnoreDependencies(
 
             hr = DictAddKey(sdIgnoreDependencies, wzToken);
             ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken);
+
+            if (!*pfIgnoreAll && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, L"ALL", -1, wzToken, -1))
+            {
+                *pfIgnoreAll = TRUE;
+            }
         }
     }
 
diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp
index 65da4ab3..c75c1753 100644
--- a/src/engine/plan.cpp
+++ b/src/engine/plan.cpp
@@ -588,8 +588,8 @@ extern "C" HRESULT PlanRegistration(
     STRINGDICT_HANDLE sdIgnoreDependents = NULL;
 
     pPlan->fCanAffectMachineState = TRUE; // register the bundle since we're modifying machine state.
-
     pPlan->fDisallowRemoval = FALSE; // by default the bundle can be planned to be removed
+    pPlan->fIgnoreAllDependents = pRegistration->fIgnoreAllDependents;
 
     // Ensure the bundle is cached if not running from the cache.
     if (!CacheBundleRunningFromCache())
@@ -633,68 +633,71 @@ extern "C" HRESULT PlanRegistration(
             ExitOnFailure(hr, "Failed to add self-dependent to ignore dependents.");
         }
 
-        // If we are not doing an upgrade, we check to see if there are still dependents on us and if so we skip planning.
-        // However, when being upgraded, we always execute our uninstall because a newer version of us is probably
-        // already on the machine and we need to clean up the stuff specific to this bundle.
-        if (BOOTSTRAPPER_RELATION_UPGRADE != relationType)
+        if (!pPlan->fIgnoreAllDependents)
         {
-            // If there were other dependencies to ignore, add them.
-            for (DWORD iDependency = 0; iDependency < pRegistration->cIgnoredDependencies; ++iDependency)
+            // If we are not doing an upgrade, we check to see if there are still dependents on us and if so we skip planning.
+            // However, when being upgraded, we always execute our uninstall because a newer version of us is probably
+            // already on the machine and we need to clean up the stuff specific to this bundle.
+            if (BOOTSTRAPPER_RELATION_UPGRADE != relationType)
             {
-                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
+                // If there were other dependencies to ignore, add them.
+                for (DWORD iDependency = 0; iDependency < pRegistration->cIgnoredDependencies; ++iDependency)
                 {
-                    hr = DictAddKey(sdIgnoreDependents, pDependency->sczKey);
-                    ExitOnFailure(hr, "Failed to add dependent key to ignored dependents.");
-                }
-            }
+                    DEPENDENCY* pDependency = pRegistration->rgIgnoredDependencies + iDependency;
 
-            // For addon or patch bundles, dependent related bundles should be ignored. This allows
-            // that addon or patch to be removed even though bundles it targets still are registered.
-            for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
-            {
-                const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
+                    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.");
+                    }
+                }
 
-                if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType)
+                // For addon or patch bundles, dependent related bundles should be ignored. This allows
+                // that addon or patch to be removed even though bundles it targets still are registered.
+                for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
                 {
-                    for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j)
+                    const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
+
+                    if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType)
                     {
-                        const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j;
+                        for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j)
+                        {
+                            const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j;
 
-                        hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pProvider->sczKey);
-                        ExitOnFailure(hr, "Failed to add dependent bundle provider key to ignore dependents.");
+                            hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pProvider->sczKey);
+                            ExitOnFailure(hr, "Failed to add dependent bundle provider key to ignore dependents.");
+                        }
                     }
                 }
-            }
-
-            // If there are any (non-ignored and not-planned-to-be-removed) dependents left, skip planning.
-            for (DWORD iDependent = 0; iDependent < pRegistration->cDependents; ++iDependent)
-            {
-                DEPENDENCY* pDependent = pRegistration->rgDependents + iDependent;
 
-                hr = DictKeyExists(sdIgnoreDependents, pDependent->sczKey);
-                if (E_NOTFOUND == 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;
+                    DEPENDENCY* pDependent = pRegistration->rgDependents + iDependent;
 
-                    // TODO: callback to the BA and let it have the option to ignore this dependent?
-                    if (!pPlan->fDisallowRemoval)
+                    hr = DictKeyExists(sdIgnoreDependents, pDependent->sczKey);
+                    if (E_NOTFOUND == hr)
                     {
-                        pPlan->fDisallowRemoval = TRUE; // ensure the registration stays
-                        *pfContinuePlanning = FALSE; // skip the rest of planning.
+                        hr = S_OK;
 
-                        LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS);
-                    }
+                        // 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_VERBOSE, MSG_DEPENDENCY_BUNDLE_DEPENDENT, pDependent->sczKey, LoggingStringOrUnknownIfNull(pDependent->sczName));
+                            LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS);
+                        }
+
+                        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.");
             }
         }
     }
diff --git a/src/engine/plan.h b/src/engine/plan.h
index e72186c7..77f82e1f 100644
--- a/src/engine/plan.h
+++ b/src/engine/plan.h
@@ -314,6 +314,7 @@ typedef struct _BURN_PLAN
     BOOL fDisallowRemoval;
     BOOL fDisableRollback;
     BOOL fAffectedMachineState;
+    BOOL fIgnoreAllDependents;
 
     DWORD64 qwCacheSizeTotal;
 
diff --git a/src/engine/registration.h b/src/engine/registration.h
index e0418fa3..39fee65f 100644
--- a/src/engine/registration.h
+++ b/src/engine/registration.h
@@ -146,6 +146,7 @@ typedef struct _BURN_REGISTRATION
     UINT cIgnoredDependencies;           // Only valid after detect.
     DEPENDENCY* rgDependents;            // Only valid after detect.
     UINT cDependents;                    // Only valid after detect.
+    BOOL fIgnoreAllDependents;           // Only valid after detect.
     LPCWSTR wzSelfDependent;             // Only valid after detect.
     BOOL fSelfRegisteredAsDependent;     // Only valid after detect.
     BOOL fParentRegisteredAsDependent;   // Only valid after detect.
-- 
cgit v1.2.3-55-g6feb