@joebigelow / wix / commits / 648f370f

Make PathGetSystemPath return an array of paths ordered by preference.

Sean Hall committed Jun 3, 2022 at 17:48 UTC 648f370f7966b2738c1446601057d888bbd2c70f
12 files changed +338 -75
src/burn/engine/cache.cpp
+72 -55
@@ -14,10 +14,10 @@ static HRESULT CacheVerifyPayloadSignature(
14 __in_z LPCWSTR wzUnverifiedPayloadPath,
15 __in HANDLE hFile
16 );
17 -static HRESULT CalculateBaseWorkingFolder(
17 +static HRESULT CalculatePotentialBaseWorkingFolders(
18 + __in BURN_CACHE* pCache,
19 __in BURN_ENGINE_COMMAND* pInternalCommand,
19 - __in LPCWSTR wzAcquisitionFolder,
20 - __inout_z LPWSTR* psczBaseWorkingFolder
20 + __in LPCWSTR wzAcquisitionFolder
21 );
22 static HRESULT CalculateWorkingFolders(
23 __in BURN_CACHE* pCache,
@@ -321,8 +321,8 @@ extern "C" HRESULT CacheEnsureAcquisitionFolder(
321 hr = DirEnsureExists(pCache->sczAcquisitionFolder, NULL);
322 ExitOnFailure(hr, "Failed create acquisition folder.");
323
324 - // Best effort to ensure our working folder is not encrypted.
325 - ::DecryptFileW(pCache->sczBaseWorkingFolder, 0);
324 + // Best effort to ensure our acquisition folder is not encrypted.
325 + ::DecryptFileW(pCache->sczAcquisitionFolder, 0);
326
327 LExit:
328 return hr;
@@ -336,9 +336,30 @@ extern "C" HRESULT CacheEnsureBaseWorkingFolder(
336 Assert(pCache->fInitializedCache);
337
338 HRESULT hr = S_OK;
339 + LPWSTR sczPotential = NULL;
340 +
341 + if (!pCache->fInitializedBaseWorkingFolder)
342 + {
343 + for (DWORD i = 0; i < pCache->cPotentialBaseWorkingFolders; ++i)
344 + {
345 + hr = PathConcatRelativeToBase(pCache->rgsczPotentialBaseWorkingFolders[i], pCache->wzGuid, &sczPotential);
346 + ExitOnFailure(hr, "Failed to append random guid on to potential path for working folder.");
347 +
348 + hr = DirEnsureExists(sczPotential, NULL);
349 + if (SUCCEEDED(hr))
350 + {
351 + pCache->sczBaseWorkingFolder = sczPotential;
352 + sczPotential = NULL;
353 + break;
354 + }
355 +
356 + LogErrorId(hr, MSG_INVALID_BASE_WORKING_FOLDER, sczPotential, NULL, NULL);
357 + }
358
340 - hr = DirEnsureExists(pCache->sczBaseWorkingFolder, NULL);
341 - ExitOnFailure(hr, "Failed create working folder.");
359 + ExitOnNull(pCache->sczBaseWorkingFolder, hr, E_INVALIDSTATE, "No usable base working folder found.");
360 +
361 + pCache->fInitializedBaseWorkingFolder = TRUE;
362 + }
363
364 // Best effort to ensure our working folder is not encrypted.
365 ::DecryptFileW(pCache->sczBaseWorkingFolder, 0);
@@ -350,6 +371,8 @@ extern "C" HRESULT CacheEnsureBaseWorkingFolder(
371 }
372
373 LExit:
374 + ReleaseStr(sczPotential);
375 +
376 return hr;
377 }
378
@@ -360,6 +383,7 @@ extern "C" HRESULT CacheCalculateBundleWorkingPath(
383 )
384 {
385 Assert(pCache->fInitializedCache);
386 + Assert(pCache->fInitializedBaseWorkingFolder);
387
388 HRESULT hr = S_OK;
389
@@ -1180,7 +1204,7 @@ extern "C" HRESULT CacheRemoveBaseWorkingFolder(
1204 {
1205 HRESULT hr = S_OK;
1206
1183 - if (pCache->fInitializedCacheSources)
1207 + if (pCache->fInitializedBaseWorkingFolder)
1208 {
1209 // Try to clean out everything in the working folder.
1210 hr = DirEnsureDeleteEx(pCache->sczBaseWorkingFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
@@ -1343,70 +1367,78 @@ extern "C" void CacheUninitialize(
1367 __in BURN_CACHE* pCache
1368 )
1369 {
1346 - ReleaseNullStr(pCache->sczCurrentMachinePackageCache);
1347 - ReleaseNullStr(pCache->sczDefaultMachinePackageCache);
1348 - ReleaseNullStr(pCache->sczDefaultUserPackageCache);
1349 - ReleaseNullStr(pCache->sczBaseWorkingFolder);
1350 - ReleaseNullStr(pCache->sczAcquisitionFolder);
1351 - ReleaseNullStr(pCache->sczSourceProcessFolder);
1352 -
1353 - pCache->fRunningFromCache = FALSE;
1354 - pCache->fInitializedCache = FALSE;
1355 - pCache->fInitializedCacheSources = FALSE;
1356 - pCache->fPerMachineCacheRootVerified = FALSE;
1357 - pCache->fOriginalPerMachineCacheRootVerified = FALSE;
1358 - pCache->fUnverifiedCacheFolderCreated = FALSE;
1359 - pCache->fCustomMachinePackageCache = FALSE;
1370 + ReleaseStrArray(pCache->rgsczPotentialBaseWorkingFolders, pCache->cPotentialBaseWorkingFolders);
1371 + ReleaseStr(pCache->sczCurrentMachinePackageCache);
1372 + ReleaseStr(pCache->sczDefaultMachinePackageCache);
1373 + ReleaseStr(pCache->sczDefaultUserPackageCache);
1374 + ReleaseStr(pCache->sczBaseWorkingFolder);
1375 + ReleaseStr(pCache->sczAcquisitionFolder);
1376 + ReleaseStr(pCache->sczSourceProcessFolder);
1377 +
1378 + memset(pCache, 0, sizeof(BURN_CACHE));
1379 }
1380
1381 // Internal functions.
1382
1364 -static HRESULT CalculateBaseWorkingFolder(
1383 +static HRESULT CalculatePotentialBaseWorkingFolders(
1384 + __in BURN_CACHE* pCache,
1385 __in BURN_ENGINE_COMMAND* pInternalCommand,
1366 - __in LPCWSTR wzAcquisitionFolder,
1367 - __inout_z LPWSTR* psczBaseWorkingFolder
1386 + __in LPCWSTR wzAcquisitionFolder
1387 )
1388 {
1389 + Assert(!pCache->rgsczPotentialBaseWorkingFolders && !pCache->cPotentialBaseWorkingFolders);
1390 HRESULT hr = S_OK;
1391 + LPWSTR sczTemp = NULL;
1392
1372 - ReleaseNullStr(*psczBaseWorkingFolder);
1393 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pCache->rgsczPotentialBaseWorkingFolders), 6, sizeof(LPWSTR), 6);
1394 + ExitOnFailure(hr, "Failed to initialize array.");
1395
1396 // The value from the command line takes precedence.
1397 if (pInternalCommand->sczEngineWorkingDirectory)
1398 {
1377 - hr = PathExpand(psczBaseWorkingFolder, pInternalCommand->sczEngineWorkingDirectory, PATH_EXPAND_FULLPATH);
1399 + hr = PathExpand(&sczTemp, pInternalCommand->sczEngineWorkingDirectory, PATH_EXPAND_FULLPATH);
1400 ExitOnFailure(hr, "Failed to expand engine working directory from command-line: '%ls'", pInternalCommand->sczEngineWorkingDirectory);
1401
1380 - ExitFunction();
1402 + pCache->rgsczPotentialBaseWorkingFolders[pCache->cPotentialBaseWorkingFolders] = sczTemp;
1403 + sczTemp = NULL;
1404 + ++pCache->cPotentialBaseWorkingFolders;
1405 }
1406
1407 // The base working folder can be specified through policy,
1408 // but only use it if elevated because it should be secured against non-admin users.
1409 if (pInternalCommand->fInitiallyElevated)
1410 {
1387 - hr = PolcReadString(POLICY_BURN_REGISTRY_PATH, L"EngineWorkingDirectory", NULL, psczBaseWorkingFolder);
1411 + hr = PolcReadString(POLICY_BURN_REGISTRY_PATH, L"EngineWorkingDirectory", NULL, &sczTemp);
1412 ExitOnFailure(hr, "Failed to read EngineWorkingDirectory policy directory.");
1413
1390 - if (*psczBaseWorkingFolder)
1414 + if (sczTemp)
1415 {
1416 // PolcReadString is supposed to automatically expand REG_EXPAND_SZ values.
1393 - ExitFunction();
1417 + pCache->rgsczPotentialBaseWorkingFolders[pCache->cPotentialBaseWorkingFolders] = sczTemp;
1418 + sczTemp = NULL;
1419 + ++pCache->cPotentialBaseWorkingFolders;
1420 }
1421 }
1422
1423 // Default to the acquisition folder, but need to use system temp path for security reasons if running elevated.
1424 if (pInternalCommand->fInitiallyElevated)
1425 {
1400 - hr = PathGetSystemTempPath(psczBaseWorkingFolder);
1401 - ExitOnFailure(hr, "Failed to get system temp folder path for base working folder.");
1426 + hr = PathGetSystemTempPaths(&pCache->rgsczPotentialBaseWorkingFolders, &pCache->cPotentialBaseWorkingFolders);
1427 + ExitOnFailure(hr, "Failed to get system temp folder paths for base working folder.");
1428 }
1429 else
1430 {
1405 - hr = StrAllocString(psczBaseWorkingFolder, wzAcquisitionFolder, 0);
1431 + hr = StrAllocString(&sczTemp, wzAcquisitionFolder, 0);
1432 ExitOnFailure(hr, "Failed to copy acquisition folder path for base working folder.");
1433 +
1434 + pCache->rgsczPotentialBaseWorkingFolders[pCache->cPotentialBaseWorkingFolders] = sczTemp;
1435 + sczTemp = NULL;
1436 + ++pCache->cPotentialBaseWorkingFolders;
1437 }
1438
1439 LExit:
1440 + ReleaseStr(sczTemp);
1441 +
1442 return hr;
1443 }
1444
@@ -1416,11 +1448,7 @@ static HRESULT CalculateWorkingFolders(
1448 )
1449 {
1450 HRESULT hr = S_OK;
1419 - RPC_STATUS rs = RPC_S_OK;
1451 LPWSTR sczBaseAcquisitionPath = NULL;
1421 - LPWSTR sczTempPath = NULL;
1422 - UUID guid = {};
1423 - WCHAR wzGuid[39];
1452
1453 hr = PathGetTempPath(&sczBaseAcquisitionPath);
1454 ExitOnFailure(hr, "Failed to get temp folder path for acquisition folder base.");
@@ -1428,31 +1456,20 @@ static HRESULT CalculateWorkingFolders(
1456 hr = PathBackslashTerminate(&sczBaseAcquisitionPath);
1457 ExitOnFailure(hr, "Failed to backslashify base engine working directory.");
1458
1431 - hr = CalculateBaseWorkingFolder(pInternalCommand, sczBaseAcquisitionPath, &sczTempPath);
1432 - ExitOnFailure(hr, "Failed to get base engine working directory.");
1433 -
1434 - hr = PathBackslashTerminate(&sczTempPath);
1435 - ExitOnFailure(hr, "Failed to backslashify base engine working directory.");
1459 + hr = CalculatePotentialBaseWorkingFolders(pCache, pInternalCommand, sczBaseAcquisitionPath);
1460 + ExitOnFailure(hr, "Failed to get potential base engine working directories.");
1461
1437 - rs = ::UuidCreate(&guid);
1438 - hr = HRESULT_FROM_RPC(rs);
1462 + hr = GuidFixedCreate(pCache->wzGuid);
1463 ExitOnFailure(hr, "Failed to create working folder guid.");
1464
1441 - if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid)))
1442 - {
1443 - hr = E_OUTOFMEMORY;
1444 - ExitOnRootFailure(hr, "Failed to convert working folder guid into string.");
1445 - }
1465 + pCache->wzGuid[GUID_STRING_LENGTH - 1] = L'\\';
1466 + pCache->wzGuid[GUID_STRING_LENGTH] = L'\0';
1467
1447 - hr = StrAllocFormatted(&pCache->sczAcquisitionFolder, L"%ls%ls\\", sczBaseAcquisitionPath, wzGuid);
1468 + hr = PathConcatRelativeToBase(sczBaseAcquisitionPath, pCache->wzGuid, &pCache->sczAcquisitionFolder);
1469 ExitOnFailure(hr, "Failed to append random guid on to temp path for acquisition folder.");
1470
1450 - hr = StrAllocFormatted(&pCache->sczBaseWorkingFolder, L"%ls%ls\\", sczTempPath, wzGuid);
1451 - ExitOnFailure(hr, "Failed to append random guid on to temp path for working folder.");
1452 -
1471 LExit:
1472 ReleaseStr(sczBaseAcquisitionPath);
1455 - ReleaseStr(sczTempPath);
1473
1474 return hr;
1475 }
src/burn/engine/cache.h
+7
@@ -35,11 +35,18 @@ typedef struct _BURN_CACHE
35 LPWSTR sczDefaultMachinePackageCache;
36 LPWSTR sczCurrentMachinePackageCache;
37
38 + WCHAR wzGuid[GUID_STRING_LENGTH + 1];
39 + LPWSTR* rgsczPotentialBaseWorkingFolders;
40 + DWORD cPotentialBaseWorkingFolders;
41 +
42 // Only valid after CacheInitializeSources
43 BOOL fInitializedCacheSources;
44 BOOL fRunningFromCache;
45 LPWSTR sczSourceProcessFolder;
46 LPWSTR sczAcquisitionFolder;
47 +
48 + // Only valid after CacheEnsureBaseWorkingFolder
49 + BOOL fInitializedBaseWorkingFolder;
50 LPWSTR sczBaseWorkingFolder;
51 } BURN_CACHE;
52
src/burn/engine/engine.cpp
+7
@@ -97,6 +97,7 @@ extern "C" HRESULT EngineRun(
97 LPWSTR sczExePath = NULL;
98 BOOL fRunUntrusted = FALSE;
99 BOOL fRunNormal = FALSE;
100 + BOOL fRunRunOnce = FALSE;
101 BOOL fRestart = FALSE;
102
103 BURN_ENGINE_STATE engineState = { };
@@ -221,6 +222,8 @@ extern "C" HRESULT EngineRun(
222 break;
223
224 case BURN_MODE_RUNONCE:
225 + fRunRunOnce = TRUE;
226 +
227 hr = RunRunOnce(&engineState, nCmdShow);
228 ExitOnFailure(hr, "Failed to run RunOnce mode.");
229 break;
@@ -303,6 +306,10 @@ LExit:
306 {
307 LogId(REPORT_STANDARD, MSG_EXITING_CLEAN_ROOM, FAILED(hr) ? (int)hr : *pdwExitCode);
308 }
309 + else if (fRunRunOnce)
310 + {
311 + LogId(REPORT_STANDARD, MSG_EXITING_RUN_ONCE, FAILED(hr) ? (int)hr : *pdwExitCode);
312 + }
313
314 if (fLogInitialized)
315 {
src/burn/engine/engine.mc
+15 -1
@@ -149,6 +149,20 @@ Language=English
149 Exit code: 0x%1!x!
150 .
151
152 +MessageId=18
153 +Severity=Success
154 +SymbolicName=MSG_EXITING_RUN_ONCE
155 +Language=English
156 +Exit code: 0x%1!x!
157 +.
158 +
159 +MessageId=19
160 +Severity=Warning
161 +SymbolicName=MSG_INVALID_BASE_WORKING_FOLDER
162 +Language=English
163 +Failed to use folder as base working folder: %2!ls!, encountered error: %1!ls!.
164 +.
165 +
166 MessageId=51
167 Severity=Error
168 SymbolicName=MSG_FAILED_PARSE_CONDITION
@@ -811,7 +825,7 @@ MessageId=346
825 Severity=Warning
826 SymbolicName=MSG_CACHE_RETRYING_PACKAGE
827 Language=English
814 -Application requested retry of caching package: %1!ls!, encountered error: 0x%2!x!. Retrying...
828 +Application requested retry of caching package: %2!ls!, encountered error: %1!ls!. Retrying...
829 .
830
831 MessageId=347
src/burn/test/BurnUnitTest/CacheTest.cpp
+93 -2
@@ -37,11 +37,100 @@ namespace Bootstrapper
37 using namespace System::IO;
38 using namespace Xunit;
39
40 - public ref class CacheTest : BurnUnitTest
40 + public ref class CacheTest : BurnUnitTest, IClassFixture<TestRegistryFixture^>
41 {
42 + private:
43 + TestRegistryFixture^ testRegistry;
44 public:
43 - CacheTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture)
45 + CacheTest(BurnTestFixture^ fixture, TestRegistryFixture^ registryFixture) : BurnUnitTest(fixture)
46 {
47 + this->testRegistry = registryFixture;
48 + }
49 +
50 + [Fact]
51 + void CacheElevatedTempFallbacksTest()
52 + {
53 + HRESULT hr = S_OK;
54 + BURN_CACHE cache = { };
55 + BURN_ENGINE_COMMAND internalCommand = { };
56 + HKEY hkSystemEnvironment = NULL;
57 + HKEY hkBurnPolicy = NULL;
58 +
59 + internalCommand.fInitiallyElevated = TRUE;
60 +
61 + try
62 + {
63 + this->testRegistry->SetUp();
64 +
65 + // No registry keys, so should fallback to %windir%\TEMP.
66 + hr = CacheInitialize(&cache, &internalCommand);
67 + NativeAssert::Succeeded(hr, "Failed to initialize cache.");
68 + Assert::NotEqual<DWORD>(0, cache.cPotentialBaseWorkingFolders);
69 + VerifyBaseWorkingFolder(L"%windir%\\TEMP\\", cache.rgsczPotentialBaseWorkingFolders[0]);
70 + CacheUninitialize(&cache);
71 +
72 + hr = RegCreate(HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\Session Manager\\Environment", GENERIC_WRITE, &hkSystemEnvironment);
73 + NativeAssert::Succeeded(hr, "Failed to create system environment key.");
74 +
75 + // Third fallback is system-level %TEMP%.
76 + hr = RegWriteExpandString(hkSystemEnvironment, L"TEMP", L"A:\\TEST\\TEMP");
77 + NativeAssert::Succeeded(hr, "Failed to write TEMP system environment value.");
78 +
79 + hr = CacheInitialize(&cache, &internalCommand);
80 + NativeAssert::Succeeded(hr, "Failed to initialize cache.");
81 + Assert::NotEqual<DWORD>(0, cache.cPotentialBaseWorkingFolders);
82 + VerifyBaseWorkingFolder(L"A:\\TEST\\TEMP\\", cache.rgsczPotentialBaseWorkingFolders[0]);
83 + CacheUninitialize(&cache);
84 +
85 + // Second fallback is system-level %TMP%.
86 + hr = RegWriteExpandString(hkSystemEnvironment, L"TMP", L"B:\\TEST\\TMP\\");
87 + NativeAssert::Succeeded(hr, "Failed to write TEMP system environment value.");
88 +
89 + hr = CacheInitialize(&cache, &internalCommand);
90 + NativeAssert::Succeeded(hr, "Failed to initialize cache.");
91 + Assert::NotEqual<DWORD>(0, cache.cPotentialBaseWorkingFolders);
92 + VerifyBaseWorkingFolder(L"B:\\TEST\\TMP\\", cache.rgsczPotentialBaseWorkingFolders[0]);
93 + CacheUninitialize(&cache);
94 +
95 + hr = RegCreate(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Policies\\WiX\\Burn", GENERIC_WRITE, &hkBurnPolicy);
96 + NativeAssert::Succeeded(hr, "Failed to create Burn policy key.");
97 +
98 + // Default source is Burn policy.
99 + hr = RegWriteExpandString(hkBurnPolicy, L"EngineWorkingDirectory", L"D:\\TEST\\POLICY\\");
100 + NativeAssert::Succeeded(hr, "Failed to write EngineWorkingDirectory Burn policy value.");
101 +
102 + hr = CacheInitialize(&cache, &internalCommand);
103 + NativeAssert::Succeeded(hr, "Failed to initialize cache.");
104 + Assert::NotEqual<DWORD>(0, cache.cPotentialBaseWorkingFolders);
105 + VerifyBaseWorkingFolder(L"D:\\TEST\\POLICY\\", cache.rgsczPotentialBaseWorkingFolders[0]);
106 + CacheUninitialize(&cache);
107 +
108 + // Command line parameter overrides everything else.
109 + hr = StrAllocString(&internalCommand.sczEngineWorkingDirectory, L"E:\\TEST\\COMMANDLINE\\", 0);
110 + NativeAssert::Succeeded(hr, "Failed to copy command line working directory.");
111 +
112 + hr = CacheInitialize(&cache, &internalCommand);
113 + NativeAssert::Succeeded(hr, "Failed to initialize cache.");
114 + Assert::NotEqual<DWORD>(0, cache.cPotentialBaseWorkingFolders);
115 + VerifyBaseWorkingFolder(L"E:\\TEST\\COMMANDLINE\\", cache.rgsczPotentialBaseWorkingFolders[0]);
116 + CacheUninitialize(&cache);
117 + }
118 + finally
119 + {
120 + ReleaseRegKey(hkBurnPolicy);
121 + ReleaseRegKey(hkSystemEnvironment);
122 + ReleaseStr(internalCommand.sczEngineWorkingDirectory);
123 +
124 + CacheUninitialize(&cache);
125 +
126 + this->testRegistry->TearDown();
127 + }
128 + }
129 +
130 + void VerifyBaseWorkingFolder(LPCWSTR wzExpectedUnexpanded, LPCWSTR wzActual)
131 + {
132 + String^ expected = Environment::ExpandEnvironmentVariables(gcnew String(wzExpectedUnexpanded));
133 + WixAssert::StringEqual(expected, gcnew String(wzActual), true);
134 }
135
136 [Fact]
@@ -93,6 +182,8 @@ namespace Bootstrapper
182 File::SetAttributes(filePath, FileAttributes::Normal);
183 File::Delete(filePath);
184 }
185 +
186 + CacheUninitialize(&cache);
187 }
188 }
189 };
src/burn/test/BurnUnitTest/precomp.h
+2
@@ -19,9 +19,11 @@
19 #include <buffutil.h>
20 #include <dirutil.h>
21 #include <fileutil.h>
22 +#include <guidutil.h>
23 #include <logutil.h>
24 #include <memutil.h>
25 #include <pathutil.h>
26 +#include <polcutil.h>
27 #include <regutil.h>
28 #include <resrutil.h>
29 #include <shelutil.h>
src/libs/dutil/WixToolset.DUtil/guidutil.cpp
+2 -2
@@ -9,6 +9,7 @@
9 #define GuidExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__)
10 #define GuidExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__)
11 #define GuidExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__)
12 +#define GuidExitWithRootFailure(x, e, s, ...) ExitWithRootFailureSource(DUTIL_SOURCE_GUIDUTIL, x, e, s, __VA_ARGS__)
13 #define GuidExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__)
14 #define GuidExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_GUIDUTIL, p, x, e, s, __VA_ARGS__)
15 #define GuidExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, p, x, s, __VA_ARGS__)
@@ -29,8 +30,7 @@ extern "C" HRESULT DAPI GuidFixedCreate(
30
31 if (!::StringFromGUID2(guid, wzGuid, GUID_STRING_LENGTH))
32 {
32 - hr = E_OUTOFMEMORY;
33 - GuidExitOnRootFailure(hr, "Failed to convert guid into string.");
33 + GuidExitWithRootFailure(hr, E_OUTOFMEMORY, "Failed to convert guid into string.");
34 }
35
36 LExit:
src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
+5 -4
@@ -205,11 +205,12 @@ DAPI_(HRESULT) PathGetTempPath(
205 );
206
207 /*******************************************************************
208 - PathGetSystemTempPath - returns the path to the system temp folder
209 - that is backslash terminated.
208 + PathGetSystemTempPaths - returns the paths to system temp folders
209 + that are backslash terminated with higher preference first.
210 *******************************************************************/
211 -DAPI_(HRESULT) PathGetSystemTempPath(
212 - __out_z LPWSTR* psczSystemTempPath
211 +DAPI_(HRESULT) PathGetSystemTempPaths(
212 + __inout_z LPWSTR** prgsczSystemTempPaths,
213 + __inout DWORD* pcSystemTempPaths
214 );
215
216 /*******************************************************************
src/libs/dutil/WixToolset.DUtil/pathutil.cpp
+29 -9
@@ -923,12 +923,14 @@ LExit:
923 }
924
925
926 -DAPI_(HRESULT) PathGetSystemTempPath(
927 - __out_z LPWSTR* psczSystemTempPath
926 +DAPI_(HRESULT) PathGetSystemTempPaths(
927 + __inout_z LPWSTR** prgsczSystemTempPaths,
928 + __inout DWORD* pcSystemTempPaths
929 )
930 {
931 HRESULT hr = S_OK;
932 HKEY hKey = NULL;
933 + LPWSTR sczTemp = NULL;
934 WCHAR wzTempPath[MAX_PATH + 1] = { };
935 DWORD cch = 0;
936
@@ -940,26 +942,36 @@ DAPI_(HRESULT) PathGetSystemTempPath(
942
943 // Follow documented precedence rules for TMP/TEMP from ::GetTempPath.
944 // TODO: values will be expanded with the current environment variables instead of the system environment variables.
943 - hr = RegReadString(hKey, L"TMP", psczSystemTempPath);
945 + hr = RegReadString(hKey, L"TMP", &sczTemp);
946 if (E_FILENOTFOUND != hr)
947 {
948 PathExitOnFailure(hr, "Failed to get system TMP value.");
949
948 - hr = PathBackslashTerminate(psczSystemTempPath);
950 + hr = PathBackslashTerminate(&sczTemp);
951 PathExitOnFailure(hr, "Failed to backslash terminate system TMP value.");
952
951 - ExitFunction();
953 + hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(prgsczSystemTempPaths), *pcSystemTempPaths, 1, sizeof(LPWSTR), 3);
954 + PathExitOnFailure(hr, "Failed to ensure array size for system TMP value.");
955 +
956 + (*prgsczSystemTempPaths)[*pcSystemTempPaths] = sczTemp;
957 + sczTemp = NULL;
958 + *pcSystemTempPaths += 1;
959 }
960
954 - hr = RegReadString(hKey, L"TEMP", psczSystemTempPath);
961 + hr = RegReadString(hKey, L"TEMP", &sczTemp);
962 if (E_FILENOTFOUND != hr)
963 {
964 PathExitOnFailure(hr, "Failed to get system TEMP value.");
965
959 - hr = PathBackslashTerminate(psczSystemTempPath);
966 + hr = PathBackslashTerminate(&sczTemp);
967 PathExitOnFailure(hr, "Failed to backslash terminate system TEMP value.");
968
962 - ExitFunction();
969 + hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(prgsczSystemTempPaths), *pcSystemTempPaths, 1, sizeof(LPWSTR), 2);
970 + PathExitOnFailure(hr, "Failed to ensure array size for system TEMP value.");
971 +
972 + (*prgsczSystemTempPaths)[*pcSystemTempPaths] = sczTemp;
973 + sczTemp = NULL;
974 + *pcSystemTempPaths += 1;
975 }
976 }
977
@@ -973,11 +985,19 @@ DAPI_(HRESULT) PathGetSystemTempPath(
985 PathExitWithRootFailure(hr, E_INSUFFICIENT_BUFFER, "Windows directory path too long.");
986 }
987
976 - hr = PathConcat(wzTempPath, L"TEMP\\", psczSystemTempPath);
988 + hr = PathConcat(wzTempPath, L"TEMP\\", &sczTemp);
989 PathExitOnFailure(hr, "Failed to concat Temp directory on Windows directory path.");
990
991 + hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(prgsczSystemTempPaths), *pcSystemTempPaths, 1, sizeof(LPWSTR), 1);
992 + PathExitOnFailure(hr, "Failed to ensure array size for Windows\\TEMP value.");
993 +
994 + (*prgsczSystemTempPaths)[*pcSystemTempPaths] = sczTemp;
995 + sczTemp = NULL;
996 + *pcSystemTempPaths += 1;
997 +
998 LExit:
999 ReleaseRegKey(hKey);
1000 + ReleaseStr(sczTemp);
1001
1002 return hr;
1003 }
src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
+28
@@ -799,6 +799,34 @@ namespace DutilTests
799 }
800 }
801
802 + [Fact]
803 + void PathGetSystemTempPathsTest()
804 + {
805 + HRESULT hr = S_OK;
806 + LPWSTR* rgsczPaths = NULL;
807 + DWORD cPaths = 0;
808 + DWORD cPathsOriginal = 0;
809 +
810 + try
811 + {
812 + hr = PathGetSystemTempPaths(&rgsczPaths, &cPaths);
813 + NativeAssert::Succeeded(hr, "PathGetSystemTempPaths failed.");
814 +
815 + Assert::InRange<DWORD>(cPaths, 1, 3);
816 + WixAssert::StringEqual(Environment::ExpandEnvironmentVariables("%windir%\\temp\\"), gcnew String(rgsczPaths[cPaths - 1]), true);
817 +
818 + cPathsOriginal = cPaths;
819 +
820 + hr = PathGetSystemTempPaths(&rgsczPaths, &cPaths);
821 + NativeAssert::Succeeded(hr, "PathGetSystemTempPaths failed.");
822 + Assert::Equal(cPathsOriginal * 2, cPaths);
823 + }
824 + finally
825 + {
826 + ReleaseStrArray(rgsczPaths, cPaths);
827 + }
828 + }
829 +
830 [Fact]
831 void PathNormalizeSlashesFixedTest()
832 {
src/test/burn/WixTestTools/BundleVerifier.cs
+10 -2
@@ -15,7 +15,8 @@ namespace WixTestTools
15 public partial class BundleInstaller
16 {
17 public const string DependencyRegistryRoot = "Software\\Classes\\Installer\\Dependencies";
18 - public const string FULL_BURN_POLICY_REGISTRY_PATH = "SOFTWARE\\WOW6432Node\\Policies\\WiX\\Burn";
18 + public const string FULL_BURN_POLICY_REGISTRY_PATH = "SOFTWARE\\Policies\\WiX\\Burn";
19 + public const string FULL_BURN_POLICY_REGISTRY_PATH_WOW6432NODE = "SOFTWARE\\WOW6432Node\\Policies\\WiX\\Burn";
20 public const string PACKAGE_CACHE_FOLDER_NAME = "Package Cache";
21
22 public string BundlePdb { get; }
@@ -35,12 +36,19 @@ namespace WixTestTools
36 return this.BundleSymbol;
37 }
38
39 + public string GetFullBurnPolicyRegistryPath()
40 + {
41 + var bundleSymbol = this.GetBundleSymbol();
42 + var x64 = bundleSymbol.Platform != Platform.X86;
43 + return x64 ? FULL_BURN_POLICY_REGISTRY_PATH : FULL_BURN_POLICY_REGISTRY_PATH_WOW6432NODE;
44 + }
45 +
46 public string GetPackageCachePathForCacheId(string cacheId, bool perMachine)
47 {
48 string cachePath;
49 if (perMachine)
50 {
43 - using var policyKey = Registry.LocalMachine.OpenSubKey(FULL_BURN_POLICY_REGISTRY_PATH);
51 + using var policyKey = Registry.LocalMachine.OpenSubKey(this.GetFullBurnPolicyRegistryPath());
52 var redirectedCachePath = policyKey?.GetValue("PackageCache") as string;
53 cachePath = redirectedCachePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), PACKAGE_CACHE_FOLDER_NAME);
54 }
src/test/burn/WixToolsetTest.BurnE2E/CacheTests.cs
+68
@@ -5,6 +5,7 @@ namespace WixToolsetTest.BurnE2E
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 + using Microsoft.Win32;
9 using WixBuildTools.TestSupport;
10 using WixTestTools;
11 using WixToolset.Mba.Core;
@@ -201,5 +202,72 @@ namespace WixToolsetTest.BurnE2E
202 packageB.VerifyInstalled(true);
203 }
204 }
205 +
206 + [RuntimeFact]
207 + public void CanGetEngineWorkingDirectoryFromCommandLine()
208 + {
209 + var bundleA = this.CreateBundleInstaller("BundleA");
210 + var testBAController = this.CreateTestBAController();
211 +
212 + testBAController.SetImmediatelyQuit();
213 +
214 + using (var dfs = new DisposableFileSystem())
215 + {
216 + var baseTempPath = dfs.GetFolder(true);
217 + var logPath = bundleA.Install(0, $"-burn.engine.working.directory=\"{baseTempPath}\"");
218 + LogVerifier.MessageInLogFileRegex(logPath, $"Burn x86 v4.*, Windows v.* \\(Build .*: Service Pack .*\\), path: {baseTempPath.Replace("\\", "\\\\")}\\\\.*\\\\.cr\\\\BundleA.exe");
219 + }
220 + }
221 +
222 + [RuntimeFact]
223 + public void CanGetEngineWorkingDirectoryFromPolicy()
224 + {
225 + var deletePolicyKey = false;
226 + string originalPolicyValue = null;
227 +
228 + var bundleA = this.CreateBundleInstaller("BundleA");
229 + var testBAController = this.CreateTestBAController();
230 + var policyPath = bundleA.GetFullBurnPolicyRegistryPath();
231 +
232 + testBAController.SetImmediatelyQuit();
233 +
234 + try
235 + {
236 + using (var dfs = new DisposableFileSystem())
237 + {
238 + var baseTempPath = dfs.GetFolder(true);
239 +
240 + var policyKey = Registry.LocalMachine.OpenSubKey(policyPath, writable: true);
241 + if (policyKey == null)
242 + {
243 + policyKey = Registry.LocalMachine.CreateSubKey(policyPath, writable: true);
244 + deletePolicyKey = true;
245 + }
246 +
247 + using (policyKey)
248 + {
249 + originalPolicyValue = policyKey.GetValue("EngineWorkingDirectory") as string;
250 + policyKey.SetValue("EngineWorkingDirectory", baseTempPath);
251 + }
252 +
253 + var logPath = bundleA.Install();
254 + LogVerifier.MessageInLogFileRegex(logPath, $"Burn x86 v4.*, Windows v.* \\(Build .*: Service Pack .*\\), path: {baseTempPath.Replace("\\", "\\\\")}\\\\.*\\\\.cr\\\\BundleA.exe");
255 + }
256 + }
257 + finally
258 + {
259 + if (deletePolicyKey)
260 + {
261 + Registry.LocalMachine.DeleteSubKeyTree(policyPath);
262 + }
263 + else if (originalPolicyValue != null)
264 + {
265 + using (var policyKey = Registry.LocalMachine.CreateSubKey(policyPath, writable: true))
266 + {
267 + policyKey.SetValue("EngineWorkingDirectory", originalPolicyValue);
268 + }
269 + }
270 + }
271 + }
272 }
273 }