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 | |
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
-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); |