From a98115d996d65834e7c8d593c10d2cfa66096ccd Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 22 Feb 2021 19:58:44 -0600 Subject: Fix patch registration states during plan and apply. Add logging for slipstreamed patches. #6297 --- src/engine/apply.cpp | 32 ++++- src/engine/core.cpp | 25 +++- src/engine/dependency.cpp | 12 ++ src/engine/elevation.cpp | 23 +-- src/engine/engine.mc | 22 ++- src/engine/msiengine.cpp | 60 ++++++-- src/engine/msiengine.h | 1 + src/engine/mspengine.cpp | 38 ++++- src/engine/package.h | 5 +- src/engine/plan.cpp | 287 +++++++++++++++++++++---------------- src/engine/plan.h | 1 - src/test/BurnUnitTest/PlanTest.cpp | 2 +- 12 files changed, 344 insertions(+), 164 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 55141ef9..44ff9429 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -2032,7 +2032,7 @@ static HRESULT ExecuteMsiPackage( LExit: if (fExecuted) { - MsiEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute, fInsideMsiTransaction); + MsiEngineUpdateInstallRegistrationState(pExecuteAction, fRollback, hrExecute, fInsideMsiTransaction); } if (fBeginCalled) @@ -2248,7 +2248,20 @@ static HRESULT ExecuteDependencyAction( { pAction->packageDependency.pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; } - if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pAction->packageDependency.pPackage->installRegistrationState) + + if (BURN_PACKAGE_TYPE_MSP == pAction->packageDependency.pPackage->type) + { + for (DWORD i = 0; i < pAction->packageDependency.pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pAction->packageDependency.pPackage->Msp.rgTargetProducts + i; + + if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pTargetProduct->registrationState) + { + pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + } + } + else if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pAction->packageDependency.pPackage->installRegistrationState) { pAction->packageDependency.pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; } @@ -2259,7 +2272,20 @@ static HRESULT ExecuteDependencyAction( { pAction->packageDependency.pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; } - if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pAction->packageDependency.pPackage->installRegistrationState) + + if (BURN_PACKAGE_TYPE_MSP == pAction->packageDependency.pPackage->type) + { + for (DWORD i = 0; i < pAction->packageDependency.pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pAction->packageDependency.pPackage->Msp.rgTargetProducts + i; + + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pTargetProduct->registrationState) + { + pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + } + } + else if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pAction->packageDependency.pPackage->installRegistrationState) { pAction->packageDependency.pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; } diff --git a/src/engine/core.cpp b/src/engine/core.cpp index cb7ebbfd..0a19ac8e 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -1814,15 +1814,30 @@ static void LogPackages( 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), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedInstallRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedCacheRegistrationState)); - if (BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.cFeatures) + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) { - LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURES, pPackage->Msi.cFeatures, pPackage->sczId); + if (pPackage->Msi.cFeatures) + { + LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURES, pPackage->Msi.cFeatures, pPackage->sczId); + + for (DWORD j = 0; j < pPackage->Msi.cFeatures; ++j) + { + const BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[j]; - for (DWORD j = 0; j < pPackage->Msi.cFeatures; ++j) + LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURE, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState), LoggingMsiFeatureStateToString(pFeature->defaultRequested), LoggingMsiFeatureStateToString(pFeature->requested), LoggingMsiFeatureActionToString(pFeature->execute), LoggingMsiFeatureActionToString(pFeature->rollback)); + } + } + + if (pPackage->Msi.cSlipstreamMspPackages) { - const BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[j]; + LogId(REPORT_STANDARD, MSG_PLANNED_SLIPSTREAMED_MSP_TARGETS, pPackage->Msi.cSlipstreamMspPackages, pPackage->sczId); + + for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) + { + const BURN_SLIPSTREAM_MSP* pSlipstreamMsp = &pPackage->Msi.rgSlipstreamMsps[j]; - LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURE, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState), LoggingMsiFeatureStateToString(pFeature->defaultRequested), LoggingMsiFeatureStateToString(pFeature->requested), LoggingMsiFeatureActionToString(pFeature->execute), LoggingMsiFeatureActionToString(pFeature->rollback)); + LogId(REPORT_STANDARD, MSG_PLANNED_SLIPSTREAMED_MSP_TARGET, pSlipstreamMsp->pMspPackage->sczId, LoggingActionStateToString(pSlipstreamMsp->execute), LoggingActionStateToString(pSlipstreamMsp->rollback)); + } } } else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.cTargetProductCodes) diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp index a6a8fe4d..3d978740 100644 --- a/src/engine/dependency.cpp +++ b/src/engine/dependency.cpp @@ -774,6 +774,18 @@ static HRESULT DetectPackageDependents( { pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; } + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pTargetProduct->registrationState) + { + pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + } + } } LExit: diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index e4af1840..c9ed9810 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -855,6 +855,9 @@ extern "C" HRESULT ElevationExecuteMsiPackage( DWORD dwResult = 0; // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback); + ExitOnFailure(hr, "Failed to write rollback flag to message buffer."); + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.pPackage->sczId); ExitOnFailure(hr, "Failed to write package id to message buffer."); @@ -886,16 +889,15 @@ extern "C" HRESULT ElevationExecuteMsiPackage( // Slipstream patches actions. for (DWORD i = 0; i < pExecuteAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i) { - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.rgSlipstreamPatches[i]); + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pExecuteAction->msiPackage.pPackage->Msi.rgSlipstreamMsps + i; + BOOTSTRAPPER_ACTION_STATE* pAction = fRollback ? &pSlipstreamMsp->rollback : &pSlipstreamMsp->execute; + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)*pAction); ExitOnFailure(hr, "Failed to write slipstream patch action to message buffer."); } hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); ExitOnFailure(hr, "Failed to write variables."); - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback); - ExitOnFailure(hr, "Failed to write rollback flag to message buffer."); - // send message context.pfnMessageHandler = pfnMessageHandler; @@ -2263,6 +2265,9 @@ static HRESULT OnExecuteMsiPackage( executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; // Deserialize message data. + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback); + ExitOnFailure(hr, "Failed to read rollback flag."); + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); ExitOnFailure(hr, "Failed to read MSI package id."); @@ -2303,12 +2308,11 @@ static HRESULT OnExecuteMsiPackage( // Read slipstream patches actions. if (executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages) { - executeAction.msiPackage.rgSlipstreamPatches = (BOOTSTRAPPER_ACTION_STATE*)MemAlloc(executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages * sizeof(BOOTSTRAPPER_ACTION_STATE), TRUE); - ExitOnNull(executeAction.msiPackage.rgSlipstreamPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream patch actions."); - for (DWORD i = 0; i < executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i) { - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.rgSlipstreamPatches[i]); + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = executeAction.msiPackage.pPackage->Msi.rgSlipstreamMsps + i; + BOOTSTRAPPER_ACTION_STATE* pAction = fRollback ? &pSlipstreamMsp->rollback : &pSlipstreamMsp->execute; + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)pAction); ExitOnFailure(hr, "Failed to read slipstream action."); } } @@ -2316,9 +2320,6 @@ static HRESULT OnExecuteMsiPackage( hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); ExitOnFailure(hr, "Failed to read variables."); - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback); - ExitOnFailure(hr, "Failed to read rollback flag."); - // Execute MSI package. hr = MsiEngineExecutePackage(hwndParent, &executeAction, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &msiRestart); ExitOnFailure(hr, "Failed to execute MSI package."); diff --git a/src/engine/engine.mc b/src/engine/engine.mc index 1cdde207..f7b18c59 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -321,14 +321,14 @@ MessageId=203 Severity=Success SymbolicName=MSG_PLANNED_MSI_FEATURE Language=English -Planned feature: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute action: %5!hs!, rollback action: %6!hs! + Planned feature: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute action: %5!hs!, rollback action: %6!hs! . MessageId=204 Severity=Success SymbolicName=MSG_PLANNED_MSI_FEATURES Language=English -Plan %1!u! msi features for package: %2!ls! + Plan %1!u! msi features for package: %2!ls! . MessageId=205 @@ -419,14 +419,28 @@ MessageId=218 Severity=Success SymbolicName=MSG_PLANNED_MSP_TARGETS Language=English -Plan %1!u! patch targets for package: %2!ls! + Plan %1!u! patch targets for package: %2!ls! . MessageId=219 Severity=Success SymbolicName=MSG_PLANNED_MSP_TARGET Language=English -Planned patch target: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs! + Planned patch target: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs! +. + +MessageId=220 +Severity=Success +SymbolicName=MSG_PLANNED_SLIPSTREAMED_MSP_TARGETS +Language=English + Plan %1!u! slipstream patches for package: %2!ls! +. + +MessageId=221 +Severity=Success +SymbolicName=MSG_PLANNED_SLIPSTREAMED_MSP_TARGET +Language=English + Planned slipstreamed patch: %1!ls!, execute: %2!hs!, rollback: %3!hs! . MessageId=299 diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index ae105155..f0aa784e 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -42,7 +42,7 @@ static HRESULT ConcatFeatureActionProperties( ); static HRESULT ConcatPatchProperty( __in BURN_PACKAGE* pPackage, - __in_opt BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatchActions, + __in BOOL fRollback, __inout_z LPWSTR* psczArguments ); static void RegisterSourceDirectory( @@ -1180,10 +1180,10 @@ extern "C" HRESULT MsiEngineExecutePackage( ExitOnFailure(hr, "Failed to add feature action properties to obfuscated argument string."); // add slipstream patch properties - hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgSlipstreamPatches, &sczProperties); + hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, fRollback, &sczProperties); ExitOnFailure(hr, "Failed to add patch properties to argument string."); - hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgSlipstreamPatches, &sczObfuscatedProperties); + hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, fRollback, &sczObfuscatedProperties); ExitOnFailure(hr, "Failed to add patch properties to obfuscated argument string."); hr = MsiEngineConcatActionProperty(pExecuteAction->msiPackage.actionMsiProperty, &sczProperties); @@ -1432,6 +1432,7 @@ extern "C" HRESULT MsiEngineCalculateInstallUiLevel( extern "C" void MsiEngineUpdateInstallRegistrationState( __in BURN_EXECUTE_ACTION* pAction, + __in BOOL fRollback, __in HRESULT hrExecute, __in BOOL fInsideMsiTransaction ) @@ -1462,6 +1463,49 @@ extern "C" void MsiEngineUpdateInstallRegistrationState( pPackage->installRegistrationState = newState; } + if (BURN_PACKAGE_REGISTRATION_STATE_ABSENT == newState) + { + for (DWORD i = 0; i < pPackage->Msi.cChainedPatches; ++i) + { + BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + i; + BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; + + if (fInsideMsiTransaction) + { + pTargetProduct->transactionRegistrationState = newState; + } + else + { + pTargetProduct->registrationState = newState; + } + } + } + else + { + for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + i; + BOOTSTRAPPER_ACTION_STATE patchExecuteAction = fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute; + + if (BOOTSTRAPPER_ACTION_STATE_INSTALL > patchExecuteAction) + { + continue; + } + + BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pSlipstreamMsp->dwMsiChainedPatchIndex; + BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; + + if (fInsideMsiTransaction) + { + pTargetProduct->transactionRegistrationState = newState; + } + else + { + pTargetProduct->registrationState = newState; + } + } + } + LExit: return; } @@ -1911,7 +1955,7 @@ LExit: static HRESULT ConcatPatchProperty( __in BURN_PACKAGE* pPackage, - __in_opt BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatchActions, + __in BOOL fRollback, __inout_z LPWSTR* psczArguments ) { @@ -1921,14 +1965,14 @@ static HRESULT ConcatPatchProperty( LPWSTR sczPatches = NULL; // If there are slipstream patch actions, build up their patch action. - if (rgSlipstreamPatchActions) + if (pPackage->Msi.cSlipstreamMspPackages) { for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) { - BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[i].pMspPackage; - AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + i; + BURN_PACKAGE* pMspPackage = pSlipstreamMsp->pMspPackage; + BOOTSTRAPPER_ACTION_STATE patchExecuteAction = fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute; - BOOTSTRAPPER_ACTION_STATE patchExecuteAction = rgSlipstreamPatchActions[i]; if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < patchExecuteAction) { hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory); diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h index 6c7c83e5..e2dc5e82 100644 --- a/src/engine/msiengine.h +++ b/src/engine/msiengine.h @@ -95,6 +95,7 @@ HRESULT MsiEngineCalculateInstallUiLevel( ); void MsiEngineUpdateInstallRegistrationState( __in BURN_EXECUTE_ACTION* pAction, + __in BOOL fRollback, __in HRESULT hrExecute, __in BOOL fInsideMsiTransaction ); diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index 4b7f9a0a..ed105d24 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -372,6 +372,7 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( ) { HRESULT hr = S_OK; + BOOL fWillUninstallAll = TRUE; for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) { @@ -388,6 +389,7 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( { case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = BOOTSTRAPPER_ACTION_STATE_REPAIR; + fWillUninstallAll = FALSE; break; case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough; @@ -401,6 +403,7 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( default: execute = BOOTSTRAPPER_ACTION_STATE_NONE; + fWillUninstallAll = FALSE; break; } break; @@ -411,6 +414,7 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; + fWillUninstallAll = FALSE; break; default: @@ -418,6 +422,13 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( break; } break; + + default: + if (pTargetProduct->fInstalled) + { + fWillUninstallAll = FALSE; + } + break; } // Calculate the rollback action if there is an execute action. @@ -475,6 +486,13 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( } } + // The dependency manager will do the wrong thing if the package level action is UNINSTALL + // when the patch will still be applied to at least one product. + if (!fWillUninstallAll && BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) + { + pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; + } + return hr; } @@ -776,16 +794,22 @@ extern "C" void MspEngineFinalizeInstallRegistrationState( ExitFunction(); } - pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - - for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) + if (!pPackage->Msp.cTargetProductCodes) { - BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + else + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; - if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pTargetProduct->registrationState) + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) { - pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - break; + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + + if (pPackage->installRegistrationState < pTargetProduct->registrationState) + { + pPackage->installRegistrationState = pTargetProduct->registrationState; + } } } diff --git a/src/engine/package.h b/src/engine/package.h index 283afa57..3a95852e 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -83,8 +83,8 @@ enum BOOTSTRAPPER_FEATURE_ACTION enum BURN_PACKAGE_REGISTRATION_STATE { BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, - BURN_PACKAGE_REGISTRATION_STATE_IGNORED, BURN_PACKAGE_REGISTRATION_STATE_ABSENT, + BURN_PACKAGE_REGISTRATION_STATE_IGNORED, BURN_PACKAGE_REGISTRATION_STATE_PRESENT, }; @@ -186,6 +186,9 @@ typedef struct _BURN_SLIPSTREAM_MSP { BURN_PACKAGE* pMspPackage; DWORD dwMsiChainedPatchIndex; // index into the Msi.rgChainedPatches + + BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan. + BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan. } BURN_SLIPSTREAM_MSP; typedef struct _BURN_PACKAGE_PAYLOAD diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 86a07dfb..b3cb0ee3 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -154,11 +154,15 @@ static void RemoveUnnecessaryActions( __in BURN_EXECUTE_ACTION* rgActions, __in DWORD cActions ); -static HRESULT FinalizeSlipstreamPatchActions( +static void FinalizePatchActions( __in BOOL fExecute, __in BURN_EXECUTE_ACTION* rgActions, __in DWORD cActions ); +static void CalculateExpectedRegistrationStates( + __in BURN_PACKAGE* rgPackages, + __in DWORD cPackages + ); static HRESULT PlanDependencyActions( __in BOOL fBundlePerMachine, __in BURN_PLAN* pPlan, @@ -301,7 +305,6 @@ extern "C" void PlanUninitializeExecuteAction( case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: ReleaseStr(pExecuteAction->msiPackage.sczLogPath); ReleaseMem(pExecuteAction->msiPackage.rgFeatures); - ReleaseMem(pExecuteAction->msiPackage.rgSlipstreamPatches); break; case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: @@ -823,6 +826,8 @@ static HRESULT PlanPackagesHelper( hr = PlanFinalizeActions(pPlan); ExitOnFailure(hr, "Failed to remove unnecessary actions from plan."); + CalculateExpectedRegistrationStates(rgPackages, cPackages); + // Let the BA know the actions that were planned. for (DWORD i = 0; i < cPackages; ++i) { @@ -848,6 +853,12 @@ static HRESULT InitializePackage( BOOL fInstallCondition = FALSE; BOOL fBeginCalled = FALSE; + if (pPackage->fCanAffectRegistration) + { + pPackage->expectedCacheRegistrationState = pPackage->cacheRegistrationState; + pPackage->expectedInstallRegistrationState = pPackage->installRegistrationState; + } + if (pPackage->sczInstallCondition && *pPackage->sczInstallCondition) { hr = ConditionEvaluate(pVariables, pPackage->sczInstallCondition, &fInstallCondition); @@ -903,68 +914,24 @@ static HRESULT ProcessPackage( hr = ProcessPackageRollbackBoundary(pPlan, pEffectiveRollbackBoundary, ppRollbackBoundary); ExitOnFailure(hr, "Failed to process package rollback boundary."); - if (pPackage->fCanAffectRegistration) + if (BOOTSTRAPPER_ACTION_LAYOUT == pPlan->action) { - pPackage->expectedCacheRegistrationState = pPackage->cacheRegistrationState; - pPackage->expectedInstallRegistrationState = pPackage->installRegistrationState; + hr = PlanLayoutPackage(pPlan, pPackage, wzLayoutDirectory); + ExitOnFailure(hr, "Failed to plan layout package."); } - - // If the package is in a requested state, plan it. - if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested) + else { - if (BOOTSTRAPPER_ACTION_LAYOUT == pPlan->action) - { - hr = PlanLayoutPackage(pPlan, pPackage, wzLayoutDirectory); - ExitOnFailure(hr, "Failed to plan layout package."); - } - else + if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested) { + // If the package is in a requested state, plan it. hr = PlanExecutePackage(fBundlePerMachine, display, pUX, pPlan, pPackage, pLog, pVariables, phSyncpointEvent); ExitOnFailure(hr, "Failed to plan execute package."); - - if (pPackage->fCanAffectRegistration) - { - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->execute) - { - pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) - { - pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - } - } - } - } - else if (BOOTSTRAPPER_ACTION_LAYOUT != pPlan->action) - { - // Make sure the package is properly ref-counted even if no plan is requested. - hr = PlanDependencyActions(fBundlePerMachine, pPlan, pPackage); - ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId); - } - - if (pPackage->fCanAffectRegistration) - { - if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute) - { - if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedCacheRegistrationState) - { - pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedInstallRegistrationState) - { - pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } } - else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pPackage->dependencyExecute) + else { - if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedCacheRegistrationState) - { - pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; - } - if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedInstallRegistrationState) - { - pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; - } + // Make sure the package is properly ref-counted even if no plan is requested. + hr = PlanDependencyActions(fBundlePerMachine, pPlan, pPackage); + ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId); } } @@ -1498,17 +1465,14 @@ extern "C" HRESULT PlanFinalizeActions( { HRESULT hr = S_OK; - RemoveUnnecessaryActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); + FinalizePatchActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); - RemoveUnnecessaryActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); + FinalizePatchActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); - hr = FinalizeSlipstreamPatchActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); - ExitOnFailure(hr, "Failed to finalize slipstream execute actions."); + RemoveUnnecessaryActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); - hr = FinalizeSlipstreamPatchActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); - ExitOnFailure(hr, "Failed to finalize slipstream rollback actions."); + RemoveUnnecessaryActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); -LExit: return hr; } @@ -1868,7 +1832,7 @@ static void ResetPlannedPackageState( pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; - if (BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.rgFeatures) + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) { for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) { @@ -1880,6 +1844,14 @@ static void ResetPlannedPackageState( pFeature->execute = BOOTSTRAPPER_FEATURE_ACTION_NONE; pFeature->rollback = BOOTSTRAPPER_FEATURE_ACTION_NONE; } + + for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = &pPackage->Msi.rgSlipstreamMsps[i]; + + pSlipstreamMsp->execute = BOOTSTRAPPER_ACTION_STATE_NONE; + pSlipstreamMsp->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + } } else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.rgTargetProducts) { @@ -2716,101 +2688,165 @@ static void RemoveUnnecessaryActions( { BURN_EXECUTE_ACTION* pAction = rgActions + i; - // If this MSP targets a package in the chain, check the target's execute state - // to see if this patch should be skipped. if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type && pAction->mspTarget.pChainedTargetPackage) { + BURN_MSPTARGETPRODUCT* pFirstTargetProduct = pAction->mspTarget.rgOrderedPatches->pTargetProduct; + BURN_PATCH_SKIP_STATE skipState = fExecute ? pFirstTargetProduct->executeSkip : pFirstTargetProduct->rollbackSkip; BOOTSTRAPPER_ACTION_STATE chainedTargetPackageAction = fExecute ? pAction->mspTarget.pChainedTargetPackage->execute : pAction->mspTarget.pChainedTargetPackage->rollback; - BURN_PATCH_SKIP_STATE skipState = BURN_PATCH_SKIP_STATE_NONE; - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == chainedTargetPackageAction) - { - skipState = BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL; - LogId(REPORT_STANDARD, MSG_PLAN_SKIP_PATCH_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); - } - else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < chainedTargetPackageAction && pAction->mspTarget.fSlipstream && BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pAction->mspTarget.action) - { - // If the slipstream target is being installed or upgraded (not uninstalled or repaired) then we will slipstream so skip - // this action to install the patch standalone. Also, if the slipstream target is being repaired and the patch is being - // repaired, skip this operation since it will be redundant. - // - // The primary goal here is to ensure that a slipstream patch that is yet not installed is installed even if the MSI - // is already on the machine. The slipstream must be installed standalone if the MSI is being repaired. - if (BOOTSTRAPPER_ACTION_STATE_REPAIR != chainedTargetPackageAction || BOOTSTRAPPER_ACTION_STATE_REPAIR == pAction->mspTarget.action) - { - skipState = BURN_PATCH_SKIP_STATE_SLIPSTREAM; - LogId(REPORT_STANDARD, MSG_PLAN_SKIP_SLIPSTREAM_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); - } - } - if (BURN_PATCH_SKIP_STATE_NONE != skipState) + switch (skipState) { + case BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL: pAction->fDeleted = TRUE; - - for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j) - { - BURN_MSPTARGETPRODUCT* pTargetProduct = pAction->mspTarget.rgOrderedPatches[j].pTargetProduct; - - if (fExecute) - { - pTargetProduct->executeSkip = skipState; - } - else - { - pTargetProduct->rollbackSkip = skipState; - } - } + LogId(REPORT_STANDARD, MSG_PLAN_SKIP_PATCH_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); + break; + case BURN_PATCH_SKIP_STATE_SLIPSTREAM: + pAction->fDeleted = TRUE; + LogId(REPORT_STANDARD, MSG_PLAN_SKIP_SLIPSTREAM_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); + break; } } } } -static HRESULT FinalizeSlipstreamPatchActions( +static void FinalizePatchActions( __in BOOL fExecute, __in BURN_EXECUTE_ACTION* rgActions, __in DWORD cActions ) { - HRESULT hr = S_OK; - for (DWORD i = 0; i < cActions; ++i) { BURN_EXECUTE_ACTION* pAction = rgActions + i; - // If this MSI package contains slipstream patches store the slipstream actions. - if (BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE == pAction->type && pAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages) + if (BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE == pAction->type) { BURN_PACKAGE* pPackage = pAction->msiPackage.pPackage; + AssertSz(BOOTSTRAPPER_ACTION_STATE_NONE < pAction->msiPackage.action, "Planned execute MSI action to do nothing"); - // By default all slipstream actions will be initialized to "no action" (aka: 0). - pAction->msiPackage.rgSlipstreamPatches = (BOOTSTRAPPER_ACTION_STATE*)MemAlloc(sizeof(BOOTSTRAPPER_ACTION_STATE) * pPackage->Msi.cSlipstreamMspPackages, TRUE); - ExitOnNull(pAction->msiPackage.rgSlipstreamPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch actions."); + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msiPackage.action) + { + // If we are uninstalling the MSI, we must skip all the patches. + for (DWORD j = 0; j < pPackage->Msi.cChainedPatches; ++j) + { + BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + j; + BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; - // If we are uninstalling or repairing the MSI, we must ignore all the slipstream patches because they cannot - // be applied right now. - if (BOOTSTRAPPER_ACTION_STATE_REPAIR != pAction->msiPackage.action && BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pAction->msiPackage.action) + if (fExecute) + { + pTargetProduct->execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; + pTargetProduct->executeSkip = BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL; + } + else + { + pTargetProduct->rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; + pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL; + } + } + } + else { + // If the slipstream target is being installed or upgraded (not uninstalled or repaired) then we will slipstream so skip + // the patch's standalone action. Also, if the slipstream target is being repaired and the patch is being + // repaired, skip this operation since it will be redundant. + // + // The primary goal here is to ensure that a slipstream patch that is yet not installed is installed even if the MSI + // is already on the machine. The slipstream must be installed standalone if the MSI is being repaired. for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) { - BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[j].pMspPackage; - AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); - - pAction->msiPackage.rgSlipstreamPatches[j] = fExecute ? pMspPackage->execute : pMspPackage->rollback; - for (DWORD k = 0; k < pMspPackage->Msp.cTargetProductCodes; ++k) + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + j; + BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pSlipstreamMsp->dwMsiChainedPatchIndex; + BURN_MSPTARGETPRODUCT* pTargetProduct = pSlipstreamMsp->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; + BOOTSTRAPPER_ACTION_STATE action = fExecute ? pTargetProduct->execute : pTargetProduct->rollback; + BOOL fSlipstream = BOOTSTRAPPER_ACTION_STATE_UNINSTALL < action && + (BOOTSTRAPPER_ACTION_STATE_REPAIR != pAction->msiPackage.action || BOOTSTRAPPER_ACTION_STATE_REPAIR == action); + + if (fSlipstream) { - BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + k; - if (pPackage == pTargetProduct->pChainedTargetPackage) + if (fExecute) + { + pSlipstreamMsp->execute = action; + pTargetProduct->executeSkip = BURN_PATCH_SKIP_STATE_SLIPSTREAM; + } + else { - pAction->msiPackage.rgSlipstreamPatches[j] = fExecute ? pTargetProduct->execute : pTargetProduct->rollback; - break; + pSlipstreamMsp->rollback = action; + pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_SLIPSTREAM; } } } } } } +} -LExit: - return hr; +static void CalculateExpectedRegistrationStates( + __in BURN_PACKAGE* rgPackages, + __in DWORD cPackages + ) +{ + for (DWORD i = 0; i < cPackages; ++i) + { + BURN_PACKAGE* pPackage = rgPackages + i; + + // MspPackages can have actions throughout the plan, so the plan needed to be finalized before anything could be calculated. + if (BURN_PACKAGE_TYPE_MSP == pPackage->type && !pPackage->fDependencyManagerWasHere) + { + pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; + pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + + for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j; + + // The highest aggregate action state found will be used. + if (pPackage->execute < pTargetProduct->execute) + { + pPackage->execute = pTargetProduct->execute; + } + + if (pPackage->rollback < pTargetProduct->rollback) + { + pPackage->rollback = pTargetProduct->rollback; + } + } + } + + if (pPackage->fCanAffectRegistration) + { + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->execute) + { + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) + { + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + + if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute) + { + if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedCacheRegistrationState) + { + pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedInstallRegistrationState) + { + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + } + else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pPackage->dependencyExecute) + { + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedCacheRegistrationState) + { + pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedInstallRegistrationState) + { + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + } + } + } } static HRESULT PlanDependencyActions( @@ -3064,6 +3100,11 @@ static void ExecuteActionLog( case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: LogStringLine(PlanDumpLevel, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), LoggingBurnMsiPropertyToString(pAction->msiPackage.actionMsiProperty), pAction->msiPackage.uiLevel, pAction->msiPackage.fDisableExternalUiHandler ? L"yes" : L"no", pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes); + for (DWORD j = 0; j < pAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++j) + { + const BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pAction->msiPackage.pPackage->Msi.rgSlipstreamMsps + j; + LogStringLine(PlanDumpLevel, " Patch[%u]: msp package id: %ls, action: %hs", j, pSlipstreamMsp->pMspPackage->sczId, LoggingActionStateToString(fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute)); + } break; case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: diff --git a/src/engine/plan.h b/src/engine/plan.h index 5e981561..5e1985ea 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -253,7 +253,6 @@ typedef struct _BURN_EXECUTE_ACTION BOOTSTRAPPER_ACTION_STATE action; BOOTSTRAPPER_FEATURE_ACTION* rgFeatures; - BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatches; } msiPackage; struct { diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index 820f88c2..40a61fe3 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -760,7 +760,7 @@ namespace Bootstrapper Assert::Equal(3ul, pEngineState->packages.cPackages); ValidatePermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"NetFx48Web"); ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PatchA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PatchA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); } [Fact] -- cgit v1.2.3-55-g6feb