From 648f370f7966b2738c1446601057d888bbd2c70f Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 3 Jun 2022 17:48:57 -0500 Subject: Make PathGetSystemPath return an array of paths ordered by preference. --- src/burn/engine/cache.cpp | 127 ++++++++++++--------- src/burn/engine/cache.h | 7 ++ src/burn/engine/engine.cpp | 7 ++ src/burn/engine/engine.mc | 16 ++- src/burn/test/BurnUnitTest/CacheTest.cpp | 95 ++++++++++++++- src/burn/test/BurnUnitTest/precomp.h | 2 + src/libs/dutil/WixToolset.DUtil/guidutil.cpp | 4 +- src/libs/dutil/WixToolset.DUtil/inc/pathutil.h | 9 +- src/libs/dutil/WixToolset.DUtil/pathutil.cpp | 38 ++++-- src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp | 28 +++++ src/test/burn/WixTestTools/BundleVerifier.cs | 12 +- src/test/burn/WixToolsetTest.BurnE2E/CacheTests.cs | 68 +++++++++++ 12 files changed, 338 insertions(+), 75 deletions(-) (limited to 'src') 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( __in_z LPCWSTR wzUnverifiedPayloadPath, __in HANDLE hFile ); -static HRESULT CalculateBaseWorkingFolder( +static HRESULT CalculatePotentialBaseWorkingFolders( + __in BURN_CACHE* pCache, __in BURN_ENGINE_COMMAND* pInternalCommand, - __in LPCWSTR wzAcquisitionFolder, - __inout_z LPWSTR* psczBaseWorkingFolder + __in LPCWSTR wzAcquisitionFolder ); static HRESULT CalculateWorkingFolders( __in BURN_CACHE* pCache, @@ -321,8 +321,8 @@ extern "C" HRESULT CacheEnsureAcquisitionFolder( hr = DirEnsureExists(pCache->sczAcquisitionFolder, NULL); ExitOnFailure(hr, "Failed create acquisition folder."); - // Best effort to ensure our working folder is not encrypted. - ::DecryptFileW(pCache->sczBaseWorkingFolder, 0); + // Best effort to ensure our acquisition folder is not encrypted. + ::DecryptFileW(pCache->sczAcquisitionFolder, 0); LExit: return hr; @@ -336,9 +336,30 @@ extern "C" HRESULT CacheEnsureBaseWorkingFolder( Assert(pCache->fInitializedCache); HRESULT hr = S_OK; + LPWSTR sczPotential = NULL; + + if (!pCache->fInitializedBaseWorkingFolder) + { + for (DWORD i = 0; i < pCache->cPotentialBaseWorkingFolders; ++i) + { + hr = PathConcatRelativeToBase(pCache->rgsczPotentialBaseWorkingFolders[i], pCache->wzGuid, &sczPotential); + ExitOnFailure(hr, "Failed to append random guid on to potential path for working folder."); + + hr = DirEnsureExists(sczPotential, NULL); + if (SUCCEEDED(hr)) + { + pCache->sczBaseWorkingFolder = sczPotential; + sczPotential = NULL; + break; + } + + LogErrorId(hr, MSG_INVALID_BASE_WORKING_FOLDER, sczPotential, NULL, NULL); + } - hr = DirEnsureExists(pCache->sczBaseWorkingFolder, NULL); - ExitOnFailure(hr, "Failed create working folder."); + ExitOnNull(pCache->sczBaseWorkingFolder, hr, E_INVALIDSTATE, "No usable base working folder found."); + + pCache->fInitializedBaseWorkingFolder = TRUE; + } // Best effort to ensure our working folder is not encrypted. ::DecryptFileW(pCache->sczBaseWorkingFolder, 0); @@ -350,6 +371,8 @@ extern "C" HRESULT CacheEnsureBaseWorkingFolder( } LExit: + ReleaseStr(sczPotential); + return hr; } @@ -360,6 +383,7 @@ extern "C" HRESULT CacheCalculateBundleWorkingPath( ) { Assert(pCache->fInitializedCache); + Assert(pCache->fInitializedBaseWorkingFolder); HRESULT hr = S_OK; @@ -1180,7 +1204,7 @@ extern "C" HRESULT CacheRemoveBaseWorkingFolder( { HRESULT hr = S_OK; - if (pCache->fInitializedCacheSources) + if (pCache->fInitializedBaseWorkingFolder) { // Try to clean out everything in the working folder. hr = DirEnsureDeleteEx(pCache->sczBaseWorkingFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); @@ -1343,70 +1367,78 @@ extern "C" void CacheUninitialize( __in BURN_CACHE* pCache ) { - ReleaseNullStr(pCache->sczCurrentMachinePackageCache); - ReleaseNullStr(pCache->sczDefaultMachinePackageCache); - ReleaseNullStr(pCache->sczDefaultUserPackageCache); - ReleaseNullStr(pCache->sczBaseWorkingFolder); - ReleaseNullStr(pCache->sczAcquisitionFolder); - ReleaseNullStr(pCache->sczSourceProcessFolder); - - pCache->fRunningFromCache = FALSE; - pCache->fInitializedCache = FALSE; - pCache->fInitializedCacheSources = FALSE; - pCache->fPerMachineCacheRootVerified = FALSE; - pCache->fOriginalPerMachineCacheRootVerified = FALSE; - pCache->fUnverifiedCacheFolderCreated = FALSE; - pCache->fCustomMachinePackageCache = FALSE; + ReleaseStrArray(pCache->rgsczPotentialBaseWorkingFolders, pCache->cPotentialBaseWorkingFolders); + ReleaseStr(pCache->sczCurrentMachinePackageCache); + ReleaseStr(pCache->sczDefaultMachinePackageCache); + ReleaseStr(pCache->sczDefaultUserPackageCache); + ReleaseStr(pCache->sczBaseWorkingFolder); + ReleaseStr(pCache->sczAcquisitionFolder); + ReleaseStr(pCache->sczSourceProcessFolder); + + memset(pCache, 0, sizeof(BURN_CACHE)); } // Internal functions. -static HRESULT CalculateBaseWorkingFolder( +static HRESULT CalculatePotentialBaseWorkingFolders( + __in BURN_CACHE* pCache, __in BURN_ENGINE_COMMAND* pInternalCommand, - __in LPCWSTR wzAcquisitionFolder, - __inout_z LPWSTR* psczBaseWorkingFolder + __in LPCWSTR wzAcquisitionFolder ) { + Assert(!pCache->rgsczPotentialBaseWorkingFolders && !pCache->cPotentialBaseWorkingFolders); HRESULT hr = S_OK; + LPWSTR sczTemp = NULL; - ReleaseNullStr(*psczBaseWorkingFolder); + hr = MemEnsureArraySize(reinterpret_cast(&pCache->rgsczPotentialBaseWorkingFolders), 6, sizeof(LPWSTR), 6); + ExitOnFailure(hr, "Failed to initialize array."); // The value from the command line takes precedence. if (pInternalCommand->sczEngineWorkingDirectory) { - hr = PathExpand(psczBaseWorkingFolder, pInternalCommand->sczEngineWorkingDirectory, PATH_EXPAND_FULLPATH); + hr = PathExpand(&sczTemp, pInternalCommand->sczEngineWorkingDirectory, PATH_EXPAND_FULLPATH); ExitOnFailure(hr, "Failed to expand engine working directory from command-line: '%ls'", pInternalCommand->sczEngineWorkingDirectory); - ExitFunction(); + pCache->rgsczPotentialBaseWorkingFolders[pCache->cPotentialBaseWorkingFolders] = sczTemp; + sczTemp = NULL; + ++pCache->cPotentialBaseWorkingFolders; } // The base working folder can be specified through policy, // but only use it if elevated because it should be secured against non-admin users. if (pInternalCommand->fInitiallyElevated) { - hr = PolcReadString(POLICY_BURN_REGISTRY_PATH, L"EngineWorkingDirectory", NULL, psczBaseWorkingFolder); + hr = PolcReadString(POLICY_BURN_REGISTRY_PATH, L"EngineWorkingDirectory", NULL, &sczTemp); ExitOnFailure(hr, "Failed to read EngineWorkingDirectory policy directory."); - if (*psczBaseWorkingFolder) + if (sczTemp) { // PolcReadString is supposed to automatically expand REG_EXPAND_SZ values. - ExitFunction(); + pCache->rgsczPotentialBaseWorkingFolders[pCache->cPotentialBaseWorkingFolders] = sczTemp; + sczTemp = NULL; + ++pCache->cPotentialBaseWorkingFolders; } } // Default to the acquisition folder, but need to use system temp path for security reasons if running elevated. if (pInternalCommand->fInitiallyElevated) { - hr = PathGetSystemTempPath(psczBaseWorkingFolder); - ExitOnFailure(hr, "Failed to get system temp folder path for base working folder."); + hr = PathGetSystemTempPaths(&pCache->rgsczPotentialBaseWorkingFolders, &pCache->cPotentialBaseWorkingFolders); + ExitOnFailure(hr, "Failed to get system temp folder paths for base working folder."); } else { - hr = StrAllocString(psczBaseWorkingFolder, wzAcquisitionFolder, 0); + hr = StrAllocString(&sczTemp, wzAcquisitionFolder, 0); ExitOnFailure(hr, "Failed to copy acquisition folder path for base working folder."); + + pCache->rgsczPotentialBaseWorkingFolders[pCache->cPotentialBaseWorkingFolders] = sczTemp; + sczTemp = NULL; + ++pCache->cPotentialBaseWorkingFolders; } LExit: + ReleaseStr(sczTemp); + return hr; } @@ -1416,11 +1448,7 @@ static HRESULT CalculateWorkingFolders( ) { HRESULT hr = S_OK; - RPC_STATUS rs = RPC_S_OK; LPWSTR sczBaseAcquisitionPath = NULL; - LPWSTR sczTempPath = NULL; - UUID guid = {}; - WCHAR wzGuid[39]; hr = PathGetTempPath(&sczBaseAcquisitionPath); ExitOnFailure(hr, "Failed to get temp folder path for acquisition folder base."); @@ -1428,31 +1456,20 @@ static HRESULT CalculateWorkingFolders( hr = PathBackslashTerminate(&sczBaseAcquisitionPath); ExitOnFailure(hr, "Failed to backslashify base engine working directory."); - hr = CalculateBaseWorkingFolder(pInternalCommand, sczBaseAcquisitionPath, &sczTempPath); - ExitOnFailure(hr, "Failed to get base engine working directory."); - - hr = PathBackslashTerminate(&sczTempPath); - ExitOnFailure(hr, "Failed to backslashify base engine working directory."); + hr = CalculatePotentialBaseWorkingFolders(pCache, pInternalCommand, sczBaseAcquisitionPath); + ExitOnFailure(hr, "Failed to get potential base engine working directories."); - rs = ::UuidCreate(&guid); - hr = HRESULT_FROM_RPC(rs); + hr = GuidFixedCreate(pCache->wzGuid); ExitOnFailure(hr, "Failed to create working folder guid."); - if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) - { - hr = E_OUTOFMEMORY; - ExitOnRootFailure(hr, "Failed to convert working folder guid into string."); - } + pCache->wzGuid[GUID_STRING_LENGTH - 1] = L'\\'; + pCache->wzGuid[GUID_STRING_LENGTH] = L'\0'; - hr = StrAllocFormatted(&pCache->sczAcquisitionFolder, L"%ls%ls\\", sczBaseAcquisitionPath, wzGuid); + hr = PathConcatRelativeToBase(sczBaseAcquisitionPath, pCache->wzGuid, &pCache->sczAcquisitionFolder); ExitOnFailure(hr, "Failed to append random guid on to temp path for acquisition folder."); - hr = StrAllocFormatted(&pCache->sczBaseWorkingFolder, L"%ls%ls\\", sczTempPath, wzGuid); - ExitOnFailure(hr, "Failed to append random guid on to temp path for working folder."); - LExit: ReleaseStr(sczBaseAcquisitionPath); - ReleaseStr(sczTempPath); return hr; } 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 LPWSTR sczDefaultMachinePackageCache; LPWSTR sczCurrentMachinePackageCache; + WCHAR wzGuid[GUID_STRING_LENGTH + 1]; + LPWSTR* rgsczPotentialBaseWorkingFolders; + DWORD cPotentialBaseWorkingFolders; + // Only valid after CacheInitializeSources BOOL fInitializedCacheSources; BOOL fRunningFromCache; LPWSTR sczSourceProcessFolder; LPWSTR sczAcquisitionFolder; + + // Only valid after CacheEnsureBaseWorkingFolder + BOOL fInitializedBaseWorkingFolder; LPWSTR sczBaseWorkingFolder; } BURN_CACHE; 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( LPWSTR sczExePath = NULL; BOOL fRunUntrusted = FALSE; BOOL fRunNormal = FALSE; + BOOL fRunRunOnce = FALSE; BOOL fRestart = FALSE; BURN_ENGINE_STATE engineState = { }; @@ -221,6 +222,8 @@ extern "C" HRESULT EngineRun( break; case BURN_MODE_RUNONCE: + fRunRunOnce = TRUE; + hr = RunRunOnce(&engineState, nCmdShow); ExitOnFailure(hr, "Failed to run RunOnce mode."); break; @@ -303,6 +306,10 @@ LExit: { LogId(REPORT_STANDARD, MSG_EXITING_CLEAN_ROOM, FAILED(hr) ? (int)hr : *pdwExitCode); } + else if (fRunRunOnce) + { + LogId(REPORT_STANDARD, MSG_EXITING_RUN_ONCE, FAILED(hr) ? (int)hr : *pdwExitCode); + } if (fLogInitialized) { 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 Exit code: 0x%1!x! . +MessageId=18 +Severity=Success +SymbolicName=MSG_EXITING_RUN_ONCE +Language=English +Exit code: 0x%1!x! +. + +MessageId=19 +Severity=Warning +SymbolicName=MSG_INVALID_BASE_WORKING_FOLDER +Language=English +Failed to use folder as base working folder: %2!ls!, encountered error: %1!ls!. +. + MessageId=51 Severity=Error SymbolicName=MSG_FAILED_PARSE_CONDITION @@ -811,7 +825,7 @@ MessageId=346 Severity=Warning SymbolicName=MSG_CACHE_RETRYING_PACKAGE Language=English -Application requested retry of caching package: %1!ls!, encountered error: 0x%2!x!. Retrying... +Application requested retry of caching package: %2!ls!, encountered error: %1!ls!. Retrying... . MessageId=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 using namespace System::IO; using namespace Xunit; - public ref class CacheTest : BurnUnitTest + public ref class CacheTest : BurnUnitTest, IClassFixture { + private: + TestRegistryFixture^ testRegistry; public: - CacheTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + CacheTest(BurnTestFixture^ fixture, TestRegistryFixture^ registryFixture) : BurnUnitTest(fixture) { + this->testRegistry = registryFixture; + } + + [Fact] + void CacheElevatedTempFallbacksTest() + { + HRESULT hr = S_OK; + BURN_CACHE cache = { }; + BURN_ENGINE_COMMAND internalCommand = { }; + HKEY hkSystemEnvironment = NULL; + HKEY hkBurnPolicy = NULL; + + internalCommand.fInitiallyElevated = TRUE; + + try + { + this->testRegistry->SetUp(); + + // No registry keys, so should fallback to %windir%\TEMP. + hr = CacheInitialize(&cache, &internalCommand); + NativeAssert::Succeeded(hr, "Failed to initialize cache."); + Assert::NotEqual(0, cache.cPotentialBaseWorkingFolders); + VerifyBaseWorkingFolder(L"%windir%\\TEMP\\", cache.rgsczPotentialBaseWorkingFolders[0]); + CacheUninitialize(&cache); + + hr = RegCreate(HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\Session Manager\\Environment", GENERIC_WRITE, &hkSystemEnvironment); + NativeAssert::Succeeded(hr, "Failed to create system environment key."); + + // Third fallback is system-level %TEMP%. + hr = RegWriteExpandString(hkSystemEnvironment, L"TEMP", L"A:\\TEST\\TEMP"); + NativeAssert::Succeeded(hr, "Failed to write TEMP system environment value."); + + hr = CacheInitialize(&cache, &internalCommand); + NativeAssert::Succeeded(hr, "Failed to initialize cache."); + Assert::NotEqual(0, cache.cPotentialBaseWorkingFolders); + VerifyBaseWorkingFolder(L"A:\\TEST\\TEMP\\", cache.rgsczPotentialBaseWorkingFolders[0]); + CacheUninitialize(&cache); + + // Second fallback is system-level %TMP%. + hr = RegWriteExpandString(hkSystemEnvironment, L"TMP", L"B:\\TEST\\TMP\\"); + NativeAssert::Succeeded(hr, "Failed to write TEMP system environment value."); + + hr = CacheInitialize(&cache, &internalCommand); + NativeAssert::Succeeded(hr, "Failed to initialize cache."); + Assert::NotEqual(0, cache.cPotentialBaseWorkingFolders); + VerifyBaseWorkingFolder(L"B:\\TEST\\TMP\\", cache.rgsczPotentialBaseWorkingFolders[0]); + CacheUninitialize(&cache); + + hr = RegCreate(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Policies\\WiX\\Burn", GENERIC_WRITE, &hkBurnPolicy); + NativeAssert::Succeeded(hr, "Failed to create Burn policy key."); + + // Default source is Burn policy. + hr = RegWriteExpandString(hkBurnPolicy, L"EngineWorkingDirectory", L"D:\\TEST\\POLICY\\"); + NativeAssert::Succeeded(hr, "Failed to write EngineWorkingDirectory Burn policy value."); + + hr = CacheInitialize(&cache, &internalCommand); + NativeAssert::Succeeded(hr, "Failed to initialize cache."); + Assert::NotEqual(0, cache.cPotentialBaseWorkingFolders); + VerifyBaseWorkingFolder(L"D:\\TEST\\POLICY\\", cache.rgsczPotentialBaseWorkingFolders[0]); + CacheUninitialize(&cache); + + // Command line parameter overrides everything else. + hr = StrAllocString(&internalCommand.sczEngineWorkingDirectory, L"E:\\TEST\\COMMANDLINE\\", 0); + NativeAssert::Succeeded(hr, "Failed to copy command line working directory."); + + hr = CacheInitialize(&cache, &internalCommand); + NativeAssert::Succeeded(hr, "Failed to initialize cache."); + Assert::NotEqual(0, cache.cPotentialBaseWorkingFolders); + VerifyBaseWorkingFolder(L"E:\\TEST\\COMMANDLINE\\", cache.rgsczPotentialBaseWorkingFolders[0]); + CacheUninitialize(&cache); + } + finally + { + ReleaseRegKey(hkBurnPolicy); + ReleaseRegKey(hkSystemEnvironment); + ReleaseStr(internalCommand.sczEngineWorkingDirectory); + + CacheUninitialize(&cache); + + this->testRegistry->TearDown(); + } + } + + void VerifyBaseWorkingFolder(LPCWSTR wzExpectedUnexpanded, LPCWSTR wzActual) + { + String^ expected = Environment::ExpandEnvironmentVariables(gcnew String(wzExpectedUnexpanded)); + WixAssert::StringEqual(expected, gcnew String(wzActual), true); } [Fact] @@ -93,6 +182,8 @@ namespace Bootstrapper File::SetAttributes(filePath, FileAttributes::Normal); File::Delete(filePath); } + + CacheUninitialize(&cache); } } }; 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 @@ #include #include #include +#include #include #include #include +#include #include #include #include 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 @@ #define GuidExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) #define GuidExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) #define GuidExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitWithRootFailure(x, e, s, ...) ExitWithRootFailureSource(DUTIL_SOURCE_GUIDUTIL, x, e, s, __VA_ARGS__) #define GuidExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) #define GuidExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_GUIDUTIL, p, x, e, s, __VA_ARGS__) #define GuidExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, p, x, s, __VA_ARGS__) @@ -29,8 +30,7 @@ extern "C" HRESULT DAPI GuidFixedCreate( if (!::StringFromGUID2(guid, wzGuid, GUID_STRING_LENGTH)) { - hr = E_OUTOFMEMORY; - GuidExitOnRootFailure(hr, "Failed to convert guid into string."); + GuidExitWithRootFailure(hr, E_OUTOFMEMORY, "Failed to convert guid into string."); } LExit: 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( ); /******************************************************************* - PathGetSystemTempPath - returns the path to the system temp folder - that is backslash terminated. + PathGetSystemTempPaths - returns the paths to system temp folders + that are backslash terminated with higher preference first. *******************************************************************/ -DAPI_(HRESULT) PathGetSystemTempPath( - __out_z LPWSTR* psczSystemTempPath +DAPI_(HRESULT) PathGetSystemTempPaths( + __inout_z LPWSTR** prgsczSystemTempPaths, + __inout DWORD* pcSystemTempPaths ); /******************************************************************* 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: } -DAPI_(HRESULT) PathGetSystemTempPath( - __out_z LPWSTR* psczSystemTempPath +DAPI_(HRESULT) PathGetSystemTempPaths( + __inout_z LPWSTR** prgsczSystemTempPaths, + __inout DWORD* pcSystemTempPaths ) { HRESULT hr = S_OK; HKEY hKey = NULL; + LPWSTR sczTemp = NULL; WCHAR wzTempPath[MAX_PATH + 1] = { }; DWORD cch = 0; @@ -940,26 +942,36 @@ DAPI_(HRESULT) PathGetSystemTempPath( // Follow documented precedence rules for TMP/TEMP from ::GetTempPath. // TODO: values will be expanded with the current environment variables instead of the system environment variables. - hr = RegReadString(hKey, L"TMP", psczSystemTempPath); + hr = RegReadString(hKey, L"TMP", &sczTemp); if (E_FILENOTFOUND != hr) { PathExitOnFailure(hr, "Failed to get system TMP value."); - hr = PathBackslashTerminate(psczSystemTempPath); + hr = PathBackslashTerminate(&sczTemp); PathExitOnFailure(hr, "Failed to backslash terminate system TMP value."); - ExitFunction(); + hr = MemEnsureArraySizeForNewItems(reinterpret_cast(prgsczSystemTempPaths), *pcSystemTempPaths, 1, sizeof(LPWSTR), 3); + PathExitOnFailure(hr, "Failed to ensure array size for system TMP value."); + + (*prgsczSystemTempPaths)[*pcSystemTempPaths] = sczTemp; + sczTemp = NULL; + *pcSystemTempPaths += 1; } - hr = RegReadString(hKey, L"TEMP", psczSystemTempPath); + hr = RegReadString(hKey, L"TEMP", &sczTemp); if (E_FILENOTFOUND != hr) { PathExitOnFailure(hr, "Failed to get system TEMP value."); - hr = PathBackslashTerminate(psczSystemTempPath); + hr = PathBackslashTerminate(&sczTemp); PathExitOnFailure(hr, "Failed to backslash terminate system TEMP value."); - ExitFunction(); + hr = MemEnsureArraySizeForNewItems(reinterpret_cast(prgsczSystemTempPaths), *pcSystemTempPaths, 1, sizeof(LPWSTR), 2); + PathExitOnFailure(hr, "Failed to ensure array size for system TEMP value."); + + (*prgsczSystemTempPaths)[*pcSystemTempPaths] = sczTemp; + sczTemp = NULL; + *pcSystemTempPaths += 1; } } @@ -973,11 +985,19 @@ DAPI_(HRESULT) PathGetSystemTempPath( PathExitWithRootFailure(hr, E_INSUFFICIENT_BUFFER, "Windows directory path too long."); } - hr = PathConcat(wzTempPath, L"TEMP\\", psczSystemTempPath); + hr = PathConcat(wzTempPath, L"TEMP\\", &sczTemp); PathExitOnFailure(hr, "Failed to concat Temp directory on Windows directory path."); + hr = MemEnsureArraySizeForNewItems(reinterpret_cast(prgsczSystemTempPaths), *pcSystemTempPaths, 1, sizeof(LPWSTR), 1); + PathExitOnFailure(hr, "Failed to ensure array size for Windows\\TEMP value."); + + (*prgsczSystemTempPaths)[*pcSystemTempPaths] = sczTemp; + sczTemp = NULL; + *pcSystemTempPaths += 1; + LExit: ReleaseRegKey(hKey); + ReleaseStr(sczTemp); return hr; } 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 @@ -799,6 +799,34 @@ namespace DutilTests } } + [Fact] + void PathGetSystemTempPathsTest() + { + HRESULT hr = S_OK; + LPWSTR* rgsczPaths = NULL; + DWORD cPaths = 0; + DWORD cPathsOriginal = 0; + + try + { + hr = PathGetSystemTempPaths(&rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "PathGetSystemTempPaths failed."); + + Assert::InRange(cPaths, 1, 3); + WixAssert::StringEqual(Environment::ExpandEnvironmentVariables("%windir%\\temp\\"), gcnew String(rgsczPaths[cPaths - 1]), true); + + cPathsOriginal = cPaths; + + hr = PathGetSystemTempPaths(&rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "PathGetSystemTempPaths failed."); + Assert::Equal(cPathsOriginal * 2, cPaths); + } + finally + { + ReleaseStrArray(rgsczPaths, cPaths); + } + } + [Fact] void PathNormalizeSlashesFixedTest() { 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 public partial class BundleInstaller { public const string DependencyRegistryRoot = "Software\\Classes\\Installer\\Dependencies"; - public const string FULL_BURN_POLICY_REGISTRY_PATH = "SOFTWARE\\WOW6432Node\\Policies\\WiX\\Burn"; + public const string FULL_BURN_POLICY_REGISTRY_PATH = "SOFTWARE\\Policies\\WiX\\Burn"; + public const string FULL_BURN_POLICY_REGISTRY_PATH_WOW6432NODE = "SOFTWARE\\WOW6432Node\\Policies\\WiX\\Burn"; public const string PACKAGE_CACHE_FOLDER_NAME = "Package Cache"; public string BundlePdb { get; } @@ -35,12 +36,19 @@ namespace WixTestTools return this.BundleSymbol; } + public string GetFullBurnPolicyRegistryPath() + { + var bundleSymbol = this.GetBundleSymbol(); + var x64 = bundleSymbol.Platform != Platform.X86; + return x64 ? FULL_BURN_POLICY_REGISTRY_PATH : FULL_BURN_POLICY_REGISTRY_PATH_WOW6432NODE; + } + public string GetPackageCachePathForCacheId(string cacheId, bool perMachine) { string cachePath; if (perMachine) { - using var policyKey = Registry.LocalMachine.OpenSubKey(FULL_BURN_POLICY_REGISTRY_PATH); + using var policyKey = Registry.LocalMachine.OpenSubKey(this.GetFullBurnPolicyRegistryPath()); var redirectedCachePath = policyKey?.GetValue("PackageCache") as string; cachePath = redirectedCachePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), PACKAGE_CACHE_FOLDER_NAME); } 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 using System; using System.Collections.Generic; using System.IO; + using Microsoft.Win32; using WixBuildTools.TestSupport; using WixTestTools; using WixToolset.Mba.Core; @@ -201,5 +202,72 @@ namespace WixToolsetTest.BurnE2E packageB.VerifyInstalled(true); } } + + [RuntimeFact] + public void CanGetEngineWorkingDirectoryFromCommandLine() + { + var bundleA = this.CreateBundleInstaller("BundleA"); + var testBAController = this.CreateTestBAController(); + + testBAController.SetImmediatelyQuit(); + + using (var dfs = new DisposableFileSystem()) + { + var baseTempPath = dfs.GetFolder(true); + var logPath = bundleA.Install(0, $"-burn.engine.working.directory=\"{baseTempPath}\""); + LogVerifier.MessageInLogFileRegex(logPath, $"Burn x86 v4.*, Windows v.* \\(Build .*: Service Pack .*\\), path: {baseTempPath.Replace("\\", "\\\\")}\\\\.*\\\\.cr\\\\BundleA.exe"); + } + } + + [RuntimeFact] + public void CanGetEngineWorkingDirectoryFromPolicy() + { + var deletePolicyKey = false; + string originalPolicyValue = null; + + var bundleA = this.CreateBundleInstaller("BundleA"); + var testBAController = this.CreateTestBAController(); + var policyPath = bundleA.GetFullBurnPolicyRegistryPath(); + + testBAController.SetImmediatelyQuit(); + + try + { + using (var dfs = new DisposableFileSystem()) + { + var baseTempPath = dfs.GetFolder(true); + + var policyKey = Registry.LocalMachine.OpenSubKey(policyPath, writable: true); + if (policyKey == null) + { + policyKey = Registry.LocalMachine.CreateSubKey(policyPath, writable: true); + deletePolicyKey = true; + } + + using (policyKey) + { + originalPolicyValue = policyKey.GetValue("EngineWorkingDirectory") as string; + policyKey.SetValue("EngineWorkingDirectory", baseTempPath); + } + + var logPath = bundleA.Install(); + LogVerifier.MessageInLogFileRegex(logPath, $"Burn x86 v4.*, Windows v.* \\(Build .*: Service Pack .*\\), path: {baseTempPath.Replace("\\", "\\\\")}\\\\.*\\\\.cr\\\\BundleA.exe"); + } + } + finally + { + if (deletePolicyKey) + { + Registry.LocalMachine.DeleteSubKeyTree(policyPath); + } + else if (originalPolicyValue != null) + { + using (var policyKey = Registry.LocalMachine.CreateSubKey(policyPath, writable: true)) + { + policyKey.SetValue("EngineWorkingDirectory", originalPolicyValue); + } + } + } + } } } -- cgit v1.2.3-55-g6feb