aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/burn/engine/cache.cpp127
-rw-r--r--src/burn/engine/cache.h7
-rw-r--r--src/burn/engine/engine.cpp7
-rw-r--r--src/burn/engine/engine.mc16
-rw-r--r--src/burn/test/BurnUnitTest/CacheTest.cpp95
-rw-r--r--src/burn/test/BurnUnitTest/precomp.h2
-rw-r--r--src/libs/dutil/WixToolset.DUtil/guidutil.cpp4
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/pathutil.h9
-rw-r--r--src/libs/dutil/WixToolset.DUtil/pathutil.cpp38
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp28
-rw-r--r--src/test/burn/WixTestTools/BundleVerifier.cs12
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/CacheTests.cs68
12 files changed, 338 insertions, 75 deletions
diff --git a/src/burn/engine/cache.cpp b/src/burn/engine/cache.cpp
index b311a195..7fca8cc7 100644
--- a/src/burn/engine/cache.cpp
+++ b/src/burn/engine/cache.cpp
@@ -14,10 +14,10 @@ static HRESULT CacheVerifyPayloadSignature(
14 __in_z LPCWSTR wzUnverifiedPayloadPath, 14 __in_z LPCWSTR wzUnverifiedPayloadPath,
15 __in HANDLE hFile 15 __in HANDLE hFile
16 ); 16 );
17static HRESULT CalculateBaseWorkingFolder( 17static HRESULT CalculatePotentialBaseWorkingFolders(
18 __in BURN_CACHE* pCache,
18 __in BURN_ENGINE_COMMAND* pInternalCommand, 19 __in BURN_ENGINE_COMMAND* pInternalCommand,
19 __in LPCWSTR wzAcquisitionFolder, 20 __in LPCWSTR wzAcquisitionFolder
20 __inout_z LPWSTR* psczBaseWorkingFolder
21 ); 21 );
22static HRESULT CalculateWorkingFolders( 22static HRESULT CalculateWorkingFolders(
23 __in BURN_CACHE* pCache, 23 __in BURN_CACHE* pCache,
@@ -321,8 +321,8 @@ extern "C" HRESULT CacheEnsureAcquisitionFolder(
321 hr = DirEnsureExists(pCache->sczAcquisitionFolder, NULL); 321 hr = DirEnsureExists(pCache->sczAcquisitionFolder, NULL);
322 ExitOnFailure(hr, "Failed create acquisition folder."); 322 ExitOnFailure(hr, "Failed create acquisition folder.");
323 323
324 // Best effort to ensure our working folder is not encrypted. 324 // Best effort to ensure our acquisition folder is not encrypted.
325 ::DecryptFileW(pCache->sczBaseWorkingFolder, 0); 325 ::DecryptFileW(pCache->sczAcquisitionFolder, 0);
326 326
327LExit: 327LExit:
328 return hr; 328 return hr;
@@ -336,9 +336,30 @@ extern "C" HRESULT CacheEnsureBaseWorkingFolder(
336 Assert(pCache->fInitializedCache); 336 Assert(pCache->fInitializedCache);
337 337
338 HRESULT hr = S_OK; 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 }
339 358
340 hr = DirEnsureExists(pCache->sczBaseWorkingFolder, NULL); 359 ExitOnNull(pCache->sczBaseWorkingFolder, hr, E_INVALIDSTATE, "No usable base working folder found.");
341 ExitOnFailure(hr, "Failed create working folder."); 360
361 pCache->fInitializedBaseWorkingFolder = TRUE;
362 }
342 363
343 // Best effort to ensure our working folder is not encrypted. 364 // Best effort to ensure our working folder is not encrypted.
344 ::DecryptFileW(pCache->sczBaseWorkingFolder, 0); 365 ::DecryptFileW(pCache->sczBaseWorkingFolder, 0);
@@ -350,6 +371,8 @@ extern "C" HRESULT CacheEnsureBaseWorkingFolder(
350 } 371 }
351 372
352LExit: 373LExit:
374 ReleaseStr(sczPotential);
375
353 return hr; 376 return hr;
354} 377}
355 378
@@ -360,6 +383,7 @@ extern "C" HRESULT CacheCalculateBundleWorkingPath(
360 ) 383 )
361{ 384{
362 Assert(pCache->fInitializedCache); 385 Assert(pCache->fInitializedCache);
386 Assert(pCache->fInitializedBaseWorkingFolder);
363 387
364 HRESULT hr = S_OK; 388 HRESULT hr = S_OK;
365 389
@@ -1180,7 +1204,7 @@ extern "C" HRESULT CacheRemoveBaseWorkingFolder(
1180{ 1204{
1181 HRESULT hr = S_OK; 1205 HRESULT hr = S_OK;
1182 1206
1183 if (pCache->fInitializedCacheSources) 1207 if (pCache->fInitializedBaseWorkingFolder)
1184 { 1208 {
1185 // Try to clean out everything in the working folder. 1209 // Try to clean out everything in the working folder.
1186 hr = DirEnsureDeleteEx(pCache->sczBaseWorkingFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); 1210 hr = DirEnsureDeleteEx(pCache->sczBaseWorkingFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
@@ -1343,70 +1367,78 @@ extern "C" void CacheUninitialize(
1343 __in BURN_CACHE* pCache 1367 __in BURN_CACHE* pCache
1344 ) 1368 )
1345{ 1369{
1346 ReleaseNullStr(pCache->sczCurrentMachinePackageCache); 1370 ReleaseStrArray(pCache->rgsczPotentialBaseWorkingFolders, pCache->cPotentialBaseWorkingFolders);
1347 ReleaseNullStr(pCache->sczDefaultMachinePackageCache); 1371 ReleaseStr(pCache->sczCurrentMachinePackageCache);
1348 ReleaseNullStr(pCache->sczDefaultUserPackageCache); 1372 ReleaseStr(pCache->sczDefaultMachinePackageCache);
1349 ReleaseNullStr(pCache->sczBaseWorkingFolder); 1373 ReleaseStr(pCache->sczDefaultUserPackageCache);
1350 ReleaseNullStr(pCache->sczAcquisitionFolder); 1374 ReleaseStr(pCache->sczBaseWorkingFolder);
1351 ReleaseNullStr(pCache->sczSourceProcessFolder); 1375 ReleaseStr(pCache->sczAcquisitionFolder);
1352 1376 ReleaseStr(pCache->sczSourceProcessFolder);
1353 pCache->fRunningFromCache = FALSE; 1377
1354 pCache->fInitializedCache = FALSE; 1378 memset(pCache, 0, sizeof(BURN_CACHE));
1355 pCache->fInitializedCacheSources = FALSE;
1356 pCache->fPerMachineCacheRootVerified = FALSE;
1357 pCache->fOriginalPerMachineCacheRootVerified = FALSE;
1358 pCache->fUnverifiedCacheFolderCreated = FALSE;
1359 pCache->fCustomMachinePackageCache = FALSE;
1360} 1379}
1361 1380
1362// Internal functions. 1381// Internal functions.
1363 1382
1364static HRESULT CalculateBaseWorkingFolder( 1383static HRESULT CalculatePotentialBaseWorkingFolders(
1384 __in BURN_CACHE* pCache,
1365 __in BURN_ENGINE_COMMAND* pInternalCommand, 1385 __in BURN_ENGINE_COMMAND* pInternalCommand,
1366 __in LPCWSTR wzAcquisitionFolder, 1386 __in LPCWSTR wzAcquisitionFolder
1367 __inout_z LPWSTR* psczBaseWorkingFolder
1368 ) 1387 )
1369{ 1388{
1389 Assert(!pCache->rgsczPotentialBaseWorkingFolders && !pCache->cPotentialBaseWorkingFolders);
1370 HRESULT hr = S_OK; 1390 HRESULT hr = S_OK;
1391 LPWSTR sczTemp = NULL;
1371 1392
1372 ReleaseNullStr(*psczBaseWorkingFolder); 1393 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pCache->rgsczPotentialBaseWorkingFolders), 6, sizeof(LPWSTR), 6);
1394 ExitOnFailure(hr, "Failed to initialize array.");
1373 1395
1374 // The value from the command line takes precedence. 1396 // The value from the command line takes precedence.
1375 if (pInternalCommand->sczEngineWorkingDirectory) 1397 if (pInternalCommand->sczEngineWorkingDirectory)
1376 { 1398 {
1377 hr = PathExpand(psczBaseWorkingFolder, pInternalCommand->sczEngineWorkingDirectory, PATH_EXPAND_FULLPATH); 1399 hr = PathExpand(&sczTemp, pInternalCommand->sczEngineWorkingDirectory, PATH_EXPAND_FULLPATH);
1378 ExitOnFailure(hr, "Failed to expand engine working directory from command-line: '%ls'", pInternalCommand->sczEngineWorkingDirectory); 1400 ExitOnFailure(hr, "Failed to expand engine working directory from command-line: '%ls'", pInternalCommand->sczEngineWorkingDirectory);
1379 1401
1380 ExitFunction(); 1402 pCache->rgsczPotentialBaseWorkingFolders[pCache->cPotentialBaseWorkingFolders] = sczTemp;
1403 sczTemp = NULL;
1404 ++pCache->cPotentialBaseWorkingFolders;
1381 } 1405 }
1382 1406
1383 // The base working folder can be specified through policy, 1407 // The base working folder can be specified through policy,
1384 // but only use it if elevated because it should be secured against non-admin users. 1408 // but only use it if elevated because it should be secured against non-admin users.
1385 if (pInternalCommand->fInitiallyElevated) 1409 if (pInternalCommand->fInitiallyElevated)
1386 { 1410 {
1387 hr = PolcReadString(POLICY_BURN_REGISTRY_PATH, L"EngineWorkingDirectory", NULL, psczBaseWorkingFolder); 1411 hr = PolcReadString(POLICY_BURN_REGISTRY_PATH, L"EngineWorkingDirectory", NULL, &sczTemp);
1388 ExitOnFailure(hr, "Failed to read EngineWorkingDirectory policy directory."); 1412 ExitOnFailure(hr, "Failed to read EngineWorkingDirectory policy directory.");
1389 1413
1390 if (*psczBaseWorkingFolder) 1414 if (sczTemp)
1391 { 1415 {
1392 // PolcReadString is supposed to automatically expand REG_EXPAND_SZ values. 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;
1394 } 1420 }
1395 } 1421 }
1396 1422
1397 // Default to the acquisition folder, but need to use system temp path for security reasons if running elevated. 1423 // Default to the acquisition folder, but need to use system temp path for security reasons if running elevated.
1398 if (pInternalCommand->fInitiallyElevated) 1424 if (pInternalCommand->fInitiallyElevated)
1399 { 1425 {
1400 hr = PathGetSystemTempPath(psczBaseWorkingFolder); 1426 hr = PathGetSystemTempPaths(&pCache->rgsczPotentialBaseWorkingFolders, &pCache->cPotentialBaseWorkingFolders);
1401 ExitOnFailure(hr, "Failed to get system temp folder path for base working folder."); 1427 ExitOnFailure(hr, "Failed to get system temp folder paths for base working folder.");
1402 } 1428 }
1403 else 1429 else
1404 { 1430 {
1405 hr = StrAllocString(psczBaseWorkingFolder, wzAcquisitionFolder, 0); 1431 hr = StrAllocString(&sczTemp, wzAcquisitionFolder, 0);
1406 ExitOnFailure(hr, "Failed to copy acquisition folder path for base working folder."); 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;
1407 } 1437 }
1408 1438
1409LExit: 1439LExit:
1440 ReleaseStr(sczTemp);
1441
1410 return hr; 1442 return hr;
1411} 1443}
1412 1444
@@ -1416,11 +1448,7 @@ static HRESULT CalculateWorkingFolders(
1416 ) 1448 )
1417{ 1449{
1418 HRESULT hr = S_OK; 1450 HRESULT hr = S_OK;
1419 RPC_STATUS rs = RPC_S_OK;
1420 LPWSTR sczBaseAcquisitionPath = NULL; 1451 LPWSTR sczBaseAcquisitionPath = NULL;
1421 LPWSTR sczTempPath = NULL;
1422 UUID guid = {};
1423 WCHAR wzGuid[39];
1424 1452
1425 hr = PathGetTempPath(&sczBaseAcquisitionPath); 1453 hr = PathGetTempPath(&sczBaseAcquisitionPath);
1426 ExitOnFailure(hr, "Failed to get temp folder path for acquisition folder base."); 1454 ExitOnFailure(hr, "Failed to get temp folder path for acquisition folder base.");
@@ -1428,31 +1456,20 @@ static HRESULT CalculateWorkingFolders(
1428 hr = PathBackslashTerminate(&sczBaseAcquisitionPath); 1456 hr = PathBackslashTerminate(&sczBaseAcquisitionPath);
1429 ExitOnFailure(hr, "Failed to backslashify base engine working directory."); 1457 ExitOnFailure(hr, "Failed to backslashify base engine working directory.");
1430 1458
1431 hr = CalculateBaseWorkingFolder(pInternalCommand, sczBaseAcquisitionPath, &sczTempPath); 1459 hr = CalculatePotentialBaseWorkingFolders(pCache, pInternalCommand, sczBaseAcquisitionPath);
1432 ExitOnFailure(hr, "Failed to get base engine working directory."); 1460 ExitOnFailure(hr, "Failed to get potential base engine working directories.");
1433
1434 hr = PathBackslashTerminate(&sczTempPath);
1435 ExitOnFailure(hr, "Failed to backslashify base engine working directory.");
1436 1461
1437 rs = ::UuidCreate(&guid); 1462 hr = GuidFixedCreate(pCache->wzGuid);
1438 hr = HRESULT_FROM_RPC(rs);
1439 ExitOnFailure(hr, "Failed to create working folder guid."); 1463 ExitOnFailure(hr, "Failed to create working folder guid.");
1440 1464
1441 if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) 1465 pCache->wzGuid[GUID_STRING_LENGTH - 1] = L'\\';
1442 { 1466 pCache->wzGuid[GUID_STRING_LENGTH] = L'\0';
1443 hr = E_OUTOFMEMORY;
1444 ExitOnRootFailure(hr, "Failed to convert working folder guid into string.");
1445 }
1446 1467
1447 hr = StrAllocFormatted(&pCache->sczAcquisitionFolder, L"%ls%ls\\", sczBaseAcquisitionPath, wzGuid); 1468 hr = PathConcatRelativeToBase(sczBaseAcquisitionPath, pCache->wzGuid, &pCache->sczAcquisitionFolder);
1448 ExitOnFailure(hr, "Failed to append random guid on to temp path for acquisition folder."); 1469 ExitOnFailure(hr, "Failed to append random guid on to temp path for acquisition folder.");
1449 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
1453LExit: 1471LExit:
1454 ReleaseStr(sczBaseAcquisitionPath); 1472 ReleaseStr(sczBaseAcquisitionPath);
1455 ReleaseStr(sczTempPath);
1456 1473
1457 return hr; 1474 return hr;
1458} 1475}
diff --git a/src/burn/engine/cache.h b/src/burn/engine/cache.h
index 8b038b99..9c698b4b 100644
--- a/src/burn/engine/cache.h
+++ b/src/burn/engine/cache.h
@@ -35,11 +35,18 @@ typedef struct _BURN_CACHE
35 LPWSTR sczDefaultMachinePackageCache; 35 LPWSTR sczDefaultMachinePackageCache;
36 LPWSTR sczCurrentMachinePackageCache; 36 LPWSTR sczCurrentMachinePackageCache;
37 37
38 WCHAR wzGuid[GUID_STRING_LENGTH + 1];
39 LPWSTR* rgsczPotentialBaseWorkingFolders;
40 DWORD cPotentialBaseWorkingFolders;
41
38 // Only valid after CacheInitializeSources 42 // Only valid after CacheInitializeSources
39 BOOL fInitializedCacheSources; 43 BOOL fInitializedCacheSources;
40 BOOL fRunningFromCache; 44 BOOL fRunningFromCache;
41 LPWSTR sczSourceProcessFolder; 45 LPWSTR sczSourceProcessFolder;
42 LPWSTR sczAcquisitionFolder; 46 LPWSTR sczAcquisitionFolder;
47
48 // Only valid after CacheEnsureBaseWorkingFolder
49 BOOL fInitializedBaseWorkingFolder;
43 LPWSTR sczBaseWorkingFolder; 50 LPWSTR sczBaseWorkingFolder;
44} BURN_CACHE; 51} BURN_CACHE;
45 52
diff --git a/src/burn/engine/engine.cpp b/src/burn/engine/engine.cpp
index a408ed4a..aca43438 100644
--- a/src/burn/engine/engine.cpp
+++ b/src/burn/engine/engine.cpp
@@ -97,6 +97,7 @@ extern "C" HRESULT EngineRun(
97 LPWSTR sczExePath = NULL; 97 LPWSTR sczExePath = NULL;
98 BOOL fRunUntrusted = FALSE; 98 BOOL fRunUntrusted = FALSE;
99 BOOL fRunNormal = FALSE; 99 BOOL fRunNormal = FALSE;
100 BOOL fRunRunOnce = FALSE;
100 BOOL fRestart = FALSE; 101 BOOL fRestart = FALSE;
101 102
102 BURN_ENGINE_STATE engineState = { }; 103 BURN_ENGINE_STATE engineState = { };
@@ -221,6 +222,8 @@ extern "C" HRESULT EngineRun(
221 break; 222 break;
222 223
223 case BURN_MODE_RUNONCE: 224 case BURN_MODE_RUNONCE:
225 fRunRunOnce = TRUE;
226
224 hr = RunRunOnce(&engineState, nCmdShow); 227 hr = RunRunOnce(&engineState, nCmdShow);
225 ExitOnFailure(hr, "Failed to run RunOnce mode."); 228 ExitOnFailure(hr, "Failed to run RunOnce mode.");
226 break; 229 break;
@@ -303,6 +306,10 @@ LExit:
303 { 306 {
304 LogId(REPORT_STANDARD, MSG_EXITING_CLEAN_ROOM, FAILED(hr) ? (int)hr : *pdwExitCode); 307 LogId(REPORT_STANDARD, MSG_EXITING_CLEAN_ROOM, FAILED(hr) ? (int)hr : *pdwExitCode);
305 } 308 }
309 else if (fRunRunOnce)
310 {
311 LogId(REPORT_STANDARD, MSG_EXITING_RUN_ONCE, FAILED(hr) ? (int)hr : *pdwExitCode);
312 }
306 313
307 if (fLogInitialized) 314 if (fLogInitialized)
308 { 315 {
diff --git a/src/burn/engine/engine.mc b/src/burn/engine/engine.mc
index 32473252..9a08fa3f 100644
--- a/src/burn/engine/engine.mc
+++ b/src/burn/engine/engine.mc
@@ -149,6 +149,20 @@ Language=English
149Exit code: 0x%1!x! 149Exit code: 0x%1!x!
150. 150.
151 151
152MessageId=18
153Severity=Success
154SymbolicName=MSG_EXITING_RUN_ONCE
155Language=English
156Exit code: 0x%1!x!
157.
158
159MessageId=19
160Severity=Warning
161SymbolicName=MSG_INVALID_BASE_WORKING_FOLDER
162Language=English
163Failed to use folder as base working folder: %2!ls!, encountered error: %1!ls!.
164.
165
152MessageId=51 166MessageId=51
153Severity=Error 167Severity=Error
154SymbolicName=MSG_FAILED_PARSE_CONDITION 168SymbolicName=MSG_FAILED_PARSE_CONDITION
@@ -811,7 +825,7 @@ MessageId=346
811Severity=Warning 825Severity=Warning
812SymbolicName=MSG_CACHE_RETRYING_PACKAGE 826SymbolicName=MSG_CACHE_RETRYING_PACKAGE
813Language=English 827Language=English
814Application requested retry of caching package: %1!ls!, encountered error: 0x%2!x!. Retrying... 828Application requested retry of caching package: %2!ls!, encountered error: %1!ls!. Retrying...
815. 829.
816 830
817MessageId=347 831MessageId=347
diff --git a/src/burn/test/BurnUnitTest/CacheTest.cpp b/src/burn/test/BurnUnitTest/CacheTest.cpp
index 6979ec1a..eb0b31ab 100644
--- a/src/burn/test/BurnUnitTest/CacheTest.cpp
+++ b/src/burn/test/BurnUnitTest/CacheTest.cpp
@@ -37,11 +37,100 @@ namespace Bootstrapper
37 using namespace System::IO; 37 using namespace System::IO;
38 using namespace Xunit; 38 using namespace Xunit;
39 39
40 public ref class CacheTest : BurnUnitTest 40 public ref class CacheTest : BurnUnitTest, IClassFixture<TestRegistryFixture^>
41 { 41 {
42 private:
43 TestRegistryFixture^ testRegistry;
42 public: 44 public:
43 CacheTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) 45 CacheTest(BurnTestFixture^ fixture, TestRegistryFixture^ registryFixture) : BurnUnitTest(fixture)
44 { 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);
45 } 134 }
46 135
47 [Fact] 136 [Fact]
@@ -93,6 +182,8 @@ namespace Bootstrapper
93 File::SetAttributes(filePath, FileAttributes::Normal); 182 File::SetAttributes(filePath, FileAttributes::Normal);
94 File::Delete(filePath); 183 File::Delete(filePath);
95 } 184 }
185
186 CacheUninitialize(&cache);
96 } 187 }
97 } 188 }
98 }; 189 };
diff --git a/src/burn/test/BurnUnitTest/precomp.h b/src/burn/test/BurnUnitTest/precomp.h
index 11e54284..92c8c4c0 100644
--- a/src/burn/test/BurnUnitTest/precomp.h
+++ b/src/burn/test/BurnUnitTest/precomp.h
@@ -19,9 +19,11 @@
19#include <buffutil.h> 19#include <buffutil.h>
20#include <dirutil.h> 20#include <dirutil.h>
21#include <fileutil.h> 21#include <fileutil.h>
22#include <guidutil.h>
22#include <logutil.h> 23#include <logutil.h>
23#include <memutil.h> 24#include <memutil.h>
24#include <pathutil.h> 25#include <pathutil.h>
26#include <polcutil.h>
25#include <regutil.h> 27#include <regutil.h>
26#include <resrutil.h> 28#include <resrutil.h>
27#include <shelutil.h> 29#include <shelutil.h>
diff --git a/src/libs/dutil/WixToolset.DUtil/guidutil.cpp b/src/libs/dutil/WixToolset.DUtil/guidutil.cpp
index 204c9af2..946c256f 100644
--- a/src/libs/dutil/WixToolset.DUtil/guidutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/guidutil.cpp
@@ -9,6 +9,7 @@
9#define GuidExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) 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__) 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__) 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__)
12#define GuidExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) 13#define GuidExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__)
13#define GuidExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_GUIDUTIL, p, x, e, s, __VA_ARGS__) 14#define GuidExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_GUIDUTIL, p, x, e, s, __VA_ARGS__)
14#define GuidExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, p, x, 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(
29 30
30 if (!::StringFromGUID2(guid, wzGuid, GUID_STRING_LENGTH)) 31 if (!::StringFromGUID2(guid, wzGuid, GUID_STRING_LENGTH))
31 { 32 {
32 hr = E_OUTOFMEMORY; 33 GuidExitWithRootFailure(hr, E_OUTOFMEMORY, "Failed to convert guid into string.");
33 GuidExitOnRootFailure(hr, "Failed to convert guid into string.");
34 } 34 }
35 35
36LExit: 36LExit:
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
index 727318f2..875cfafb 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
@@ -205,11 +205,12 @@ DAPI_(HRESULT) PathGetTempPath(
205 ); 205 );
206 206
207/******************************************************************* 207/*******************************************************************
208 PathGetSystemTempPath - returns the path to the system temp folder 208 PathGetSystemTempPaths - returns the paths to system temp folders
209 that is backslash terminated. 209 that are backslash terminated with higher preference first.
210*******************************************************************/ 210*******************************************************************/
211DAPI_(HRESULT) PathGetSystemTempPath( 211DAPI_(HRESULT) PathGetSystemTempPaths(
212 __out_z LPWSTR* psczSystemTempPath 212 __inout_z LPWSTR** prgsczSystemTempPaths,
213 __inout DWORD* pcSystemTempPaths
213 ); 214 );
214 215
215/******************************************************************* 216/*******************************************************************
diff --git a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
index 1ac76626..dc33e656 100644
--- a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
@@ -923,12 +923,14 @@ LExit:
923} 923}
924 924
925 925
926DAPI_(HRESULT) PathGetSystemTempPath( 926DAPI_(HRESULT) PathGetSystemTempPaths(
927 __out_z LPWSTR* psczSystemTempPath 927 __inout_z LPWSTR** prgsczSystemTempPaths,
928 __inout DWORD* pcSystemTempPaths
928 ) 929 )
929{ 930{
930 HRESULT hr = S_OK; 931 HRESULT hr = S_OK;
931 HKEY hKey = NULL; 932 HKEY hKey = NULL;
933 LPWSTR sczTemp = NULL;
932 WCHAR wzTempPath[MAX_PATH + 1] = { }; 934 WCHAR wzTempPath[MAX_PATH + 1] = { };
933 DWORD cch = 0; 935 DWORD cch = 0;
934 936
@@ -940,26 +942,36 @@ DAPI_(HRESULT) PathGetSystemTempPath(
940 942
941 // Follow documented precedence rules for TMP/TEMP from ::GetTempPath. 943 // Follow documented precedence rules for TMP/TEMP from ::GetTempPath.
942 // TODO: values will be expanded with the current environment variables instead of the system environment variables. 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);
944 if (E_FILENOTFOUND != hr) 946 if (E_FILENOTFOUND != hr)
945 { 947 {
946 PathExitOnFailure(hr, "Failed to get system TMP value."); 948 PathExitOnFailure(hr, "Failed to get system TMP value.");
947 949
948 hr = PathBackslashTerminate(psczSystemTempPath); 950 hr = PathBackslashTerminate(&sczTemp);
949 PathExitOnFailure(hr, "Failed to backslash terminate system TMP value."); 951 PathExitOnFailure(hr, "Failed to backslash terminate system TMP value.");
950 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;
952 } 959 }
953 960
954 hr = RegReadString(hKey, L"TEMP", psczSystemTempPath); 961 hr = RegReadString(hKey, L"TEMP", &sczTemp);
955 if (E_FILENOTFOUND != hr) 962 if (E_FILENOTFOUND != hr)
956 { 963 {
957 PathExitOnFailure(hr, "Failed to get system TEMP value."); 964 PathExitOnFailure(hr, "Failed to get system TEMP value.");
958 965
959 hr = PathBackslashTerminate(psczSystemTempPath); 966 hr = PathBackslashTerminate(&sczTemp);
960 PathExitOnFailure(hr, "Failed to backslash terminate system TEMP value."); 967 PathExitOnFailure(hr, "Failed to backslash terminate system TEMP value.");
961 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;
963 } 975 }
964 } 976 }
965 977
@@ -973,11 +985,19 @@ DAPI_(HRESULT) PathGetSystemTempPath(
973 PathExitWithRootFailure(hr, E_INSUFFICIENT_BUFFER, "Windows directory path too long."); 985 PathExitWithRootFailure(hr, E_INSUFFICIENT_BUFFER, "Windows directory path too long.");
974 } 986 }
975 987
976 hr = PathConcat(wzTempPath, L"TEMP\\", psczSystemTempPath); 988 hr = PathConcat(wzTempPath, L"TEMP\\", &sczTemp);
977 PathExitOnFailure(hr, "Failed to concat Temp directory on Windows directory path."); 989 PathExitOnFailure(hr, "Failed to concat Temp directory on Windows directory path.");
978 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
979LExit: 998LExit:
980 ReleaseRegKey(hKey); 999 ReleaseRegKey(hKey);
1000 ReleaseStr(sczTemp);
981 1001
982 return hr; 1002 return hr;
983} 1003}
diff --git a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
index d1d304d3..e9ef1047 100644
--- a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
+++ b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
@@ -800,6 +800,34 @@ namespace DutilTests
800 } 800 }
801 801
802 [Fact] 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]
803 void PathNormalizeSlashesFixedTest() 831 void PathNormalizeSlashesFixedTest()
804 { 832 {
805 HRESULT hr = S_OK; 833 HRESULT hr = S_OK;
diff --git a/src/test/burn/WixTestTools/BundleVerifier.cs b/src/test/burn/WixTestTools/BundleVerifier.cs
index 594b19aa..103171cd 100644
--- a/src/test/burn/WixTestTools/BundleVerifier.cs
+++ b/src/test/burn/WixTestTools/BundleVerifier.cs
@@ -15,7 +15,8 @@ namespace WixTestTools
15 public partial class BundleInstaller 15 public partial class BundleInstaller
16 { 16 {
17 public const string DependencyRegistryRoot = "Software\\Classes\\Installer\\Dependencies"; 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";
19 public const string PACKAGE_CACHE_FOLDER_NAME = "Package Cache"; 20 public const string PACKAGE_CACHE_FOLDER_NAME = "Package Cache";
20 21
21 public string BundlePdb { get; } 22 public string BundlePdb { get; }
@@ -35,12 +36,19 @@ namespace WixTestTools
35 return this.BundleSymbol; 36 return this.BundleSymbol;
36 } 37 }
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
38 public string GetPackageCachePathForCacheId(string cacheId, bool perMachine) 46 public string GetPackageCachePathForCacheId(string cacheId, bool perMachine)
39 { 47 {
40 string cachePath; 48 string cachePath;
41 if (perMachine) 49 if (perMachine)
42 { 50 {
43 using var policyKey = Registry.LocalMachine.OpenSubKey(FULL_BURN_POLICY_REGISTRY_PATH); 51 using var policyKey = Registry.LocalMachine.OpenSubKey(this.GetFullBurnPolicyRegistryPath());
44 var redirectedCachePath = policyKey?.GetValue("PackageCache") as string; 52 var redirectedCachePath = policyKey?.GetValue("PackageCache") as string;
45 cachePath = redirectedCachePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), PACKAGE_CACHE_FOLDER_NAME); 53 cachePath = redirectedCachePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), PACKAGE_CACHE_FOLDER_NAME);
46 } 54 }
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/CacheTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/CacheTests.cs
index 6c8250d9..f5a1cda8 100644
--- a/src/test/burn/WixToolsetTest.BurnE2E/CacheTests.cs
+++ b/src/test/burn/WixToolsetTest.BurnE2E/CacheTests.cs
@@ -5,6 +5,7 @@ namespace WixToolsetTest.BurnE2E
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.IO; 7 using System.IO;
8 using Microsoft.Win32;
8 using WixBuildTools.TestSupport; 9 using WixBuildTools.TestSupport;
9 using WixTestTools; 10 using WixTestTools;
10 using WixToolset.Mba.Core; 11 using WixToolset.Mba.Core;
@@ -201,5 +202,72 @@ namespace WixToolsetTest.BurnE2E
201 packageB.VerifyInstalled(true); 202 packageB.VerifyInstalled(true);
202 } 203 }
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 }
204 } 272 }
205} 273}