From 4f4c85ed66f1b2dfb1bec76d54d7b50c637d5bfa Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 22 Feb 2021 16:16:12 -0600 Subject: Add patch target for slipstream MSI package even if not installed. Fixes #3897 --- src/engine/core.cpp | 3 + src/engine/detect.cpp | 10 + src/engine/msiengine.cpp | 56 +++- src/engine/msiengine.h | 3 + src/engine/mspengine.cpp | 196 ++++++++----- src/engine/mspengine.h | 16 +- src/engine/package.cpp | 23 +- src/engine/package.h | 21 +- src/engine/plan.cpp | 4 +- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 1 + src/test/BurnUnitTest/PlanTest.cpp | 303 ++++++++++++++++++++- .../PlanTest/Slipstream_BundleA_manifest.xml | 1 + 12 files changed, 546 insertions(+), 91 deletions(-) create mode 100644 src/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 1a079973..cb7ebbfd 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -351,6 +351,9 @@ extern "C" HRESULT CoreDetect( { hr = MspEngineDetectInitialize(&pEngineState->packages); ExitOnFailure(hr, "Failed to initialize MSP engine detection."); + + hr = MsiEngineDetectInitialize(&pEngineState->packages); + ExitOnFailure(hr, "Failed to initialize MSI engine detection."); } for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp index b702306e..9bb58487 100644 --- a/src/engine/detect.cpp +++ b/src/engine/detect.cpp @@ -82,6 +82,16 @@ extern "C" void DetectReset( pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; } + + for (DWORD iSlipstreamMsp = 0; iSlipstreamMsp < pPackage->Msi.cSlipstreamMspPackages; ++iSlipstreamMsp) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + iSlipstreamMsp; + + pSlipstreamMsp->dwMsiChainedPatchIndex = BURN_PACKAGE_INVALID_PATCH_INDEX; + } + + ReleaseNullMem(pPackage->Msi.rgChainedPatches); + pPackage->Msi.cChainedPatches = 0; } else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) { diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index 801bf9a8..ae105155 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -213,8 +213,8 @@ extern "C" HRESULT MsiEngineParsePackageFromXml( if (cNodes) { - pPackage->Msi.rgpSlipstreamMspPackages = reinterpret_cast(MemAlloc(sizeof(BURN_PACKAGE*) * cNodes, TRUE)); - ExitOnNull(pPackage->Msi.rgpSlipstreamMspPackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP packages."); + pPackage->Msi.rgSlipstreamMsps = reinterpret_cast(MemAlloc(sizeof(BURN_SLIPSTREAM_MSP) * cNodes, TRUE)); + ExitOnNull(pPackage->Msi.rgSlipstreamMsps, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP packages."); pPackage->Msi.rgsczSlipstreamMspPackageIds = reinterpret_cast(MemAlloc(sizeof(LPWSTR*) * cNodes, TRUE)); ExitOnNull(pPackage->Msi.rgsczSlipstreamMspPackageIds, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP ids."); @@ -383,15 +383,52 @@ extern "C" void MsiEnginePackageUninitialize( MemFree(pPackage->Msi.rgsczSlipstreamMspPackageIds); } - if (pPackage->Msi.rgpSlipstreamMspPackages) + if (pPackage->Msi.rgSlipstreamMsps) { - MemFree(pPackage->Msi.rgpSlipstreamMspPackages); + MemFree(pPackage->Msi.rgSlipstreamMsps); + } + + if (pPackage->Msi.rgChainedPatches) + { + MemFree(pPackage->Msi.rgChainedPatches); } // clear struct memset(&pPackage->Msi, 0, sizeof(pPackage->Msi)); } +extern "C" HRESULT MsiEngineDetectInitialize( + __in BURN_PACKAGES* pPackages + ) +{ + AssertSz(pPackages->cPatchInfo, "MsiEngineDetectInitialize() should only be called if there are MSP packages."); + + HRESULT hr = S_OK; + + // Add target products for slipstream MSIs that weren't detected. + for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) + { + BURN_PACKAGE* pMsiPackage = pPackages->rgPackages + iPackage; + if (BURN_PACKAGE_TYPE_MSI == pMsiPackage->type) + { + for (DWORD j = 0; j < pMsiPackage->Msi.cSlipstreamMspPackages; ++j) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pMsiPackage->Msi.rgSlipstreamMsps + j; + Assert(pSlipstreamMsp->pMspPackage && BURN_PACKAGE_TYPE_MSP == pSlipstreamMsp->pMspPackage->type); + + if (pSlipstreamMsp->pMspPackage && BURN_PACKAGE_INVALID_PATCH_INDEX == pSlipstreamMsp->dwMsiChainedPatchIndex) + { + hr = MspEngineAddMissingSlipstreamTarget(pMsiPackage, pSlipstreamMsp); + ExitOnFailure(hr, "Failed to add slipstreamed target product code to package: %ls", pSlipstreamMsp->pMspPackage->sczId); + } + } + } + } + +LExit: + return hr; +} + extern "C" HRESULT MsiEngineDetectPackage( __in BURN_PACKAGE* pPackage, __in BURN_USER_EXPERIENCE* pUserExperience @@ -971,15 +1008,6 @@ extern "C" HRESULT MsiEnginePlanAddPackage( pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes; } - // Update any slipstream patches' state. - for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) - { - BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i]; - AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); - - MspEngineSlipstreamUpdateState(pMspPackage, pPackage->execute, pPackage->rollback); - } - LExit: ReleaseMem(rgFeatureActions); ReleaseMem(rgRollbackFeatureActions); @@ -1897,7 +1925,7 @@ static HRESULT ConcatPatchProperty( { for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) { - BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i]; + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[i].pMspPackage; AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); BOOTSTRAPPER_ACTION_STATE patchExecuteAction = rgSlipstreamPatchActions[i]; diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h index fe742a16..6c7c83e5 100644 --- a/src/engine/msiengine.h +++ b/src/engine/msiengine.h @@ -27,6 +27,9 @@ HRESULT MsiEngineParsePropertiesFromXml( void MsiEnginePackageUninitialize( __in BURN_PACKAGE* pPackage ); +HRESULT MsiEngineDetectInitialize( + __in BURN_PACKAGES* pPackages + ); HRESULT MsiEngineDetectPackage( __in BURN_PACKAGE* pPackage, __in BURN_USER_EXPERIENCE* pUserExperience diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index 14db27a6..4b7f9a0a 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -30,18 +30,23 @@ static HRESULT AddPossibleTargetProduct( __inout DWORD* pcPossibleTargetProducts ); static HRESULT AddDetectedTargetProduct( - __in BURN_PACKAGES* pPackages, __in BURN_PACKAGE* pPackage, __in DWORD dwOrder, __in_z LPCWSTR wzProductCode, - __in MSIINSTALLCONTEXT context + __in MSIINSTALLCONTEXT context, + __out DWORD* pdwTargetProductIndex + ); +static HRESULT AddMsiChainedPatch( + __in BURN_PACKAGE* pPackage, + __in BURN_PACKAGE* pMspPackage, + __in DWORD dwMspTargetProductIndex, + __out DWORD* pdwChainedPatchIndex ); -static void DeterminePatchChainedTarget( +static HRESULT DeterminePatchChainedTarget( __in BURN_PACKAGES* pPackages, __in BURN_PACKAGE* pMspPackage, __in LPCWSTR wzTargetProductCode, - __out BURN_PACKAGE** ppChainedTargetPackage, - __out BOOL* pfSlipstreamed + __in DWORD dwMspTargetProductIndex ); static HRESULT PlanTargetProduct( __in BOOTSTRAPPER_DISPLAY display, @@ -159,16 +164,20 @@ extern "C" HRESULT MspEngineDetectInitialize( { for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) { - if (ERROR_SUCCESS == pPackages->rgPatchInfo[iPatchInfo].uStatus) - { - BURN_PACKAGE* pMspPackage = pPackages->rgPatchInfoToPackage[iPatchInfo]; - Assert(BURN_PACKAGE_TYPE_MSP == pMspPackage->type); + hr = HRESULT_FROM_WIN32(pPackages->rgPatchInfo[iPatchInfo].uStatus); + BURN_PACKAGE* pMspPackage = pPackages->rgPatchInfoToPackage[iPatchInfo]; + Assert(BURN_PACKAGE_TYPE_MSP == pMspPackage->type); + if (S_OK == hr) + { // Note that we do add superseded and obsolete MSP packages. Package Detect and Plan will sort them out later. - hr = AddDetectedTargetProduct(pPackages, pMspPackage, pPackages->rgPatchInfo[iPatchInfo].dwOrder, pPossibleTargetProduct->wzProductCode, pPossibleTargetProduct->context); + hr = MspEngineAddDetectedTargetProduct(pPackages, pMspPackage, pPackages->rgPatchInfo[iPatchInfo].dwOrder, pPossibleTargetProduct->wzProductCode, pPossibleTargetProduct->context); ExitOnFailure(hr, "Failed to add target product code to package: %ls", pMspPackage->sczId); } - // TODO: should we log something for this error case? + else + { + LogStringLine(REPORT_DEBUG, " 0x%x: Patch applicability failed for package: %ls", hr, pMspPackage->sczId); + } } } else @@ -192,6 +201,54 @@ LExit: return hr; } +extern "C" HRESULT MspEngineAddDetectedTargetProduct( + __in BURN_PACKAGES* pPackages, + __in BURN_PACKAGE* pPackage, + __in DWORD dwOrder, + __in_z LPCWSTR wzProductCode, + __in MSIINSTALLCONTEXT context + ) +{ + HRESULT hr = S_OK; + DWORD dwTargetProductIndex = 0; + + hr = AddDetectedTargetProduct(pPackage, dwOrder, wzProductCode, context, &dwTargetProductIndex); + ExitOnFailure(hr, "Failed to add detected target product."); + + hr = DeterminePatchChainedTarget(pPackages, pPackage, wzProductCode, dwTargetProductIndex); + ExitOnFailure(hr, "Failed to determine patch chained target."); + +LExit: + return hr; +} + +extern "C" HRESULT MspEngineAddMissingSlipstreamTarget( + __in BURN_PACKAGE* pMsiPackage, + __in BURN_SLIPSTREAM_MSP* pSlipstreamMsp + ) +{ + HRESULT hr = S_OK; + DWORD dwTargetProductIndex = 0; + BURN_MSPTARGETPRODUCT* pTargetProduct = NULL; + DWORD dwChainedPatchIndex = 0; + + hr = AddDetectedTargetProduct(pSlipstreamMsp->pMspPackage, 0, pMsiPackage->Msi.sczProductCode, pMsiPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, &dwTargetProductIndex); + ExitOnFailure(hr, "Failed to add missing slipstream target."); + + pTargetProduct = pSlipstreamMsp->pMspPackage->Msp.rgTargetProducts + dwTargetProductIndex; + pTargetProduct->fSlipstream = TRUE; + pTargetProduct->fSlipstreamRequired = TRUE; + pTargetProduct->pChainedTargetPackage = pMsiPackage; + + hr = AddMsiChainedPatch(pMsiPackage, pSlipstreamMsp->pMspPackage, dwTargetProductIndex, &dwChainedPatchIndex); + ExitOnFailure(hr, "Failed to add chained patch."); + + pSlipstreamMsp->dwMsiChainedPatchIndex = dwChainedPatchIndex; + +LExit: + return hr; +} + extern "C" HRESULT MspEngineDetectPackage( __in BURN_PACKAGE* pPackage, __in BURN_USER_EXPERIENCE* pUserExperience @@ -219,7 +276,6 @@ extern "C" HRESULT MspEngineDetectPackage( for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) { BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; - BOOL fInstalled = FALSE; hr = WiuGetPatchInfoEx(pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode, NULL, pTargetProduct->context, INSTALLPROPERTY_PATCHSTATE, &sczState); if (SUCCEEDED(hr)) @@ -227,17 +283,17 @@ extern "C" HRESULT MspEngineDetectPackage( switch (*sczState) { case '1': - fInstalled = TRUE; + pTargetProduct->fInstalled = TRUE; pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; break; case '2': - fInstalled = TRUE; + pTargetProduct->fInstalled = TRUE; pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; break; case '4': - fInstalled = TRUE; + pTargetProduct->fInstalled = TRUE; pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE; break; @@ -246,7 +302,7 @@ extern "C" HRESULT MspEngineDetectPackage( break; } } - else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PATCH) == hr) + else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PATCH) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) { pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; hr = S_OK; @@ -260,9 +316,9 @@ extern "C" HRESULT MspEngineDetectPackage( if (pPackage->fCanAffectRegistration) { - pTargetProduct->registrationState = fInstalled ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + pTargetProduct->registrationState = pTargetProduct->fInstalled ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - if (fInstalled) + if (pTargetProduct->fInstalled) { pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; } @@ -290,6 +346,13 @@ extern "C" HRESULT MspEnginePlanInitializePackage( { BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + if (!pTargetProduct->fInstalled && pTargetProduct->fSlipstreamRequired && BOOTSTRAPPER_REQUEST_STATE_PRESENT > pTargetProduct->pChainedTargetPackage->requested) + { + // There's no way to apply the patch if the target isn't installed. + pTargetProduct->defaultRequested = pTargetProduct->requested = BOOTSTRAPPER_REQUEST_STATE_NONE; + continue; + } + pTargetProduct->defaultRequested = pTargetProduct->requested = pPackage->requested; hr = UserExperienceOnPlanPatchTarget(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, &pTargetProduct->requested); @@ -637,32 +700,6 @@ LExit: return hr; } -extern "C" void MspEngineSlipstreamUpdateState( - __in BURN_PACKAGE* pPackage, - __in BOOTSTRAPPER_ACTION_STATE execute, - __in BOOTSTRAPPER_ACTION_STATE rollback - ) -{ - Assert(BURN_PACKAGE_TYPE_MSP == pPackage->type); - - // If the dependency manager set our state then that means something else - // is dependent on our package. That trumps whatever the slipstream update - // state might set. - if (!pPackage->fDependencyManagerWasHere) - { - // The highest aggregate action state found will be returned. - if (pPackage->execute < execute) - { - pPackage->execute = execute; - } - - if (pPackage->rollback < rollback) - { - pPackage->rollback = rollback; - } - } -} - extern "C" void MspEngineUpdateInstallRegistrationState( __in BURN_EXECUTE_ACTION* pAction, __in HRESULT hrExecute, @@ -926,43 +963,68 @@ LExit: } static HRESULT AddDetectedTargetProduct( - __in BURN_PACKAGES* pPackages, __in BURN_PACKAGE* pPackage, __in DWORD dwOrder, __in_z LPCWSTR wzProductCode, - __in MSIINSTALLCONTEXT context + __in MSIINSTALLCONTEXT context, + __out DWORD* pdwTargetProductIndex ) { HRESULT hr = S_OK; + BURN_MSPTARGETPRODUCT* pTargetProduct = NULL; + + *pdwTargetProductIndex = BURN_PACKAGE_INVALID_PATCH_INDEX; hr = MemEnsureArraySize(reinterpret_cast(&pPackage->Msp.rgTargetProducts), pPackage->Msp.cTargetProductCodes + 1, sizeof(BURN_MSPTARGETPRODUCT), 5); ExitOnFailure(hr, "Failed to ensure enough target product codes were allocated."); - hr = ::StringCchCopyW(pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].wzTargetProductCode, countof(pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].wzTargetProductCode), wzProductCode); + pTargetProduct = pPackage->Msp.rgTargetProducts + pPackage->Msp.cTargetProductCodes; + + hr = ::StringCchCopyW(pTargetProduct->wzTargetProductCode, countof(pTargetProduct->wzTargetProductCode), wzProductCode); ExitOnFailure(hr, "Failed to copy target product code."); - DeterminePatchChainedTarget(pPackages, pPackage, wzProductCode, - &pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].pChainedTargetPackage, - &pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].fSlipstream); + pTargetProduct->context = context; + pTargetProduct->dwOrder = dwOrder; - pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].context = context; - pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].dwOrder = dwOrder; + *pdwTargetProductIndex = pPackage->Msp.cTargetProductCodes; ++pPackage->Msp.cTargetProductCodes; LExit: return hr; } -static void DeterminePatchChainedTarget( +static HRESULT AddMsiChainedPatch( + __in BURN_PACKAGE* pPackage, + __in BURN_PACKAGE* pMspPackage, + __in DWORD dwMspTargetProductIndex, + __out DWORD* pdwChainedPatchIndex + ) +{ + HRESULT hr = S_OK; + + hr = MemEnsureArraySize(reinterpret_cast(&pPackage->Msi.rgChainedPatches), pPackage->Msi.cChainedPatches + 1, sizeof(BURN_CHAINED_PATCH), 5); + ExitOnFailure(hr, "Failed to ensure enough chained patches were allocated."); + + BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pPackage->Msi.cChainedPatches; + pChainedPatch->pMspPackage = pMspPackage; + pChainedPatch->dwMspTargetProductIndex = dwMspTargetProductIndex; + + *pdwChainedPatchIndex = pPackage->Msi.cChainedPatches; + ++pPackage->Msi.cChainedPatches; +LExit: + return hr; +} + +static HRESULT DeterminePatchChainedTarget( __in BURN_PACKAGES* pPackages, __in BURN_PACKAGE* pMspPackage, __in LPCWSTR wzTargetProductCode, - __out BURN_PACKAGE** ppChainedTargetPackage, - __out BOOL* pfSlipstreamed + __in DWORD dwMspTargetProductIndex ) { - BURN_PACKAGE* pTargetMsiPackage = NULL; - BOOL fSlipstreamed = FALSE; + HRESULT hr = S_OK; + DWORD dwChainedPatchIndex = 0; + BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + dwMspTargetProductIndex; for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) { @@ -970,15 +1032,19 @@ static void DeterminePatchChainedTarget( if (BURN_PACKAGE_TYPE_MSI == pPackage->type && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTargetProductCode, -1, pPackage->Msi.sczProductCode, -1)) { - pTargetMsiPackage = pPackage; + pTargetProduct->pChainedTargetPackage = pPackage; + + hr = AddMsiChainedPatch(pPackage, pMspPackage, dwMspTargetProductIndex, &dwChainedPatchIndex); + ExitOnFailure(hr, "Failed to add chained patch."); for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) { - BURN_PACKAGE* pSlipstreamMsp = pPackage->Msi.rgpSlipstreamMspPackages[j]; - if (pSlipstreamMsp == pMspPackage) + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + j; + if (pSlipstreamMsp->pMspPackage == pMspPackage) { - AssertSz(!fSlipstreamed, "An MSP should only show up as a slipstreamed patch in an MSI once."); - fSlipstreamed = TRUE; + AssertSz(BURN_PACKAGE_INVALID_PATCH_INDEX == pSlipstreamMsp->dwMsiChainedPatchIndex, "An MSP should only show up as a slipstreamed patch in an MSI once."); + pTargetProduct->fSlipstream = TRUE; + pSlipstreamMsp->dwMsiChainedPatchIndex = dwChainedPatchIndex; break; } } @@ -987,10 +1053,8 @@ static void DeterminePatchChainedTarget( } } - *ppChainedTargetPackage = pTargetMsiPackage; - *pfSlipstreamed = fSlipstreamed; - - return; +LExit: + return hr; } static HRESULT PlanTargetProduct( diff --git a/src/engine/mspengine.h b/src/engine/mspengine.h index 28682169..1530954b 100644 --- a/src/engine/mspengine.h +++ b/src/engine/mspengine.h @@ -28,6 +28,17 @@ void MspEnginePackageUninitialize( HRESULT MspEngineDetectInitialize( __in BURN_PACKAGES* pPackages ); +HRESULT MspEngineAddDetectedTargetProduct( + __in BURN_PACKAGES* pPackages, + __in BURN_PACKAGE* pPackage, + __in DWORD dwOrder, + __in_z LPCWSTR wzProductCode, + __in MSIINSTALLCONTEXT context + ); +HRESULT MspEngineAddMissingSlipstreamTarget( + __in BURN_PACKAGE* pMsiPackage, + __in BURN_SLIPSTREAM_MSP* pSlipstreamMsp + ); HRESULT MspEngineDetectPackage( __in BURN_PACKAGE* pPackage, __in BURN_USER_EXPERIENCE* pUserExperience @@ -59,11 +70,6 @@ HRESULT MspEngineExecutePackage( __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ); -void MspEngineSlipstreamUpdateState( - __in BURN_PACKAGE* pMspPackage, - __in BOOTSTRAPPER_ACTION_STATE execute, - __in BOOTSTRAPPER_ACTION_STATE rollback - ); void MspEngineUpdateInstallRegistrationState( __in BURN_EXECUTE_ACTION* pAction, __in HRESULT hrExecute, diff --git a/src/engine/package.cpp b/src/engine/package.cpp index bb61cdcd..115866f3 100644 --- a/src/engine/package.cpp +++ b/src/engine/package.cpp @@ -282,7 +282,9 @@ extern "C" HRESULT PackagesParseFromXml( { if (pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k] && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k], -1)) { - pMsiPackage->Msi.rgpSlipstreamMspPackages[k] = pPackage; + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pMsiPackage->Msi.rgSlipstreamMsps + k; + pSlipstreamMsp->pMspPackage = pPackage; + pSlipstreamMsp->dwMsiChainedPatchIndex = BURN_PACKAGE_INVALID_PATCH_INDEX; ReleaseNullStr(pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k]); // we don't need the slipstream package id any longer so free it. } @@ -295,6 +297,25 @@ extern "C" HRESULT PackagesParseFromXml( AssertSz(pPackages->cPatchInfo == cMspPackages, "Count of packages patch info should be equal to the number of MSP packages."); +#if DEBUG + // Loop through all MSI packages seeing if any of them are missing their slipstream MSP. + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + BURN_PACKAGE* pPackage = &pPackages->rgPackages[i]; + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + for (DWORD k = 0; k < pPackage->Msi.cSlipstreamMspPackages; ++k) + { + if (pPackage->Msi.rgsczSlipstreamMspPackageIds[k]) + { + AssertSz(FALSE, "MSI slipstream MSP package doesn't exist."); + } + } + } + } +#endif + hr = ParsePatchTargetCode(pPackages, pixnBundle); ExitOnFailure(hr, "Failed to parse target product codes."); diff --git a/src/engine/package.h b/src/engine/package.h index 3a243c7d..283afa57 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -14,6 +14,8 @@ typedef _BURN_PACKAGE BURN_PACKAGE; // constants +const DWORD BURN_PACKAGE_INVALID_PATCH_INDEX = 0x80000000; + enum BURN_EXE_EXIT_CODE_TYPE { BURN_EXE_EXIT_CODE_TYPE_NONE, @@ -116,7 +118,9 @@ typedef struct _BURN_MSPTARGETPRODUCT DWORD dwOrder; WCHAR wzTargetProductCode[39]; BURN_PACKAGE* pChainedTargetPackage; + BOOL fInstalled; BOOL fSlipstream; + BOOL fSlipstreamRequired; // this means the target product is not present on the machine, but is available in the chain as a slipstream target. BOOTSTRAPPER_PACKAGE_STATE patchPackageState; // only valid after Detect. BOOTSTRAPPER_REQUEST_STATE defaultRequested; // only valid during Plan. @@ -172,6 +176,18 @@ typedef struct _BURN_RELATED_MSI DWORD cLanguages; } BURN_RELATED_MSI; +typedef struct _BURN_CHAINED_PATCH +{ + BURN_PACKAGE* pMspPackage; + DWORD dwMspTargetProductIndex; // index into the Msp.rgTargetProducts +} BURN_CHAINED_PATCH; + +typedef struct _BURN_SLIPSTREAM_MSP +{ + BURN_PACKAGE* pMspPackage; + DWORD dwMsiChainedPatchIndex; // index into the Msi.rgChainedPatches +} BURN_SLIPSTREAM_MSP; + typedef struct _BURN_PACKAGE_PAYLOAD { BURN_PAYLOAD* pPayload; @@ -295,9 +311,12 @@ typedef struct _BURN_PACKAGE BURN_RELATED_MSI* rgRelatedMsis; DWORD cRelatedMsis; - _BURN_PACKAGE** rgpSlipstreamMspPackages; + BURN_SLIPSTREAM_MSP* rgSlipstreamMsps; LPWSTR* rgsczSlipstreamMspPackageIds; DWORD cSlipstreamMspPackages; + + BURN_CHAINED_PATCH* rgChainedPatches; + DWORD cChainedPatches; } Msi; struct { diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 95ea0b05..86a07dfb 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -2168,7 +2168,7 @@ static HRESULT AddCacheSlipstreamMsps( for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) { - BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i]; + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[i].pMspPackage; AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); hr = AddCachePackageHelper(pPlan, pMspPackage, &hIgnored); @@ -2791,7 +2791,7 @@ static HRESULT FinalizeSlipstreamPatchActions( { for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) { - BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[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; diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index 08dc68e7..65f99168 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -71,6 +71,7 @@ + diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index 42c11968..820f88c2 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -11,6 +11,7 @@ static HRESULT WINAPI PlanTestBAProc( static LPCWSTR wzMsiTransactionManifestFileName = L"MsiTransaction_BundleAv1_manifest.xml"; static LPCWSTR wzSingleMsiManifestFileName = L"BasicFunctionality_BundleA_manifest.xml"; +static LPCWSTR wzSlipstreamManifestFileName = L"Slipstream_BundleA_manifest.xml"; namespace Microsoft { @@ -650,6 +651,210 @@ namespace Bootstrapper ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_IGNORED, BURN_PACKAGE_REGISTRATION_STATE_IGNORED); } + [Fact] + void SlipstreamInstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSlipstreamManifestFileName, pEngineState); + DetectPermanentPackagesAsPresentAndCached(pEngineState); + PlanTestDetectPatchInitialize(pEngineState); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + DWORD dwPackageStart = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PatchA", 4, 1, 20480, FALSE); + ValidateCacheAcquirePayload(pPlan, fRollback, dwIndex++, L"PatchA", L"PatchA", FALSE); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PatchA", L"PatchA", TRUE, FALSE, dwPackageStart); + ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PatchA", FALSE); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 2); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 10, 1, 32768, FALSE); + ValidateCacheAcquirePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", FALSE); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); + ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 2); + ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(3055111ull, pPlan->qwEstimatedSize); + Assert::Equal(53248ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 3; + BURN_EXECUTE_ACTION* pExecuteAction = NULL; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[11].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[5].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_REGISTER); + pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_INSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); + ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 3; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PatchA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); + ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(2ul, pPlan->cExecutePackagesTotal); + Assert::Equal(4ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + 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); + } + + [Fact] + void SlipstreamUninstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSlipstreamManifestFileName, pEngineState); + DetectPackagesAsPresentAndCached(pEngineState); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(0ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 1; + BURN_EXECUTE_ACTION* pExecuteAction = NULL; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_UNREGISTER); + pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); + ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_INSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); + ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(2ul, pPlan->cExecutePackagesTotal); + Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + ValidateCleanAction(pPlan, dwIndex++, L"PatchA"); + ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{0A5113E3-06A5-4CE0-8E83-9EB42F6764A6}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(3ul, pEngineState->packages.cPackages); + ValidatePermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"NetFx48Web"); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PatchA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + } + private: // This doesn't initialize everything, just enough for CorePlan to work. void InitializeEngineStateForCorePlan(LPCWSTR wzManifestFileName, BURN_ENGINE_STATE* pEngineState) @@ -700,6 +905,30 @@ namespace Bootstrapper pEngineState->fDetected = TRUE; } + void PlanTestDetectPatchInitialize(BURN_ENGINE_STATE* pEngineState) + { + HRESULT hr = MsiEngineDetectInitialize(&pEngineState->packages); + NativeAssert::Succeeded(hr, "MsiEngineDetectInitialize failed"); + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j; + + if (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN == pTargetProduct->patchPackageState) + { + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + } + } + } + } + } + void DetectAttachedContainerAsAttached(BURN_ENGINE_STATE* pEngineState) { for (DWORD i = 0; i < pEngineState->containers.cContainers; ++i) @@ -773,6 +1002,21 @@ namespace Bootstrapper BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; DetectPackageAsPresentAndCached(pPackage); DetectPackageDependent(pPackage, pEngineState->registration.sczId); + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) + { + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; + + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[j].pMspPackage; + MspEngineAddDetectedTargetProduct(&pEngineState->packages, pMspPackage, j, pPackage->Msi.sczProductCode, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED); + + BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + (pMspPackage->Msp.cTargetProductCodes - 1); + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; + pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + } } } @@ -862,7 +1106,23 @@ namespace Bootstrapper { BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); Assert::Equal(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pAction->type); - NativeAssert::StringEqual(wzContainerId, pAction->extractContainer.pContainer->sczId); + NativeAssert::StringEqual(wzContainerId, pAction->resolveContainer.pContainer->sczId); + Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); + } + + void ValidateCacheAcquirePayload( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId, + __in LPCWSTR wzPayloadId, + __in BOOL fSkipUntilRetried + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->resolvePayload.pPackage->sczId); + NativeAssert::StringEqual(wzPayloadId, pAction->resolvePayload.pPayload->sczKey); Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); } @@ -1063,7 +1323,7 @@ namespace Bootstrapper __in BURN_PLAN* pPlan, __in BOOL fRollback, __in DWORD dwIndex, - __in LPCWSTR wzPackageId, + __in_z LPCWSTR wzPackageId, __in BOOTSTRAPPER_ACTION_STATE action, __in BURN_MSI_PROPERTY actionMsiProperty, __in DWORD uiLevel, @@ -1083,6 +1343,45 @@ namespace Bootstrapper Assert::Equal(FALSE, pAction->fDeleted); } + BURN_EXECUTE_ACTION* ValidateDeletedExecuteMspTarget( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in_z LPCWSTR wzPackageId, + __in BOOTSTRAPPER_ACTION_STATE action, + __in_z LPCWSTR wzTargetProductCode, + __in BOOL fPerMachineTarget, + __in BURN_MSI_PROPERTY actionMsiProperty, + __in DWORD uiLevel, + __in BOOL fDisableExternalUiHandler, + __in BOOL fDeleted + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_MSP_TARGET, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->mspTarget.pPackage->sczId); + Assert::Equal(action, pAction->mspTarget.action); + NativeAssert::StringEqual(wzTargetProductCode, pAction->mspTarget.sczTargetProductCode); + Assert::Equal(fPerMachineTarget, pAction->mspTarget.fPerMachineTarget); + Assert::Equal(actionMsiProperty, pAction->mspTarget.actionMsiProperty); + Assert::Equal(uiLevel, pAction->mspTarget.uiLevel); + Assert::Equal(fDisableExternalUiHandler, pAction->mspTarget.fDisableExternalUiHandler); + NativeAssert::NotNull(pAction->mspTarget.sczLogPath); + Assert::Equal(fDeleted, pAction->fDeleted); + return pAction; + } + + void ValidateExecuteMspTargetPatch( + __in BURN_EXECUTE_ACTION* pAction, + __in DWORD dwIndex, + __in_z LPCWSTR wzPackageId + ) + { + Assert::InRange(dwIndex + 1ul, 1ul, pAction->mspTarget.cOrderedPatches); + BURN_ORDERED_PATCHES* pOrderedPatch = pAction->mspTarget.rgOrderedPatches + dwIndex; + NativeAssert::StringEqual(wzPackageId, pOrderedPatch->pPackage->sczId); + } + void ValidateExecutePackageDependency( __in BURN_PLAN* pPlan, __in BOOL fRollback, diff --git a/src/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml b/src/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml new file mode 100644 index 00000000..4b5cab6f --- /dev/null +++ b/src/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml @@ -0,0 +1 @@ + \ No newline at end of file -- cgit v1.2.3-55-g6feb