diff options
| author | Sean Hall <r.sean.hall@gmail.com> | 2022-08-03 14:55:23 -0500 |
|---|---|---|
| committer | Sean Hall <r.sean.hall@gmail.com> | 2022-08-03 15:57:24 -0500 |
| commit | 124fef398a26bc8e139e889a2345602d2478590c (patch) | |
| tree | 002c77e0c1d72b0cc0e46bed3c6f02d4179625fe /src | |
| parent | a896fec453056aa5e1ad803b04a672d2dceda981 (diff) | |
| download | wix-124fef398a26bc8e139e889a2345602d2478590c.tar.gz wix-124fef398a26bc8e139e889a2345602d2478590c.tar.bz2 wix-124fef398a26bc8e139e889a2345602d2478590c.zip | |
Add ability to skip a local path candidate if it failed verification.
Fixes 6818
Diffstat (limited to 'src')
| -rw-r--r-- | src/burn/engine/apply.cpp | 137 | ||||
| -rw-r--r-- | src/burn/engine/cache.cpp | 70 | ||||
| -rw-r--r-- | src/burn/engine/cache.h | 5 | ||||
| -rw-r--r-- | src/burn/engine/container.cpp | 1 | ||||
| -rw-r--r-- | src/burn/engine/container.h | 4 | ||||
| -rw-r--r-- | src/burn/engine/elevation.cpp | 23 | ||||
| -rw-r--r-- | src/burn/engine/payload.cpp | 1 | ||||
| -rw-r--r-- | src/burn/engine/payload.h | 3 | ||||
| -rw-r--r-- | src/burn/engine/plan.cpp | 6 | ||||
| -rw-r--r-- | src/test/burn/TestBA/TestBA.cs | 51 | ||||
| -rw-r--r-- | src/test/burn/TestData/LayoutTests/BundleB/BundleB.wixproj | 17 | ||||
| -rw-r--r-- | src/test/burn/TestData/LayoutTests/BundleB/BundleB.wxs | 10 | ||||
| -rw-r--r-- | src/test/burn/WixToolsetTest.BurnE2E/LayoutTests.cs | 93 | ||||
| -rw-r--r-- | src/test/burn/WixToolsetTest.BurnE2E/Utilities/TestBAController.cs | 53 |
14 files changed, 444 insertions, 30 deletions
diff --git a/src/burn/engine/apply.cpp b/src/burn/engine/apply.cpp index dd99a5cd..31d756a8 100644 --- a/src/burn/engine/apply.cpp +++ b/src/burn/engine/apply.cpp | |||
| @@ -38,7 +38,7 @@ typedef struct _BURN_CACHE_CONTEXT | |||
| 38 | LPWSTR* rgSearchPaths; | 38 | LPWSTR* rgSearchPaths; |
| 39 | DWORD cSearchPaths; | 39 | DWORD cSearchPaths; |
| 40 | DWORD cSearchPathsMax; | 40 | DWORD cSearchPathsMax; |
| 41 | LPWSTR sczLastUsedFolderCandidate; | 41 | LPWSTR sczLocalAcquisitionSourcePath; |
| 42 | } BURN_CACHE_CONTEXT; | 42 | } BURN_CACHE_CONTEXT; |
| 43 | 43 | ||
| 44 | typedef struct _BURN_CACHE_PROGRESS_CONTEXT | 44 | typedef struct _BURN_CACHE_PROGRESS_CONTEXT |
| @@ -92,6 +92,16 @@ static HRESULT ApplyCachePackage( | |||
| 92 | __in BURN_CACHE_CONTEXT* pContext, | 92 | __in BURN_CACHE_CONTEXT* pContext, |
| 93 | __in BURN_PACKAGE* pPackage | 93 | __in BURN_PACKAGE* pPackage |
| 94 | ); | 94 | ); |
| 95 | static void FinalizeContainerAcquisition( | ||
| 96 | __in BURN_CACHE_CONTEXT* pContext, | ||
| 97 | __in BURN_CONTAINER* pContainer, | ||
| 98 | __in BOOL fSuccess | ||
| 99 | ); | ||
| 100 | static void FinalizePayloadAcquisition( | ||
| 101 | __in BURN_CACHE_CONTEXT* pContext, | ||
| 102 | __in BURN_PAYLOAD* pPayload, | ||
| 103 | __in BOOL fSuccess | ||
| 104 | ); | ||
| 95 | static HRESULT ApplyExtractContainer( | 105 | static HRESULT ApplyExtractContainer( |
| 96 | __in BURN_CACHE_CONTEXT* pContext, | 106 | __in BURN_CACHE_CONTEXT* pContext, |
| 97 | __in BURN_CONTAINER* pContainer | 107 | __in BURN_CONTAINER* pContainer |
| @@ -667,7 +677,7 @@ LExit: | |||
| 667 | ReleaseNullStr(cacheContext.rgSearchPaths[i]); | 677 | ReleaseNullStr(cacheContext.rgSearchPaths[i]); |
| 668 | } | 678 | } |
| 669 | ReleaseMem(cacheContext.rgSearchPaths); | 679 | ReleaseMem(cacheContext.rgSearchPaths); |
| 670 | ReleaseStr(cacheContext.sczLastUsedFolderCandidate); | 680 | ReleaseStr(cacheContext.sczLocalAcquisitionSourcePath); |
| 671 | 681 | ||
| 672 | UserExperienceOnCacheComplete(pUX, hr); | 682 | UserExperienceOnCacheComplete(pUX, hr); |
| 673 | return hr; | 683 | return hr; |
| @@ -1038,6 +1048,46 @@ LExit: | |||
| 1038 | return hr; | 1048 | return hr; |
| 1039 | } | 1049 | } |
| 1040 | 1050 | ||
| 1051 | static void FinalizeContainerAcquisition( | ||
| 1052 | __in BURN_CACHE_CONTEXT* pContext, | ||
| 1053 | __in BURN_CONTAINER* pContainer, | ||
| 1054 | __in BOOL fSuccess | ||
| 1055 | ) | ||
| 1056 | { | ||
| 1057 | ReleaseNullStr(pContainer->sczFailedLocalAcquisitionPath); | ||
| 1058 | |||
| 1059 | if (fSuccess) | ||
| 1060 | { | ||
| 1061 | ReleaseNullStr(pContext->sczLocalAcquisitionSourcePath); | ||
| 1062 | pContainer->fFailedVerificationFromAcquisition = FALSE; | ||
| 1063 | } | ||
| 1064 | else if (pContext->sczLocalAcquisitionSourcePath) | ||
| 1065 | { | ||
| 1066 | pContainer->sczFailedLocalAcquisitionPath = pContext->sczLocalAcquisitionSourcePath; | ||
| 1067 | pContext->sczLocalAcquisitionSourcePath = NULL; | ||
| 1068 | } | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | static void FinalizePayloadAcquisition( | ||
| 1072 | __in BURN_CACHE_CONTEXT* pContext, | ||
| 1073 | __in BURN_PAYLOAD* pPayload, | ||
| 1074 | __in BOOL fSuccess | ||
| 1075 | ) | ||
| 1076 | { | ||
| 1077 | ReleaseNullStr(pPayload->sczFailedLocalAcquisitionPath); | ||
| 1078 | |||
| 1079 | if (fSuccess) | ||
| 1080 | { | ||
| 1081 | ReleaseNullStr(pContext->sczLocalAcquisitionSourcePath); | ||
| 1082 | pPayload->fFailedVerificationFromAcquisition = FALSE; | ||
| 1083 | } | ||
| 1084 | else if (pContext->sczLocalAcquisitionSourcePath) | ||
| 1085 | { | ||
| 1086 | pPayload->sczFailedLocalAcquisitionPath = pContext->sczLocalAcquisitionSourcePath; | ||
| 1087 | pContext->sczLocalAcquisitionSourcePath = NULL; | ||
| 1088 | } | ||
| 1089 | } | ||
| 1090 | |||
| 1041 | static HRESULT ApplyExtractContainer( | 1091 | static HRESULT ApplyExtractContainer( |
| 1042 | __in BURN_CACHE_CONTEXT* pContext, | 1092 | __in BURN_CACHE_CONTEXT* pContext, |
| 1043 | __in BURN_CONTAINER* pContainer | 1093 | __in BURN_CONTAINER* pContainer |
| @@ -1066,10 +1116,11 @@ static HRESULT ApplyExtractContainer( | |||
| 1066 | hr = ExtractContainer(pContext, pContainer); | 1116 | hr = ExtractContainer(pContext, pContainer); |
| 1067 | LogExitOnFailure(hr, MSG_FAILED_EXTRACT_CONTAINER, "Failed to extract payloads from container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); | 1117 | LogExitOnFailure(hr, MSG_FAILED_EXTRACT_CONTAINER, "Failed to extract payloads from container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); |
| 1068 | 1118 | ||
| 1069 | if (pContext->sczLastUsedFolderCandidate) | 1119 | if (pContext->sczLocalAcquisitionSourcePath) |
| 1070 | { | 1120 | { |
| 1071 | // We successfully copied from a source location, set that as the last used source. | 1121 | // We successfully copied from a source location, set that as the last used source. |
| 1072 | CacheSetLastUsedSource(pContext->pVariables, pContext->sczLastUsedFolderCandidate, pContainer->sczFilePath); | 1122 | CacheSetLastUsedSource(pContext->pVariables, pContext->sczLocalAcquisitionSourcePath, pContainer->sczFilePath); |
| 1123 | ReleaseNullStr(pContext->sczLocalAcquisitionSourcePath); | ||
| 1073 | } | 1124 | } |
| 1074 | 1125 | ||
| 1075 | if (pContainer->qwExtractSizeTotal < pContainer->qwCommittedExtractProgress) | 1126 | if (pContainer->qwExtractSizeTotal < pContainer->qwCommittedExtractProgress) |
| @@ -1086,7 +1137,7 @@ static HRESULT ApplyExtractContainer( | |||
| 1086 | pContainer->qwCommittedExtractProgress = pContainer->qwExtractSizeTotal; | 1137 | pContainer->qwCommittedExtractProgress = pContainer->qwExtractSizeTotal; |
| 1087 | 1138 | ||
| 1088 | LExit: | 1139 | LExit: |
| 1089 | ReleaseNullStr(pContext->sczLastUsedFolderCandidate); | 1140 | FinalizeContainerAcquisition(pContext, pContainer, SUCCEEDED(hr)); |
| 1090 | 1141 | ||
| 1091 | return hr; | 1142 | return hr; |
| 1092 | } | 1143 | } |
| @@ -1157,13 +1208,13 @@ static HRESULT ApplyLayoutContainer( | |||
| 1157 | ++cTryAgainAttempts; | 1208 | ++cTryAgainAttempts; |
| 1158 | pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedCacheProgress; | 1209 | pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedCacheProgress; |
| 1159 | pContainer->qwCommittedCacheProgress = 0; | 1210 | pContainer->qwCommittedCacheProgress = 0; |
| 1160 | ReleaseNullStr(pContext->sczLastUsedFolderCandidate); | 1211 | FinalizeContainerAcquisition(pContext, pContainer, FALSE); |
| 1161 | LogErrorId(hr, MSG_CACHE_RETRYING_CONTAINER, pContainer->sczId, NULL, NULL); | 1212 | LogErrorId(hr, MSG_CACHE_RETRYING_CONTAINER, pContainer->sczId, NULL, NULL); |
| 1162 | } | 1213 | } |
| 1163 | } | 1214 | } |
| 1164 | 1215 | ||
| 1165 | LExit: | 1216 | LExit: |
| 1166 | ReleaseNullStr(pContext->sczLastUsedFolderCandidate); | 1217 | FinalizeContainerAcquisition(pContext, pContainer, SUCCEEDED(hr)); |
| 1167 | 1218 | ||
| 1168 | return hr; | 1219 | return hr; |
| 1169 | } | 1220 | } |
| @@ -1230,13 +1281,13 @@ static HRESULT ApplyProcessPayload( | |||
| 1230 | ++cTryAgainAttempts; | 1281 | ++cTryAgainAttempts; |
| 1231 | pContext->qwSuccessfulCacheProgress -= pPayloadGroupItem->qwCommittedCacheProgress; | 1282 | pContext->qwSuccessfulCacheProgress -= pPayloadGroupItem->qwCommittedCacheProgress; |
| 1232 | pPayloadGroupItem->qwCommittedCacheProgress = 0; | 1283 | pPayloadGroupItem->qwCommittedCacheProgress = 0; |
| 1233 | ReleaseNullStr(pContext->sczLastUsedFolderCandidate); | 1284 | FinalizePayloadAcquisition(pContext, pPayload, FALSE); |
| 1234 | LogErrorId(hr, MSG_CACHE_RETRYING_PAYLOAD, pPayload->sczKey, NULL, NULL); | 1285 | LogErrorId(hr, MSG_CACHE_RETRYING_PAYLOAD, pPayload->sczKey, NULL, NULL); |
| 1235 | } | 1286 | } |
| 1236 | } | 1287 | } |
| 1237 | 1288 | ||
| 1238 | LExit: | 1289 | LExit: |
| 1239 | ReleaseNullStr(pContext->sczLastUsedFolderCandidate); | 1290 | FinalizePayloadAcquisition(pContext, pPayload, SUCCEEDED(hr)); |
| 1240 | 1291 | ||
| 1241 | return hr; | 1292 | return hr; |
| 1242 | } | 1293 | } |
| @@ -1570,6 +1621,7 @@ static HRESULT AcquireContainerOrPayload( | |||
| 1570 | BOOL fPreferExtract = FALSE; | 1621 | BOOL fPreferExtract = FALSE; |
| 1571 | DWORD64 qwFileSize = 0; | 1622 | DWORD64 qwFileSize = 0; |
| 1572 | BOOL fMinimumFileSize = FALSE; | 1623 | BOOL fMinimumFileSize = FALSE; |
| 1624 | BOOL fEqual = FALSE; | ||
| 1573 | 1625 | ||
| 1574 | if (pContainer) | 1626 | if (pContainer) |
| 1575 | { | 1627 | { |
| @@ -1615,21 +1667,44 @@ static HRESULT AcquireContainerOrPayload( | |||
| 1615 | // When a payload comes from a container, the container has the highest chance of being correct. | 1667 | // When a payload comes from a container, the container has the highest chance of being correct. |
| 1616 | // But we want to avoid extracting the container multiple times. | 1668 | // But we want to avoid extracting the container multiple times. |
| 1617 | // So only consider the destination path, which means the container was already extracted. | 1669 | // So only consider the destination path, which means the container was already extracted. |
| 1618 | if (IsValidLocalFile(pContext->rgSearchPaths[dwDestinationSearchPath], qwFileSize, fMinimumFileSize)) | 1670 | if (!pPayload->fFailedVerificationFromAcquisition && IsValidLocalFile(pContext->rgSearchPaths[dwDestinationSearchPath], qwFileSize, fMinimumFileSize)) |
| 1619 | { | 1671 | { |
| 1620 | fFoundLocal = TRUE; | 1672 | fFoundLocal = TRUE; |
| 1621 | dwChosenSearchPath = dwDestinationSearchPath; | 1673 | dwChosenSearchPath = dwDestinationSearchPath; |
| 1622 | } | 1674 | } |
| 1623 | else // don't prefer the container if extracting it already failed. | 1675 | else // don't prefer the container if it was already extracted. |
| 1624 | { | 1676 | { |
| 1625 | fPreferExtract = SUCCEEDED(pPayload->pContainer->hrExtract); | 1677 | fPreferExtract = !pPayload->pContainer->fExtracted; |
| 1626 | } | 1678 | } |
| 1627 | } | 1679 | } |
| 1628 | 1680 | ||
| 1629 | if (!fFoundLocal) | 1681 | if (!fFoundLocal) |
| 1630 | { | 1682 | { |
| 1683 | BOOL fFailedVerificationFromAcquisition = pContainer ? pContainer->fFailedVerificationFromAcquisition : pPayload->fFailedVerificationFromAcquisition; | ||
| 1684 | LPCWSTR wzFailedLocalAcquisitionPath = pContainer ? pContainer->sczFailedLocalAcquisitionPath : pPayload->sczFailedLocalAcquisitionPath; | ||
| 1685 | |||
| 1631 | for (DWORD i = 0; i < pContext->cSearchPaths; ++i) | 1686 | for (DWORD i = 0; i < pContext->cSearchPaths; ++i) |
| 1632 | { | 1687 | { |
| 1688 | // If the file failed verification from acquisition then certain paths should not be considered. | ||
| 1689 | if (fFailedVerificationFromAcquisition) | ||
| 1690 | { | ||
| 1691 | if (i == dwDestinationSearchPath) | ||
| 1692 | { | ||
| 1693 | continue; | ||
| 1694 | } | ||
| 1695 | |||
| 1696 | if (wzFailedLocalAcquisitionPath) | ||
| 1697 | { | ||
| 1698 | hr = PathCompareCanonicalized(pContext->rgSearchPaths[i], wzFailedLocalAcquisitionPath, &fEqual); | ||
| 1699 | ExitOnFailure(hr, "Failed to compare '%ls' to '%ls'.", pContext->rgSearchPaths[i], wzFailedLocalAcquisitionPath); | ||
| 1700 | |||
| 1701 | if (fEqual) | ||
| 1702 | { | ||
| 1703 | continue; | ||
| 1704 | } | ||
| 1705 | } | ||
| 1706 | } | ||
| 1707 | |||
| 1633 | // If the file exists locally with the correct size, choose it. | 1708 | // If the file exists locally with the correct size, choose it. |
| 1634 | if (IsValidLocalFile(pContext->rgSearchPaths[i], qwFileSize, fMinimumFileSize)) | 1709 | if (IsValidLocalFile(pContext->rgSearchPaths[i], qwFileSize, fMinimumFileSize)) |
| 1635 | { | 1710 | { |
| @@ -1703,7 +1778,7 @@ static HRESULT AcquireContainerOrPayload( | |||
| 1703 | ExitOnFailure(hr, "Failed to copy payload: %ls", wzPayloadId); | 1778 | ExitOnFailure(hr, "Failed to copy payload: %ls", wzPayloadId); |
| 1704 | 1779 | ||
| 1705 | // Store the source path so it can be used as the LastUsedFolder if it passes verification. | 1780 | // Store the source path so it can be used as the LastUsedFolder if it passes verification. |
| 1706 | pContext->sczLastUsedFolderCandidate = pContext->rgSearchPaths[dwChosenSearchPath]; | 1781 | pContext->sczLocalAcquisitionSourcePath = pContext->rgSearchPaths[dwChosenSearchPath]; |
| 1707 | pContext->rgSearchPaths[dwChosenSearchPath] = NULL; | 1782 | pContext->rgSearchPaths[dwChosenSearchPath] = NULL; |
| 1708 | } | 1783 | } |
| 1709 | 1784 | ||
| @@ -1731,12 +1806,14 @@ static HRESULT AcquireContainerOrPayload( | |||
| 1731 | LExit: | 1806 | LExit: |
| 1732 | if (BOOTSTRAPPER_CACHE_OPERATION_EXTRACT == cacheOperation) | 1807 | if (BOOTSTRAPPER_CACHE_OPERATION_EXTRACT == cacheOperation) |
| 1733 | { | 1808 | { |
| 1734 | if (FAILED(hr) && SUCCEEDED(pPayload->pContainer->hrExtract) && | 1809 | // If this was the first extraction attempt and it failed |
| 1810 | // but there was a different method of acquisition available then recommend retrying. | ||
| 1811 | if (FAILED(hr) && !pPayload->pContainer->fExtracted && | ||
| 1735 | (fFoundLocal || pPayload->downloadSource.sczUrl && *pPayload->downloadSource.sczUrl)) | 1812 | (fFoundLocal || pPayload->downloadSource.sczUrl && *pPayload->downloadSource.sczUrl)) |
| 1736 | { | 1813 | { |
| 1737 | *pfRetry = TRUE; | 1814 | *pfRetry = TRUE; |
| 1738 | } | 1815 | } |
| 1739 | pPayload->pContainer->hrExtract = hr; | 1816 | pPayload->pContainer->fExtracted = TRUE; |
| 1740 | } | 1817 | } |
| 1741 | UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, pfRetry); | 1818 | UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, pfRetry); |
| 1742 | 1819 | ||
| @@ -2103,6 +2180,31 @@ static HRESULT CALLBACK CacheMessageHandler( | |||
| 2103 | hr = UserExperienceOnCacheContainerOrPayloadVerifyComplete(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, hr); | 2180 | hr = UserExperienceOnCacheContainerOrPayloadVerifyComplete(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, hr); |
| 2104 | break; | 2181 | break; |
| 2105 | } | 2182 | } |
| 2183 | case BURN_CACHE_MESSAGE_FAILURE: | ||
| 2184 | switch (pMessage->failure.cacheStep) | ||
| 2185 | { | ||
| 2186 | case BURN_CACHE_STEP_HASH: | ||
| 2187 | if (pProgress->pContainer) | ||
| 2188 | { | ||
| 2189 | LogStringLine(REPORT_DEBUG, "Verification failed on container: %ls", pProgress->pContainer->sczId); | ||
| 2190 | pProgress->pContainer->fFailedVerificationFromAcquisition = TRUE; | ||
| 2191 | } | ||
| 2192 | else if (pProgress->pPayloadGroupItem) | ||
| 2193 | { | ||
| 2194 | LogStringLine(REPORT_DEBUG, "Verification failed on payload group item: %ls", pProgress->pPayloadGroupItem->pPayload->sczKey); | ||
| 2195 | pProgress->pPayloadGroupItem->pPayload->fFailedVerificationFromAcquisition = TRUE; | ||
| 2196 | } | ||
| 2197 | else if (pProgress->pPayload) | ||
| 2198 | { | ||
| 2199 | LogStringLine(REPORT_DEBUG, "Verification failed on payload: %ls", pProgress->pPayload->sczKey); | ||
| 2200 | pProgress->pPayload->fFailedVerificationFromAcquisition = TRUE; | ||
| 2201 | } | ||
| 2202 | else | ||
| 2203 | { | ||
| 2204 | LogStringLine(REPORT_DEBUG, "Verification failed on unknown item"); | ||
| 2205 | } | ||
| 2206 | break; | ||
| 2207 | } | ||
| 2106 | } | 2208 | } |
| 2107 | 2209 | ||
| 2108 | return hr; | 2210 | return hr; |
| @@ -2159,10 +2261,11 @@ static HRESULT CompleteCacheProgress( | |||
| 2159 | pContext->pPayloadGroupItem->qwCommittedCacheProgress += qwFileSize; | 2261 | pContext->pPayloadGroupItem->qwCommittedCacheProgress += qwFileSize; |
| 2160 | } | 2262 | } |
| 2161 | 2263 | ||
| 2162 | if (BURN_CACHE_PROGRESS_TYPE_FINALIZE == pContext->type && pContext->pCacheContext->sczLastUsedFolderCandidate) | 2264 | if (BURN_CACHE_PROGRESS_TYPE_FINALIZE == pContext->type && pContext->pCacheContext->sczLocalAcquisitionSourcePath) |
| 2163 | { | 2265 | { |
| 2164 | // We successfully copied from a source location, set that as the last used source. | 2266 | // We successfully copied from a source location, set that as the last used source. |
| 2165 | CacheSetLastUsedSource(pContext->pCacheContext->pVariables, pContext->pCacheContext->sczLastUsedFolderCandidate, pContext->pContainer ? pContext->pContainer->sczFilePath : pContext->pPayloadGroupItem->pPayload->sczFilePath); | 2267 | CacheSetLastUsedSource(pContext->pCacheContext->pVariables, pContext->pCacheContext->sczLocalAcquisitionSourcePath, pContext->pContainer ? pContext->pContainer->sczFilePath : pContext->pPayloadGroupItem->pPayload->sczFilePath); |
| 2268 | ReleaseNullStr(pContext->pCacheContext->sczLocalAcquisitionSourcePath); | ||
| 2166 | } | 2269 | } |
| 2167 | } | 2270 | } |
| 2168 | else if (PROGRESS_CANCEL == dwResult) | 2271 | else if (PROGRESS_CANCEL == dwResult) |
diff --git a/src/burn/engine/cache.cpp b/src/burn/engine/cache.cpp index eb5cc508..a23ce9ed 100644 --- a/src/burn/engine/cache.cpp +++ b/src/burn/engine/cache.cpp | |||
| @@ -12,7 +12,11 @@ static const DWORD FILE_OPERATION_RETRY_WAIT = 2000; | |||
| 12 | static HRESULT CacheVerifyPayloadSignature( | 12 | static HRESULT CacheVerifyPayloadSignature( |
| 13 | __in BURN_PAYLOAD* pPayload, | 13 | __in BURN_PAYLOAD* pPayload, |
| 14 | __in_z LPCWSTR wzUnverifiedPayloadPath, | 14 | __in_z LPCWSTR wzUnverifiedPayloadPath, |
| 15 | __in HANDLE hFile | 15 | __in HANDLE hFile, |
| 16 | __in BURN_CACHE_STEP cacheStep, | ||
| 17 | __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, | ||
| 18 | __in LPPROGRESS_ROUTINE pfnProgress, | ||
| 19 | __in LPVOID pContext | ||
| 16 | ); | 20 | ); |
| 17 | static HRESULT CalculatePotentialBaseWorkingFolders( | 21 | static HRESULT CalculatePotentialBaseWorkingFolders( |
| 18 | __in BURN_CACHE* pCache, | 22 | __in BURN_CACHE* pCache, |
| @@ -159,7 +163,11 @@ static HRESULT SendCacheCompleteMessage( | |||
| 159 | __in LPVOID pContext, | 163 | __in LPVOID pContext, |
| 160 | __in HRESULT hrStatus | 164 | __in HRESULT hrStatus |
| 161 | ); | 165 | ); |
| 162 | 166 | static HRESULT SendCacheFailureMessage( | |
| 167 | __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, | ||
| 168 | __in LPVOID pContext, | ||
| 169 | __in BURN_CACHE_STEP cacheStep | ||
| 170 | ); | ||
| 163 | 171 | ||
| 164 | extern "C" HRESULT CacheInitialize( | 172 | extern "C" HRESULT CacheInitialize( |
| 165 | __in BURN_CACHE* pCache, | 173 | __in BURN_CACHE* pCache, |
| @@ -1254,11 +1262,16 @@ LExit: | |||
| 1254 | static HRESULT CacheVerifyPayloadSignature( | 1262 | static HRESULT CacheVerifyPayloadSignature( |
| 1255 | __in BURN_PAYLOAD* pPayload, | 1263 | __in BURN_PAYLOAD* pPayload, |
| 1256 | __in_z LPCWSTR wzUnverifiedPayloadPath, | 1264 | __in_z LPCWSTR wzUnverifiedPayloadPath, |
| 1257 | __in HANDLE hFile | 1265 | __in HANDLE hFile, |
| 1266 | __in BURN_CACHE_STEP cacheStep, | ||
| 1267 | __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, | ||
| 1268 | __in LPPROGRESS_ROUTINE /*pfnProgress*/, | ||
| 1269 | __in LPVOID pContext | ||
| 1258 | ) | 1270 | ) |
| 1259 | { | 1271 | { |
| 1260 | HRESULT hr = S_OK; | 1272 | HRESULT hr = S_OK; |
| 1261 | LONG er = ERROR_SUCCESS; | 1273 | LONG er = ERROR_SUCCESS; |
| 1274 | BOOL fFailedVerification = FALSE; | ||
| 1262 | 1275 | ||
| 1263 | GUID guidAuthenticode = WINTRUST_ACTION_GENERIC_VERIFY_V2; | 1276 | GUID guidAuthenticode = WINTRUST_ACTION_GENERIC_VERIFY_V2; |
| 1264 | WINTRUST_FILE_INFO wfi = { }; | 1277 | WINTRUST_FILE_INFO wfi = { }; |
| @@ -1266,6 +1279,11 @@ static HRESULT CacheVerifyPayloadSignature( | |||
| 1266 | CRYPT_PROVIDER_DATA* pProviderData = NULL; | 1279 | CRYPT_PROVIDER_DATA* pProviderData = NULL; |
| 1267 | CRYPT_PROVIDER_SGNR* pSigner = NULL; | 1280 | CRYPT_PROVIDER_SGNR* pSigner = NULL; |
| 1268 | 1281 | ||
| 1282 | hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep); | ||
| 1283 | ExitOnFailure(hr, "Aborted cache verify payload signature begin."); | ||
| 1284 | |||
| 1285 | fFailedVerification = TRUE; | ||
| 1286 | |||
| 1269 | // Verify the payload assuming online. | 1287 | // Verify the payload assuming online. |
| 1270 | wfi.cbStruct = sizeof(wfi); | 1288 | wfi.cbStruct = sizeof(wfi); |
| 1271 | wfi.pcwszFilePath = wzUnverifiedPayloadPath; | 1289 | wfi.pcwszFilePath = wzUnverifiedPayloadPath; |
| @@ -1297,7 +1315,19 @@ static HRESULT CacheVerifyPayloadSignature( | |||
| 1297 | hr = VerifyPayloadAgainstCertChain(pPayload, pSigner->pChainContext); | 1315 | hr = VerifyPayloadAgainstCertChain(pPayload, pSigner->pChainContext); |
| 1298 | ExitOnFailure(hr, "Failed to verify expected payload against actual certificate chain."); | 1316 | ExitOnFailure(hr, "Failed to verify expected payload against actual certificate chain."); |
| 1299 | 1317 | ||
| 1318 | fFailedVerification = FALSE; | ||
| 1319 | |||
| 1320 | hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, pPayload->qwFileSize); | ||
| 1321 | |||
| 1300 | LExit: | 1322 | LExit: |
| 1323 | if (fFailedVerification) | ||
| 1324 | { | ||
| 1325 | // Make sure the BA process marks this payload as having failed verification. | ||
| 1326 | SendCacheFailureMessage(pfnCacheMessageHandler, pContext, cacheStep); | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr); | ||
| 1330 | |||
| 1301 | return hr; | 1331 | return hr; |
| 1302 | } | 1332 | } |
| 1303 | 1333 | ||
| @@ -1744,7 +1774,7 @@ static HRESULT VerifyThenTransferPayload( | |||
| 1744 | switch (pPayload->verification) | 1774 | switch (pPayload->verification) |
| 1745 | { | 1775 | { |
| 1746 | case BURN_PAYLOAD_VERIFICATION_AUTHENTICODE: | 1776 | case BURN_PAYLOAD_VERIFICATION_AUTHENTICODE: |
| 1747 | hr = CacheVerifyPayloadSignature(pPayload, wzUnverifiedPayloadPath, hFile); | 1777 | hr = CacheVerifyPayloadSignature(pPayload, wzUnverifiedPayloadPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext); |
| 1748 | ExitOnFailure(hr, "Failed to verify payload signature: %ls", wzCachedPath); | 1778 | ExitOnFailure(hr, "Failed to verify payload signature: %ls", wzCachedPath); |
| 1749 | break; | 1779 | break; |
| 1750 | case BURN_PAYLOAD_VERIFICATION_HASH: | 1780 | case BURN_PAYLOAD_VERIFICATION_HASH: |
| @@ -1890,7 +1920,7 @@ static HRESULT VerifyFileAgainstPayload( | |||
| 1890 | switch (pPayload->verification) | 1920 | switch (pPayload->verification) |
| 1891 | { | 1921 | { |
| 1892 | case BURN_PAYLOAD_VERIFICATION_AUTHENTICODE: | 1922 | case BURN_PAYLOAD_VERIFICATION_AUTHENTICODE: |
| 1893 | hr = CacheVerifyPayloadSignature(pPayload, wzVerifyPath, hFile); | 1923 | hr = CacheVerifyPayloadSignature(pPayload, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext); |
| 1894 | ExitOnFailure(hr, "Failed to verify signature of payload: %ls", pPayload->sczKey); | 1924 | ExitOnFailure(hr, "Failed to verify signature of payload: %ls", pPayload->sczKey); |
| 1895 | break; | 1925 | break; |
| 1896 | case BURN_PAYLOAD_VERIFICATION_HASH: | 1926 | case BURN_PAYLOAD_VERIFICATION_HASH: |
| @@ -2285,17 +2315,18 @@ static HRESULT VerifyHash( | |||
| 2285 | __in LPVOID pContext | 2315 | __in LPVOID pContext |
| 2286 | ) | 2316 | ) |
| 2287 | { | 2317 | { |
| 2288 | UNREFERENCED_PARAMETER(wzUnverifiedPayloadPath); | ||
| 2289 | |||
| 2290 | HRESULT hr = S_OK; | 2318 | HRESULT hr = S_OK; |
| 2291 | BYTE rgbActualHash[SHA512_HASH_LEN] = { }; | 2319 | BYTE rgbActualHash[SHA512_HASH_LEN] = { }; |
| 2292 | DWORD64 qwHashedBytes = 0; | 2320 | DWORD64 qwHashedBytes = 0; |
| 2293 | LPWSTR pszExpected = NULL; | 2321 | LPWSTR pszExpected = NULL; |
| 2294 | LPWSTR pszActual = NULL; | 2322 | LPWSTR pszActual = NULL; |
| 2323 | BOOL fFailedVerification = FALSE; | ||
| 2295 | 2324 | ||
| 2296 | hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep); | 2325 | hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep); |
| 2297 | ExitOnFailure(hr, "Aborted cache verify hash begin."); | 2326 | ExitOnFailure(hr, "Aborted cache verify hash begin."); |
| 2298 | 2327 | ||
| 2328 | fFailedVerification = TRUE; | ||
| 2329 | |||
| 2299 | if (fVerifyFileSize) | 2330 | if (fVerifyFileSize) |
| 2300 | { | 2331 | { |
| 2301 | hr = VerifyFileSize(hFile, qwFileSize, wzUnverifiedPayloadPath); | 2332 | hr = VerifyFileSize(hFile, qwFileSize, wzUnverifiedPayloadPath); |
| @@ -2323,9 +2354,17 @@ static HRESULT VerifyHash( | |||
| 2323 | } | 2354 | } |
| 2324 | } | 2355 | } |
| 2325 | 2356 | ||
| 2357 | fFailedVerification = FALSE; | ||
| 2358 | |||
| 2326 | hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, qwFileSize); | 2359 | hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, qwFileSize); |
| 2327 | 2360 | ||
| 2328 | LExit: | 2361 | LExit: |
| 2362 | if (fFailedVerification) | ||
| 2363 | { | ||
| 2364 | // Make sure the BA process marks this container or payload as having failed verification. | ||
| 2365 | SendCacheFailureMessage(pfnCacheMessageHandler, pContext, cacheStep); | ||
| 2366 | } | ||
| 2367 | |||
| 2329 | SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr); | 2368 | SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr); |
| 2330 | 2369 | ||
| 2331 | ReleaseStr(pszActual); | 2370 | ReleaseStr(pszActual); |
| @@ -2448,3 +2487,20 @@ static HRESULT SendCacheCompleteMessage( | |||
| 2448 | 2487 | ||
| 2449 | return hr; | 2488 | return hr; |
| 2450 | } | 2489 | } |
| 2490 | |||
| 2491 | static HRESULT SendCacheFailureMessage( | ||
| 2492 | __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, | ||
| 2493 | __in LPVOID pContext, | ||
| 2494 | __in BURN_CACHE_STEP cacheStep | ||
| 2495 | ) | ||
| 2496 | { | ||
| 2497 | HRESULT hr = S_OK; | ||
| 2498 | BURN_CACHE_MESSAGE message = { }; | ||
| 2499 | |||
| 2500 | message.type = BURN_CACHE_MESSAGE_FAILURE; | ||
| 2501 | message.failure.cacheStep = cacheStep; | ||
| 2502 | |||
| 2503 | hr = pfnCacheMessageHandler(&message, pContext); | ||
| 2504 | |||
| 2505 | return hr; | ||
| 2506 | } | ||
diff --git a/src/burn/engine/cache.h b/src/burn/engine/cache.h index 9c698b4b..cc28166e 100644 --- a/src/burn/engine/cache.h +++ b/src/burn/engine/cache.h | |||
| @@ -13,6 +13,7 @@ enum BURN_CACHE_MESSAGE_TYPE | |||
| 13 | BURN_CACHE_MESSAGE_BEGIN, | 13 | BURN_CACHE_MESSAGE_BEGIN, |
| 14 | BURN_CACHE_MESSAGE_SUCCESS, | 14 | BURN_CACHE_MESSAGE_SUCCESS, |
| 15 | BURN_CACHE_MESSAGE_COMPLETE, | 15 | BURN_CACHE_MESSAGE_COMPLETE, |
| 16 | BURN_CACHE_MESSAGE_FAILURE, | ||
| 16 | }; | 17 | }; |
| 17 | 18 | ||
| 18 | enum BURN_CACHE_STEP | 19 | enum BURN_CACHE_STEP |
| @@ -68,6 +69,10 @@ typedef struct _BURN_CACHE_MESSAGE | |||
| 68 | { | 69 | { |
| 69 | HRESULT hrStatus; | 70 | HRESULT hrStatus; |
| 70 | } complete; | 71 | } complete; |
| 72 | struct | ||
| 73 | { | ||
| 74 | BURN_CACHE_STEP cacheStep; | ||
| 75 | } failure; | ||
| 71 | }; | 76 | }; |
| 72 | } BURN_CACHE_MESSAGE; | 77 | } BURN_CACHE_MESSAGE; |
| 73 | 78 | ||
diff --git a/src/burn/engine/container.cpp b/src/burn/engine/container.cpp index 2f21baa8..e6b91532 100644 --- a/src/burn/engine/container.cpp +++ b/src/burn/engine/container.cpp | |||
| @@ -173,6 +173,7 @@ extern "C" void ContainersUninitialize( | |||
| 173 | ReleaseStr(pContainer->downloadSource.sczUser); | 173 | ReleaseStr(pContainer->downloadSource.sczUser); |
| 174 | ReleaseStr(pContainer->downloadSource.sczPassword); | 174 | ReleaseStr(pContainer->downloadSource.sczPassword); |
| 175 | ReleaseStr(pContainer->sczUnverifiedPath); | 175 | ReleaseStr(pContainer->sczUnverifiedPath); |
| 176 | ReleaseStr(pContainer->sczFailedLocalAcquisitionPath); | ||
| 176 | ReleaseDict(pContainer->sdhPayloads); | 177 | ReleaseDict(pContainer->sdhPayloads); |
| 177 | } | 178 | } |
| 178 | MemFree(pContainers->rgContainers); | 179 | MemFree(pContainers->rgContainers); |
diff --git a/src/burn/engine/container.h b/src/burn/engine/container.h index f35f1da5..a38afa90 100644 --- a/src/burn/engine/container.h +++ b/src/burn/engine/container.h | |||
| @@ -88,7 +88,9 @@ typedef struct _BURN_CONTAINER | |||
| 88 | DWORD64 qwExtractSizeTotal; | 88 | DWORD64 qwExtractSizeTotal; |
| 89 | DWORD64 qwCommittedCacheProgress; | 89 | DWORD64 qwCommittedCacheProgress; |
| 90 | DWORD64 qwCommittedExtractProgress; | 90 | DWORD64 qwCommittedExtractProgress; |
| 91 | HRESULT hrExtract; | 91 | BOOL fExtracted; |
| 92 | BOOL fFailedVerificationFromAcquisition; | ||
| 93 | LPWSTR sczFailedLocalAcquisitionPath; | ||
| 92 | } BURN_CONTAINER; | 94 | } BURN_CONTAINER; |
| 93 | 95 | ||
| 94 | typedef struct _BURN_CONTAINERS | 96 | typedef struct _BURN_CONTAINERS |
diff --git a/src/burn/engine/elevation.cpp b/src/burn/engine/elevation.cpp index e165a257..ef9c6e97 100644 --- a/src/burn/engine/elevation.cpp +++ b/src/burn/engine/elevation.cpp | |||
| @@ -42,6 +42,7 @@ typedef enum _BURN_ELEVATION_MESSAGE_TYPE | |||
| 42 | BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_BEGIN, | 42 | BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_BEGIN, |
| 43 | BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_COMPLETE, | 43 | BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_COMPLETE, |
| 44 | BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS, | 44 | BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS, |
| 45 | BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_FAILURE, | ||
| 45 | BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS, | 46 | BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS, |
| 46 | BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROCESS_CANCEL, | 47 | BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROCESS_CANCEL, |
| 47 | BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROCESS_STARTED, | 48 | BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROCESS_STARTED, |
| @@ -1792,11 +1793,19 @@ static HRESULT ProcessBurnCacheMessages( | |||
| 1792 | case BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS: | 1793 | case BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS: |
| 1793 | // read message parameters | 1794 | // read message parameters |
| 1794 | hr = BuffReadNumber64((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.success.qwFileSize); | 1795 | hr = BuffReadNumber64((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.success.qwFileSize); |
| 1795 | ExitOnFailure(hr, "Failed to read begin cache step."); | 1796 | ExitOnFailure(hr, "Failed to read success file size."); |
| 1796 | 1797 | ||
| 1797 | message.type = BURN_CACHE_MESSAGE_SUCCESS; | 1798 | message.type = BURN_CACHE_MESSAGE_SUCCESS; |
| 1798 | break; | 1799 | break; |
| 1799 | 1800 | ||
| 1801 | case BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_FAILURE: | ||
| 1802 | // read message parameters | ||
| 1803 | hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, reinterpret_cast<DWORD*>(&message.failure.cacheStep)); | ||
| 1804 | ExitOnFailure(hr, "Failed to read failure cache step."); | ||
| 1805 | |||
| 1806 | message.type = BURN_CACHE_MESSAGE_FAILURE; | ||
| 1807 | break; | ||
| 1808 | |||
| 1800 | case BURN_ELEVATION_MESSAGE_TYPE_PROGRESS_ROUTINE: | 1809 | case BURN_ELEVATION_MESSAGE_TYPE_PROGRESS_ROUTINE: |
| 1801 | fProgressRoutine = TRUE; | 1810 | fProgressRoutine = TRUE; |
| 1802 | break; | 1811 | break; |
| @@ -3511,7 +3520,7 @@ static HRESULT CALLBACK BurnCacheMessageHandler( | |||
| 3511 | case BURN_CACHE_MESSAGE_BEGIN: | 3520 | case BURN_CACHE_MESSAGE_BEGIN: |
| 3512 | // serialize message data | 3521 | // serialize message data |
| 3513 | hr = BuffWriteNumber(&pbData, &cbData, pMessage->begin.cacheStep); | 3522 | hr = BuffWriteNumber(&pbData, &cbData, pMessage->begin.cacheStep); |
| 3514 | ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); | 3523 | ExitOnFailure(hr, "Failed to write cache step to message buffer."); |
| 3515 | 3524 | ||
| 3516 | dwMessage = BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_BEGIN; | 3525 | dwMessage = BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_BEGIN; |
| 3517 | break; | 3526 | break; |
| @@ -3526,10 +3535,18 @@ static HRESULT CALLBACK BurnCacheMessageHandler( | |||
| 3526 | 3535 | ||
| 3527 | case BURN_CACHE_MESSAGE_SUCCESS: | 3536 | case BURN_CACHE_MESSAGE_SUCCESS: |
| 3528 | hr = BuffWriteNumber64(&pbData, &cbData, pMessage->success.qwFileSize); | 3537 | hr = BuffWriteNumber64(&pbData, &cbData, pMessage->success.qwFileSize); |
| 3529 | ExitOnFailure(hr, "Failed to count of files in use to message buffer."); | 3538 | ExitOnFailure(hr, "Failed to write file size to message buffer."); |
| 3530 | 3539 | ||
| 3531 | dwMessage = BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS; | 3540 | dwMessage = BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS; |
| 3532 | break; | 3541 | break; |
| 3542 | |||
| 3543 | case BURN_CACHE_MESSAGE_FAILURE: | ||
| 3544 | // serialize message data | ||
| 3545 | hr = BuffWriteNumber(&pbData, &cbData, pMessage->failure.cacheStep); | ||
| 3546 | ExitOnFailure(hr, "Failed to write cache step to message buffer."); | ||
| 3547 | |||
| 3548 | dwMessage = BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_FAILURE; | ||
| 3549 | break; | ||
| 3533 | } | 3550 | } |
| 3534 | 3551 | ||
| 3535 | // send message | 3552 | // send message |
diff --git a/src/burn/engine/payload.cpp b/src/burn/engine/payload.cpp index a4450bf7..fe3d673e 100644 --- a/src/burn/engine/payload.cpp +++ b/src/burn/engine/payload.cpp | |||
| @@ -240,6 +240,7 @@ extern "C" void PayloadUninitialize( | |||
| 240 | ReleaseMem(pPayload->pbCertificateRootPublicKeyIdentifier); | 240 | ReleaseMem(pPayload->pbCertificateRootPublicKeyIdentifier); |
| 241 | ReleaseStr(pPayload->sczSourcePath); | 241 | ReleaseStr(pPayload->sczSourcePath); |
| 242 | ReleaseStr(pPayload->sczLocalFilePath); | 242 | ReleaseStr(pPayload->sczLocalFilePath); |
| 243 | ReleaseStr(pPayload->sczFailedLocalAcquisitionPath); | ||
| 243 | ReleaseStr(pPayload->downloadSource.sczUrl); | 244 | ReleaseStr(pPayload->downloadSource.sczUrl); |
| 244 | ReleaseStr(pPayload->downloadSource.sczUser); | 245 | ReleaseStr(pPayload->downloadSource.sczUser); |
| 245 | ReleaseStr(pPayload->downloadSource.sczPassword); | 246 | ReleaseStr(pPayload->downloadSource.sczPassword); |
diff --git a/src/burn/engine/payload.h b/src/burn/engine/payload.h index 14738506..351fd058 100644 --- a/src/burn/engine/payload.h +++ b/src/burn/engine/payload.h | |||
| @@ -60,6 +60,9 @@ typedef struct _BURN_PAYLOAD | |||
| 60 | 60 | ||
| 61 | LPWSTR sczUnverifiedPath; | 61 | LPWSTR sczUnverifiedPath; |
| 62 | DWORD cRemainingInstances; | 62 | DWORD cRemainingInstances; |
| 63 | |||
| 64 | BOOL fFailedVerificationFromAcquisition; | ||
| 65 | LPWSTR sczFailedLocalAcquisitionPath; | ||
| 63 | } BURN_PAYLOAD; | 66 | } BURN_PAYLOAD; |
| 64 | 67 | ||
| 65 | typedef struct _BURN_PAYLOADS | 68 | typedef struct _BURN_PAYLOADS |
diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp index 5c9ebe08..e6997b64 100644 --- a/src/burn/engine/plan.cpp +++ b/src/burn/engine/plan.cpp | |||
| @@ -2105,7 +2105,9 @@ static void ResetPlannedContainerState( | |||
| 2105 | pContainer->qwExtractSizeTotal = 0; | 2105 | pContainer->qwExtractSizeTotal = 0; |
| 2106 | pContainer->qwCommittedCacheProgress = 0; | 2106 | pContainer->qwCommittedCacheProgress = 0; |
| 2107 | pContainer->qwCommittedExtractProgress = 0; | 2107 | pContainer->qwCommittedExtractProgress = 0; |
| 2108 | pContainer->hrExtract = S_OK; | 2108 | pContainer->fExtracted = FALSE; |
| 2109 | pContainer->fFailedVerificationFromAcquisition = FALSE; | ||
| 2110 | ReleaseNullStr(pContainer->sczFailedLocalAcquisitionPath); | ||
| 2109 | } | 2111 | } |
| 2110 | 2112 | ||
| 2111 | static void ResetPlannedPayloadsState( | 2113 | static void ResetPlannedPayloadsState( |
| @@ -2118,7 +2120,9 @@ static void ResetPlannedPayloadsState( | |||
| 2118 | 2120 | ||
| 2119 | pPayload->cRemainingInstances = 0; | 2121 | pPayload->cRemainingInstances = 0; |
| 2120 | pPayload->state = BURN_PAYLOAD_STATE_NONE; | 2122 | pPayload->state = BURN_PAYLOAD_STATE_NONE; |
| 2123 | pPayload->fFailedVerificationFromAcquisition = FALSE; | ||
| 2121 | ReleaseNullStr(pPayload->sczLocalFilePath); | 2124 | ReleaseNullStr(pPayload->sczLocalFilePath); |
| 2125 | ReleaseNullStr(pPayload->sczFailedLocalAcquisitionPath); | ||
| 2122 | } | 2126 | } |
| 2123 | } | 2127 | } |
| 2124 | 2128 | ||
diff --git a/src/test/burn/TestBA/TestBA.cs b/src/test/burn/TestBA/TestBA.cs index 7e3d2623..e73c907e 100644 --- a/src/test/burn/TestBA/TestBA.cs +++ b/src/test/burn/TestBA/TestBA.cs | |||
| @@ -155,6 +155,9 @@ namespace WixToolset.Test.BA | |||
| 155 | this.quitAfterDetect = false; | 155 | this.quitAfterDetect = false; |
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | this.ImportContainerSources(); | ||
| 159 | this.ImportPayloadSources(); | ||
| 160 | |||
| 158 | this.wait.WaitOne(); | 161 | this.wait.WaitOne(); |
| 159 | 162 | ||
| 160 | if (this.action == LaunchAction.Help) | 163 | if (this.action == LaunchAction.Help) |
| @@ -657,6 +660,54 @@ namespace WixToolset.Test.BA | |||
| 657 | this.Engine.Log(LogLevel.Standard, String.Concat("TESTBA", relation, ": ", message)); | 660 | this.Engine.Log(LogLevel.Standard, String.Concat("TESTBA", relation, ": ", message)); |
| 658 | } | 661 | } |
| 659 | 662 | ||
| 663 | private void ImportContainerSources() | ||
| 664 | { | ||
| 665 | string testName = this.Engine.GetVariableString("TestGroupName"); | ||
| 666 | using (RegistryKey testKey = Registry.LocalMachine.OpenSubKey(String.Format(@"Software\WiX\Tests\TestBAControl\{0}\container", testName))) | ||
| 667 | { | ||
| 668 | if (testKey == null) | ||
| 669 | { | ||
| 670 | return; | ||
| 671 | } | ||
| 672 | |||
| 673 | foreach (var containerId in testKey.GetSubKeyNames()) | ||
| 674 | { | ||
| 675 | using (RegistryKey subkey = testKey.OpenSubKey(containerId)) | ||
| 676 | { | ||
| 677 | string initialSource = subkey == null ? null : subkey.GetValue("InitialLocalSource") as string; | ||
| 678 | if (initialSource != null) | ||
| 679 | { | ||
| 680 | this.Engine.SetLocalSource(containerId, null, initialSource); | ||
| 681 | } | ||
| 682 | } | ||
| 683 | } | ||
| 684 | } | ||
| 685 | } | ||
| 686 | |||
| 687 | private void ImportPayloadSources() | ||
| 688 | { | ||
| 689 | string testName = this.Engine.GetVariableString("TestGroupName"); | ||
| 690 | using (RegistryKey testKey = Registry.LocalMachine.OpenSubKey(String.Format(@"Software\WiX\Tests\TestBAControl\{0}\payload", testName))) | ||
| 691 | { | ||
| 692 | if (testKey == null) | ||
| 693 | { | ||
| 694 | return; | ||
| 695 | } | ||
| 696 | |||
| 697 | foreach (var payloadId in testKey.GetSubKeyNames()) | ||
| 698 | { | ||
| 699 | using (RegistryKey subkey = testKey.OpenSubKey(payloadId)) | ||
| 700 | { | ||
| 701 | string initialSource = subkey == null ? null : subkey.GetValue("InitialLocalSource") as string; | ||
| 702 | if (initialSource != null) | ||
| 703 | { | ||
| 704 | this.Engine.SetLocalSource(null, payloadId, initialSource); | ||
| 705 | } | ||
| 706 | } | ||
| 707 | } | ||
| 708 | } | ||
| 709 | } | ||
| 710 | |||
| 660 | private List<string> ReadVerifyArguments() | 711 | private List<string> ReadVerifyArguments() |
| 661 | { | 712 | { |
| 662 | string testName = this.Engine.GetVariableString("TestGroupName"); | 713 | string testName = this.Engine.GetVariableString("TestGroupName"); |
diff --git a/src/test/burn/TestData/LayoutTests/BundleB/BundleB.wixproj b/src/test/burn/TestData/LayoutTests/BundleB/BundleB.wixproj new file mode 100644 index 00000000..2ce22ea1 --- /dev/null +++ b/src/test/burn/TestData/LayoutTests/BundleB/BundleB.wixproj | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | <!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> | ||
| 2 | <Project Sdk="WixToolset.Sdk"> | ||
| 3 | <PropertyGroup> | ||
| 4 | <BA>hyperlinkLicense</BA> | ||
| 5 | <OutputType>Bundle</OutputType> | ||
| 6 | <UpgradeCode>{CBF22B7D-C6C0-408A-9F29-81FE8610559C}</UpgradeCode> | ||
| 7 | </PropertyGroup> | ||
| 8 | <ItemGroup> | ||
| 9 | <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" /> | ||
| 10 | </ItemGroup> | ||
| 11 | <ItemGroup> | ||
| 12 | <ProjectReference Include="..\PackageA\PackageA.wixproj" /> | ||
| 13 | </ItemGroup> | ||
| 14 | <ItemGroup> | ||
| 15 | <PackageReference Include="WixToolset.Bal.wixext" /> | ||
| 16 | </ItemGroup> | ||
| 17 | </Project> \ No newline at end of file | ||
diff --git a/src/test/burn/TestData/LayoutTests/BundleB/BundleB.wxs b/src/test/burn/TestData/LayoutTests/BundleB/BundleB.wxs new file mode 100644 index 00000000..fb6eadb9 --- /dev/null +++ b/src/test/burn/TestData/LayoutTests/BundleB/BundleB.wxs | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | <!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> | ||
| 2 | |||
| 3 | |||
| 4 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> | ||
| 5 | <Fragment> | ||
| 6 | <PackageGroup Id="BundlePackages"> | ||
| 7 | <MsiPackage Id="PackageA" SourceFile="$(var.PackageA.TargetPath)" Compressed="no" DownloadUrl="$(var.WebServerBaseUrl)BundleB/{2}" /> | ||
| 8 | </PackageGroup> | ||
| 9 | </Fragment> | ||
| 10 | </Wix> | ||
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/LayoutTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/LayoutTests.cs index 47a42750..14c3cd5d 100644 --- a/src/test/burn/WixToolsetTest.BurnE2E/LayoutTests.cs +++ b/src/test/burn/WixToolsetTest.BurnE2E/LayoutTests.cs | |||
| @@ -65,5 +65,98 @@ namespace WixToolsetTest.BurnE2E | |||
| 65 | Assert.True(File.Exists(Path.Combine(layoutDirectory, "packages.cab"))); | 65 | Assert.True(File.Exists(Path.Combine(layoutDirectory, "packages.cab"))); |
| 66 | Assert.True(File.Exists(Path.Combine(layoutDirectory, "BundleA.wxs"))); | 66 | Assert.True(File.Exists(Path.Combine(layoutDirectory, "BundleA.wxs"))); |
| 67 | } | 67 | } |
| 68 | |||
| 69 | [RuntimeFact] | ||
| 70 | public void CanSkipOverCorruptLocalFileForDownloadableFile() | ||
| 71 | { | ||
| 72 | var bundleB = this.CreateBundleInstaller("BundleB"); | ||
| 73 | var webServer = this.CreateWebServer(); | ||
| 74 | |||
| 75 | webServer.AddFiles(new Dictionary<string, string> | ||
| 76 | { | ||
| 77 | { "/BundleB/PackageA.msi", Path.Combine(this.TestContext.TestDataFolder, "PackageA.msi") }, | ||
| 78 | }); | ||
| 79 | webServer.Start(); | ||
| 80 | |||
| 81 | using var dfs = new DisposableFileSystem(); | ||
| 82 | var baseDirectory = dfs.GetFolder(true); | ||
| 83 | var sourceDirectory = Path.Combine(baseDirectory, "source"); | ||
| 84 | Directory.CreateDirectory(sourceDirectory); | ||
| 85 | var layoutDirectory = Path.Combine(baseDirectory, "layout"); | ||
| 86 | Directory.CreateDirectory(layoutDirectory); | ||
| 87 | |||
| 88 | // Manually copy bundle to empty directory and then run from there so it can't find the uncorrupted file. | ||
| 89 | var bundleBFileInfo = new FileInfo(bundleB.Bundle); | ||
| 90 | var bundleBCopiedPath = Path.Combine(sourceDirectory, bundleBFileInfo.Name); | ||
| 91 | bundleBFileInfo.CopyTo(bundleBCopiedPath); | ||
| 92 | |||
| 93 | // Copy a corrupted version of PackageA.msi next to the bundle. | ||
| 94 | var packageAFileInfo = new FileInfo(Path.Combine(bundleBFileInfo.DirectoryName, "PackageA.msi")); | ||
| 95 | var packageACorruptedFileInfo = new FileInfo(Path.Combine(sourceDirectory, packageAFileInfo.Name)); | ||
| 96 | packageAFileInfo.CopyTo(packageACorruptedFileInfo.FullName); | ||
| 97 | SubtlyCorruptFile(packageACorruptedFileInfo); | ||
| 98 | |||
| 99 | var layoutLogPath = bundleB.Layout(bundleBCopiedPath, layoutDirectory); | ||
| 100 | bundleB.VerifyUnregisteredAndRemovedFromPackageCache(); | ||
| 101 | |||
| 102 | Assert.True(File.Exists(Path.Combine(layoutDirectory, bundleBFileInfo.Name))); | ||
| 103 | Assert.True(File.Exists(Path.Combine(layoutDirectory, packageAFileInfo.Name))); | ||
| 104 | Assert.True(LogVerifier.MessageInLogFile(layoutLogPath, "Verification failed on payload group item: PackageA")); | ||
| 105 | } | ||
| 106 | |||
| 107 | [RuntimeFact] | ||
| 108 | public void CanSkipOverCorruptLocalFileForOtherLocalFile() | ||
| 109 | { | ||
| 110 | var bundleA = this.CreateBundleInstaller("BundleA"); | ||
| 111 | var testBAController = this.CreateTestBAController(); | ||
| 112 | |||
| 113 | using var dfs = new DisposableFileSystem(); | ||
| 114 | var baseDirectory = dfs.GetFolder(true); | ||
| 115 | var sourceDirectory = Path.Combine(baseDirectory, "source"); | ||
| 116 | Directory.CreateDirectory(sourceDirectory); | ||
| 117 | var layoutDirectory = Path.Combine(baseDirectory, "layout"); | ||
| 118 | Directory.CreateDirectory(layoutDirectory); | ||
| 119 | |||
| 120 | // Copy a corrupted version of packages.cab and BundleA.wxs. | ||
| 121 | var bundleAFileInfo = new FileInfo(bundleA.Bundle); | ||
| 122 | var packagesCabFileInfo = new FileInfo(Path.Combine(bundleAFileInfo.DirectoryName, "packages.cab")); | ||
| 123 | var packagesCabCorruptedFileInfo = new FileInfo(Path.Combine(sourceDirectory, packagesCabFileInfo.Name)); | ||
| 124 | packagesCabFileInfo.CopyTo(packagesCabCorruptedFileInfo.FullName); | ||
| 125 | SubtlyCorruptFile(packagesCabCorruptedFileInfo); | ||
| 126 | |||
| 127 | var layoutOnlyPayloadFileInfo = new FileInfo(Path.Combine(bundleAFileInfo.DirectoryName, "BundleA.wxs")); | ||
| 128 | var layoutOnlyPayloadCorruptedFileInfo = new FileInfo(Path.Combine(sourceDirectory, layoutOnlyPayloadFileInfo.Name)); | ||
| 129 | layoutOnlyPayloadFileInfo.CopyTo(layoutOnlyPayloadCorruptedFileInfo.FullName); | ||
| 130 | SubtlyCorruptFile(layoutOnlyPayloadCorruptedFileInfo); | ||
| 131 | |||
| 132 | // Set the source to absolute path so the engine tries the corrupted files first. | ||
| 133 | testBAController.SetContainerInitialLocalSource("PackagesContainer", packagesCabCorruptedFileInfo.FullName); | ||
| 134 | testBAController.SetPayloadInitialLocalSource("LayoutOnlyPayload", layoutOnlyPayloadCorruptedFileInfo.FullName); | ||
| 135 | |||
| 136 | var layoutLogPath = bundleA.Layout(layoutDirectory); | ||
| 137 | bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); | ||
| 138 | |||
| 139 | Assert.True(File.Exists(Path.Combine(layoutDirectory, bundleAFileInfo.Name))); | ||
| 140 | Assert.True(File.Exists(Path.Combine(layoutDirectory, packagesCabFileInfo.Name))); | ||
| 141 | Assert.True(File.Exists(Path.Combine(layoutDirectory, "BundleA.wxs"))); | ||
| 142 | Assert.True(LogVerifier.MessageInLogFile(layoutLogPath, "Verification failed on container: PackagesContainer")); | ||
| 143 | Assert.True(LogVerifier.MessageInLogFile(layoutLogPath, "Verification failed on payload group item: LayoutOnlyPayload")); | ||
| 144 | } | ||
| 145 | |||
| 146 | private static void SubtlyCorruptFile(FileInfo fileInfo) | ||
| 147 | { | ||
| 148 | using (var v = fileInfo.Open(FileMode.Open, FileAccess.ReadWrite)) | ||
| 149 | { | ||
| 150 | // Change one byte of information in the middle of the file to corrupt it. | ||
| 151 | // Hopefully this ensures that these tests will continue to work even if optimizations are added later, | ||
| 152 | // such as checking the file header, product code, or product version during acquisition. | ||
| 153 | var bytePosition = v.Length / 2; | ||
| 154 | v.Position = bytePosition; | ||
| 155 | var byteValue = v.ReadByte(); | ||
| 156 | byteValue = byteValue == 0 ? 1 : 0; | ||
| 157 | v.Position = bytePosition; | ||
| 158 | v.WriteByte((byte)byteValue); | ||
| 159 | } | ||
| 160 | } | ||
| 68 | } | 161 | } |
| 69 | } | 162 | } |
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/Utilities/TestBAController.cs b/src/test/burn/WixToolsetTest.BurnE2E/Utilities/TestBAController.cs index 3f9d76b8..115f3b54 100644 --- a/src/test/burn/WixToolsetTest.BurnE2E/Utilities/TestBAController.cs +++ b/src/test/burn/WixToolsetTest.BurnE2E/Utilities/TestBAController.cs | |||
| @@ -183,6 +183,26 @@ namespace WixToolsetTest.BurnE2E | |||
| 183 | } | 183 | } |
| 184 | 184 | ||
| 185 | /// <summary> | 185 | /// <summary> |
| 186 | /// At startup, set the payload's source path to the given path. | ||
| 187 | /// </summary> | ||
| 188 | /// <param name="payloadId"></param> | ||
| 189 | /// <param name="sourcePath"></param> | ||
| 190 | public void SetPayloadInitialLocalSource(string payloadId, string sourcePath) | ||
| 191 | { | ||
| 192 | this.SetPayloadState(payloadId, "InitialLocalSource", sourcePath); | ||
| 193 | } | ||
| 194 | |||
| 195 | /// <summary> | ||
| 196 | /// At startup, set the container's source path to the given path. | ||
| 197 | /// </summary> | ||
| 198 | /// <param name="containerId"></param> | ||
| 199 | /// <param name="sourcePath"></param> | ||
| 200 | public void SetContainerInitialLocalSource(string containerId, string sourcePath) | ||
| 201 | { | ||
| 202 | this.SetContainerState(containerId, "InitialLocalSource", sourcePath); | ||
| 203 | } | ||
| 204 | |||
| 205 | /// <summary> | ||
| 186 | /// Sets the number of times to re-run the Detect phase. | 206 | /// Sets the number of times to re-run the Detect phase. |
| 187 | /// </summary> | 207 | /// </summary> |
| 188 | /// <param name="state">Number of times to run Detect (after the first, normal, Detect).</param> | 208 | /// <param name="state">Number of times to run Detect (after the first, normal, Detect).</param> |
| @@ -204,7 +224,6 @@ namespace WixToolsetTest.BurnE2E | |||
| 204 | public void SetVerifyArguments(string verifyArguments) | 224 | public void SetVerifyArguments(string verifyArguments) |
| 205 | { | 225 | { |
| 206 | this.SetBurnTestValue("VerifyArguments", verifyArguments); | 226 | this.SetBurnTestValue("VerifyArguments", verifyArguments); |
| 207 | |||
| 208 | } | 227 | } |
| 209 | 228 | ||
| 210 | private void SetPackageState(string packageId, string name, string value) | 229 | private void SetPackageState(string packageId, string name, string value) |
| @@ -223,6 +242,38 @@ namespace WixToolsetTest.BurnE2E | |||
| 223 | } | 242 | } |
| 224 | } | 243 | } |
| 225 | 244 | ||
| 245 | private void SetContainerState(string containerId, string name, string value) | ||
| 246 | { | ||
| 247 | var key = String.Format(@"{0}\container\{1}", this.TestBaseRegKeyPath, containerId); | ||
| 248 | using (var containerKey = Registry.LocalMachine.CreateSubKey(key)) | ||
| 249 | { | ||
| 250 | if (String.IsNullOrEmpty(value)) | ||
| 251 | { | ||
| 252 | containerKey.DeleteValue(name, false); | ||
| 253 | } | ||
| 254 | else | ||
| 255 | { | ||
| 256 | containerKey.SetValue(name, value); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | private void SetPayloadState(string payloadId, string name, string value) | ||
| 262 | { | ||
| 263 | var key = String.Format(@"{0}\payload\{1}", this.TestBaseRegKeyPath, payloadId ?? String.Empty); | ||
| 264 | using (var payloadKey = Registry.LocalMachine.CreateSubKey(key)) | ||
| 265 | { | ||
| 266 | if (String.IsNullOrEmpty(value)) | ||
| 267 | { | ||
| 268 | payloadKey.DeleteValue(name, false); | ||
| 269 | } | ||
| 270 | else | ||
| 271 | { | ||
| 272 | payloadKey.SetValue(name, value); | ||
| 273 | } | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 226 | public void Dispose() | 277 | public void Dispose() |
| 227 | { | 278 | { |
| 228 | Registry.LocalMachine.DeleteSubKeyTree($@"{this.BaseRegKeyPath}\{this.TestGroupName}", false); | 279 | Registry.LocalMachine.DeleteSubKeyTree($@"{this.BaseRegKeyPath}\{this.TestGroupName}", false); |
