diff --git a/src/windows/common/wslutil.cpp b/src/windows/common/wslutil.cpp index 03decb3e1..f43f7450e 100644 --- a/src/windows/common/wslutil.cpp +++ b/src/windows/common/wslutil.cpp @@ -368,8 +368,20 @@ std::wstring wsl::windows::common::wslutil::DownloadFileImpl( Filename = Url.substr(lastSlash + 1); } - const auto downloadFolder = - winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(std::filesystem::temp_directory_path().wstring()).get(); + // GetFolderFromPathAsync does not accept hidden or system path. + // To avoid the temp path having those attributes (mainly hidden), + // we need to create a subfolder without those attributes. + auto downloadFolderPath = std::filesystem::temp_directory_path() / L"wsl-downloads"; + std::filesystem::create_directories(downloadFolderPath); + + const auto attributes = GetFileAttributesW(downloadFolderPath.c_str()); + THROW_LAST_ERROR_IF(attributes == INVALID_FILE_ATTRIBUTES); + if (attributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) + { + THROW_IF_WIN32_BOOL_FALSE(SetFileAttributesW(downloadFolderPath.c_str(), attributes & ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM))); + } + + const auto downloadFolder = winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(downloadFolderPath.wstring()).get(); const auto file = downloadFolder.CreateFileAsync(Filename, winrt::Windows::Storage::CreationCollisionOption::GenerateUniqueName).get(); diff --git a/test/windows/Common.cpp b/test/windows/Common.cpp index f5aa3b1a0..fedb76f93 100644 --- a/test/windows/Common.cpp +++ b/test/windows/Common.cpp @@ -2474,14 +2474,32 @@ void Trim(std::wstring& string) std::erase_if(string, [](auto c) { return !isalnum(c); }); } -ScopedEnvVariable::ScopedEnvVariable(const std::wstring& Name, const std::wstring& Value) : m_name(Name) +ScopedEnvVariable::ScopedEnvVariable(const std::wstring& Name, const std::wstring& Value, bool restore) : m_name(Name) { + if (restore) + { + std::wstring value; + const auto result = wil::GetEnvironmentVariableW(Name.c_str(), value); + if (result != HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND)) + { + VERIFY_SUCCEEDED(result); + m_originalValue = std::move(value); + } + } + VERIFY_IS_TRUE(SetEnvironmentVariable(Name.c_str(), Value.c_str())); } ScopedEnvVariable::~ScopedEnvVariable() { - VERIFY_IS_TRUE(SetEnvironmentVariable(m_name.c_str(), nullptr)); + if (m_originalValue.has_value()) + { + VERIFY_IS_TRUE(SetEnvironmentVariable(m_name.c_str(), m_originalValue->c_str())); + } + else + { + VERIFY_IS_TRUE(SetEnvironmentVariable(m_name.c_str(), nullptr)); + } } UniqueWebServer::UniqueWebServer(LPCWSTR Endpoint, LPCWSTR Content) diff --git a/test/windows/Common.h b/test/windows/Common.h index ad2706ebf..074699166 100644 --- a/test/windows/Common.h +++ b/test/windows/Common.h @@ -309,16 +309,15 @@ class RegistryKeyChange class ScopedEnvVariable { public: - ScopedEnvVariable(const std::wstring& Name, const std::wstring& Value); + ScopedEnvVariable(const std::wstring& Name, const std::wstring& Value, bool restore = false); ~ScopedEnvVariable(); - ScopedEnvVariable(const WslConfigChange&) = delete; - ScopedEnvVariable(WslConfigChange&&) = delete; - const ScopedEnvVariable& operator=(ScopedEnvVariable&&) = delete; - const ScopedEnvVariable& operator=(ScopedEnvVariable&) = delete; + NON_COPYABLE(ScopedEnvVariable); + NON_MOVABLE(ScopedEnvVariable); private: std::wstring m_name; + std::optional m_originalValue{std::nullopt}; }; class UniqueWebServer diff --git a/test/windows/UnitTests.cpp b/test/windows/UnitTests.cpp index b9d54b369..a0c46530f 100644 --- a/test/windows/UnitTests.cpp +++ b/test/windows/UnitTests.cpp @@ -5143,7 +5143,7 @@ Error code: Wsl/Service/RegisterDistro/E_INVALIDARG\r\n"; wsl::shared::string::MultiByteToWide("01:23:45:67:89:AB")); } - TEST_METHOD(ModernDistroInstall) + static void ModernDistroInstallImpl() { auto tarPath = "file://" + wsl::shared::string::WideToMultiByte(EscapePath(g_testDistroPath)); @@ -5779,6 +5779,31 @@ Error code: Wsl/InstallDistro/WSL_E_INVALID_JSON\r\n", } } + TEST_METHOD(ModernDistroInstall) + { + ModernDistroInstallImpl(); + } + + TEST_METHOD(ModernDistroInstallWithHiddenTempFolder) + { + // Avoid contaminating the real temp folder. + const auto testTempFolder = std::filesystem::temp_directory_path() / L"wsl-install-test"; + std::filesystem::create_directories(testTempFolder); + auto cleanupTempFolder = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { + std::error_code error; + std::filesystem::remove_all(testTempFolder, error); + }); + + const auto originalAttributes = GetFileAttributesW(testTempFolder.c_str()); + VERIFY_IS_TRUE(originalAttributes != INVALID_FILE_ATTRIBUTES); + VERIFY_IS_TRUE(SetFileAttributesW(testTempFolder.c_str(), originalAttributes | FILE_ATTRIBUTE_HIDDEN)); + + ScopedEnvVariable temp(L"TEMP", testTempFolder.wstring(), true); + ScopedEnvVariable tmp(L"TMP", testTempFolder.wstring(), true); + + ModernDistroInstallImpl(); + } + TEST_METHOD(ModernInstallEndToEnd) { constexpr auto tarName = L"end2end.tar";