winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 1ac30f8509d01671aa4f69f559d3fbcd1da2f142
parent ebca6192efcb117aee92ab1ab21dd76db1076ce5
Author: yao-msft <50888816+yao-msft@users.noreply.github.com>
Date:   Tue, 10 Mar 2020 10:50:59 -0700

Add mark of the web for downloaded installer. (#49)

* Add mark of the web for downloaded installer.

* PR comments
Diffstat:
Msrc/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp | 8+++++---
Msrc/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h | 2+-
Msrc/AppInstallerCLITests/Downloader.cpp | 9+++++++++
Msrc/AppInstallerCommonCore/Downloader.cpp | 72+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/AppInstallerCommonCore/Public/AppInstallerDownloader.h | 3+++
5 files changed, 81 insertions(+), 13 deletions(-)

diff --git a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp @@ -28,7 +28,6 @@ namespace AppInstaller::Workflow std::bind(ExecuteInstaller, m_downloadedInstaller, installerArgs, - m_argsRef.Contains(ExecutionArgs::Type::Interactive), std::placeholders::_1)); if (!installResult) @@ -48,7 +47,7 @@ namespace AppInstaller::Workflow } } - std::optional<DWORD> ShellExecuteInstallerHandler::ExecuteInstaller(const std::filesystem::path& filePath, const std::string& args, bool interactive, IProgressCallback& progress) + std::optional<DWORD> ShellExecuteInstallerHandler::ExecuteInstaller(const std::filesystem::path& filePath, const std::string& args, IProgressCallback& progress) { AICLI_LOG(CLI, Info, << "Staring installer. Path: " << filePath); @@ -58,7 +57,9 @@ namespace AppInstaller::Workflow std::string filePathUTF8Str = Utility::ConvertToUTF8(filePath.c_str()); execInfo.lpFile = filePathUTF8Str.c_str(); execInfo.lpParameters = args.c_str(); - execInfo.nShow = interactive ? SW_SHOW : SW_HIDE; + // Some installer forces UI. Setting to SW_HIDE will hide installer UI and installation will hang forever. + // Verified setting to SW_SHOW does not hurt silent mode since no UI will be shown. + execInfo.nShow = SW_SHOW; if (!ShellExecuteExA(&execInfo) || !execInfo.hProcess) { return GetLastError(); @@ -194,6 +195,7 @@ namespace AppInstaller::Workflow break; } + // std::filesystem::rename() handles motw correctly if applicable. std::filesystem::rename(m_downloadedInstaller, renamedDownloadedInstaller); m_downloadedInstaller.assign(renamedDownloadedInstaller); diff --git a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h @@ -22,7 +22,7 @@ namespace AppInstaller::Workflow void Install() override; protected: - static std::optional<DWORD> ExecuteInstaller(const std::filesystem::path& filePath, const std::string& args, bool interactive, IProgressCallback& progress); + static std::optional<DWORD> ExecuteInstaller(const std::filesystem::path& filePath, const std::string& args, IProgressCallback& progress); // Construct the installer arg string from appropriate source(known args, manifest) according to command line args. // Token is not replaced with actual values yet. diff --git a/src/AppInstallerCLITests/Downloader.cpp b/src/AppInstallerCLITests/Downloader.cpp @@ -28,6 +28,15 @@ TEST_CASE("DownloadValidFileAndVerifyHash", "[Downloader]") resultHash.begin())); REQUIRE(std::filesystem::file_size(tempFile.GetPath()) > 0); + + // Verify motw content + std::filesystem::path motwFile(tempFile); + motwFile += ":Zone.Identifier:$data"; + std::ifstream motwStream(motwFile); + std::stringstream motwContent; + motwContent << motwStream.rdbuf(); + std::string motwContentStr = motwContent.str(); + REQUIRE(motwContentStr.find("ZoneId=3") != std::string::npos); } TEST_CASE("DownloadValidFileAndCancel", "[Downloader]") diff --git a/src/AppInstallerCommonCore/Downloader.cpp b/src/AppInstallerCommonCore/Downloader.cpp @@ -19,7 +19,7 @@ namespace AppInstaller::Utility { THROW_HR_IF(E_INVALIDARG, url.empty()); - AICLI_LOG(CLI, Info, << "Downloading from url: " << url); + AICLI_LOG(Core, Info, << "Downloading from url: " << url); wil::unique_hinternet session(InternetOpenA( "appinstaller-cli", @@ -50,11 +50,11 @@ namespace AppInstaller::Utility if (requestStatus != HTTP_STATUS_OK) { - AICLI_LOG(CLI, Error, << "Download request failed. Returned status: " << requestStatus); + AICLI_LOG(Core, Error, << "Download request failed. Returned status: " << requestStatus); THROW_HR_MSG(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, requestStatus), "Download request status is not success."); } - AICLI_LOG(CLI, Verbose, << "Download request status success."); + AICLI_LOG(Core, Verbose, << "Download request status success."); // Get content length. Don't fail the download if failed. LONGLONG contentLength = 0; @@ -66,7 +66,7 @@ namespace AppInstaller::Utility &contentLength, &cbContentLength, nullptr); - AICLI_LOG(CLI, Verbose, << "Download size: " << contentLength); + AICLI_LOG(Core, Verbose, << "Download size: " << contentLength); // Setup hash engine SHA256 hashEngine; @@ -83,7 +83,7 @@ namespace AppInstaller::Utility { if (progress.IsCancelled()) { - AICLI_LOG(CLI, Info, << "Download cancelled."); + AICLI_LOG(Core, Info, << "Download cancelled."); return {}; } @@ -113,10 +113,10 @@ namespace AppInstaller::Utility if (computeHash) { result = hashEngine.Get(); - AICLI_LOG(CLI, Info, << "Download hash: " << SHA256::ConvertToString(result)); + AICLI_LOG(Core, Info, << "Download hash: " << SHA256::ConvertToString(result)); } - AICLI_LOG(CLI, Info, << "Download completed."); + AICLI_LOG(Core, Info, << "Download completed."); return result; } @@ -130,11 +130,17 @@ namespace AppInstaller::Utility THROW_HR_IF(E_INVALIDARG, url.empty()); THROW_HR_IF(E_INVALIDARG, dest.empty()); - AICLI_LOG(CLI, Info, << "Downloading to path: " << dest); + AICLI_LOG(Core, Info, << "Downloading to path: " << dest); std::filesystem::create_directories(dest.parent_path()); - std::ofstream outfile(dest, std::ofstream::binary); + std::ofstream emptyDestFile(dest); + emptyDestFile.close(); + ApplyMotwIfApplicable(dest); + + // Use std::ofstream::app to append to previous empty file so that it will not + // create a new file and clear motw. + std::ofstream outfile(dest, std::ofstream::binary | std::ofstream::app); return DownloadToStream(url, outfile, progress, computeHash); } @@ -153,4 +159,52 @@ namespace AppInstaller::Utility return false; } + + void ApplyMotwIfApplicable(const std::filesystem::path& filePath) + { + AICLI_LOG(Core, Info, << "Started applying motw to " << filePath); + + { + // Check the file system the input file is on. + wil::unique_hfile fileHandle{ CreateFileW( + filePath.c_str(), /*lpFileName*/ + GENERIC_READ, /*dwDesiredAccess*/ + 0, /*dwShareMode*/ + NULL, /*lpSecurityAttributes*/ + OPEN_EXISTING, /*dwCreationDisposition*/ + FILE_ATTRIBUTE_NORMAL, /*dwFlagsAndAttributes*/ + NULL /*hTemplateFile*/) }; + + THROW_LAST_ERROR_IF(fileHandle.get() == INVALID_HANDLE_VALUE); + + wchar_t fileSystemName[MAX_PATH]; + THROW_LAST_ERROR_IF(!GetVolumeInformationByHandleW( + fileHandle.get(), /*hFile*/ + NULL, /*lpVolumeNameBuffer*/ + 0, /*nVolumeNameSize*/ + NULL, /*lpVolumeSerialNumber*/ + NULL, /*lpMaximumComponentLength*/ + NULL, /*lpFileSystemFlags*/ + fileSystemName, /*lpFileSystemNameBuffer*/ + MAX_PATH /*nFileSystemNameSize*/)); + + if (_wcsicmp(fileSystemName, L"NTFS") != 0) + { + AICLI_LOG(Core, Info, << "File system is not NTFS. Skipped applying motw"); + return; + } + } + + // Zone Indentifier stream name + // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/6e3f7352-d11c-4d76-8c39-2516a9df36e8 + std::filesystem::path motwPath(filePath); + motwPath += L":Zone.Identifier:$DATA"; + + // Apply mark of the web. ZoneId 3 means downloaded from internet. + std::ofstream motwStream(motwPath); + motwStream << "[ZoneTransfer]" << std::endl; + motwStream << "ZoneId=3" << std::endl; + + AICLI_LOG(Core, Info, << "Finished applying motw"); + } } diff --git a/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h b/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h @@ -34,4 +34,7 @@ namespace AppInstaller::Utility // Determines if the given url is a remote location. bool IsUrlRemote(std::string_view url); + + // Apply Mark of the web if the target file is on NTFS, otherwise does nothing. + void ApplyMotwIfApplicable(const std::filesystem::path& filePath); }