From ea3d18595a610ee07b03f07af4f03cf75b5ab420 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Wed, 29 Nov 2017 14:08:08 -0800 Subject: Improved cabinet handling --- src/wixnative/enumcab.cpp | 47 ++++++++++++ src/wixnative/extractcab.cpp | 50 +++++++++++++ src/wixnative/packages.config | 5 ++ src/wixnative/precomp.cpp | 3 + src/wixnative/precomp.h | 19 +++++ src/wixnative/resetacls.cpp | 51 +++++++++++++ src/wixnative/smartcab.cpp | 157 ++++++++++++++++++++++++++++++++++++++++ src/wixnative/wixnative.cpp | 38 ++++++++++ src/wixnative/wixnative.vcxproj | 80 ++++++++++++++++++++ 9 files changed, 450 insertions(+) create mode 100644 src/wixnative/enumcab.cpp create mode 100644 src/wixnative/extractcab.cpp create mode 100644 src/wixnative/packages.config create mode 100644 src/wixnative/precomp.cpp create mode 100644 src/wixnative/precomp.h create mode 100644 src/wixnative/resetacls.cpp create mode 100644 src/wixnative/smartcab.cpp create mode 100644 src/wixnative/wixnative.cpp create mode 100644 src/wixnative/wixnative.vcxproj (limited to 'src/wixnative') diff --git a/src/wixnative/enumcab.cpp b/src/wixnative/enumcab.cpp new file mode 100644 index 00000000..e7717bac --- /dev/null +++ b/src/wixnative/enumcab.cpp @@ -0,0 +1,47 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static INT_PTR __stdcall EnumCallback(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin); + + +HRESULT EnumCabCommand( + __in int argc, + __in LPWSTR argv[] +) +{ + HRESULT hr = E_INVALIDARG; + LPCWSTR wzCabPath = NULL; + + if (argc < 1) + { + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Must specify: cabPath outputFolder"); + } + + wzCabPath = argv[0]; + + hr = CabInitialize(FALSE); + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to initialize cabinet: %ls", wzCabPath); + + hr = CabEnumerate(wzCabPath, L"*", EnumCallback, 0); + ExitOnFailure(hr, "failed to compress files into cabinet: %ls", wzCabPath); + +LExit: + CabUninitialize(); + + return hr; +} + + +static INT_PTR __stdcall EnumCallback( + __in FDINOTIFICATIONTYPE fdint, + __in PFDINOTIFICATION pfdin +) +{ + if (fdint == fdintCOPY_FILE) + { + ConsoleWriteLine(CONSOLE_COLOR_NORMAL, "%s\t%d\t%u\t%u", pfdin->psz1, pfdin->cb, pfdin->date, pfdin->time); + } + + return 0; +} diff --git a/src/wixnative/extractcab.cpp b/src/wixnative/extractcab.cpp new file mode 100644 index 00000000..53f53266 --- /dev/null +++ b/src/wixnative/extractcab.cpp @@ -0,0 +1,50 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static HRESULT ProgressCallback(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext); + + +HRESULT ExtractCabCommand( + __in int argc, + __in LPWSTR argv[] +) +{ + HRESULT hr = E_INVALIDARG; + LPCWSTR wzCabPath = NULL; + LPCWSTR wzOutputFolder = NULL; + + if (argc < 2) + { + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Must specify: cabPath outputFolder"); + } + + wzCabPath = argv[0]; + wzOutputFolder = argv[1]; + + hr = CabInitialize(FALSE); + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to initialize cabinet: %ls", wzCabPath); + + hr = CabExtract(wzCabPath, L"*", wzOutputFolder, ProgressCallback, NULL, 0); + ExitOnFailure(hr, "failed to compress files into cabinet: %ls", wzCabPath); + +LExit: + CabUninitialize(); + + return hr; +} + + +static HRESULT ProgressCallback( + __in BOOL fBeginFile, + __in LPCWSTR wzFileId, + __in LPVOID /*pvContext*/ +) +{ + if (fBeginFile) + { + ConsoleWriteLine(CONSOLE_COLOR_NORMAL, "%ls", wzFileId); + } + + return S_OK; +} diff --git a/src/wixnative/packages.config b/src/wixnative/packages.config new file mode 100644 index 00000000..02ee2250 --- /dev/null +++ b/src/wixnative/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/wixnative/precomp.cpp b/src/wixnative/precomp.cpp new file mode 100644 index 00000000..37664a1c --- /dev/null +++ b/src/wixnative/precomp.cpp @@ -0,0 +1,3 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" diff --git a/src/wixnative/precomp.h b/src/wixnative/precomp.h new file mode 100644 index 00000000..5bd617e5 --- /dev/null +++ b/src/wixnative/precomp.h @@ -0,0 +1,19 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include +#include +#include + +#include "dutil.h" +#include "conutil.h" +#include "memutil.h" +#include "pathutil.h" +#include "strutil.h" +#include "cabcutil.h" +#include "cabutil.h" + +HRESULT SmartCabCommand(int argc, LPWSTR argv[]); +HRESULT ResetAclsCommand(int argc, LPWSTR argv[]); +HRESULT EnumCabCommand(int argc, LPWSTR argv[]); +HRESULT ExtractCabCommand(int argc, LPWSTR argv[]); diff --git a/src/wixnative/resetacls.cpp b/src/wixnative/resetacls.cpp new file mode 100644 index 00000000..8c5bdc56 --- /dev/null +++ b/src/wixnative/resetacls.cpp @@ -0,0 +1,51 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +HRESULT ResetAclsCommand(int argc, LPWSTR argv[]) +{ + Unused(argc); + Unused(argv); + + HRESULT hr = S_OK; + ACL* pacl = NULL; + DWORD cbAcl = sizeof(ACL); + LPWSTR sczFilePath = NULL; + + // create an empty (not NULL!) ACL to use on all the files + pacl = static_cast(MemAlloc(cbAcl, FALSE)); + ConsoleExitOnNull(pacl, hr, E_OUTOFMEMORY, CONSOLE_COLOR_RED, "failed to allocate ACL"); + +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::InitializeAcl(pacl, cbAcl, ACL_REVISION)) +#pragma prefast(op) + { + ConsoleExitOnLastError(hr, CONSOLE_COLOR_RED, "failed to initialize ACL"); + } + + // Reset the existing security permissions on each provided file. + for (;;) + { + hr = ConsoleReadW(&sczFilePath); + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to read file path from stdin"); + + if (!*sczFilePath) + { + break; + } + + hr = ::SetNamedSecurityInfoW(sczFilePath, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, pacl, NULL); + if (ERROR_FILE_NOT_FOUND != hr && ERROR_PATH_NOT_FOUND != hr) + { + ConsoleExitOnFailure(hr = HRESULT_FROM_WIN32(hr), CONSOLE_COLOR_RED, "failed to set security descriptor for file: %ls", sczFilePath); + } + } + + AssertSz(::IsValidAcl(pacl), "ResetAcls() - created invalid ACL"); + +LExit: + ReleaseStr(sczFilePath); + ReleaseMem(pacl); + return hr; +} diff --git a/src/wixnative/smartcab.cpp b/src/wixnative/smartcab.cpp new file mode 100644 index 00000000..da9087a3 --- /dev/null +++ b/src/wixnative/smartcab.cpp @@ -0,0 +1,157 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static HRESULT CompressFiles(HANDLE hCab); +static void __stdcall CabNamesCallback(LPWSTR wzFirstCabName, LPWSTR wzNewCabName, LPWSTR wzFileToken); + + +HRESULT SmartCabCommand( + __in int argc, + __in LPWSTR argv[] +) +{ + HRESULT hr = E_INVALIDARG; + LPCWSTR wzCabPath = NULL; + LPCWSTR wzCabName = NULL; + LPWSTR sczCabDir = NULL; + UINT uiFileCount = 0; + UINT uiMaxSize = 0; + UINT uiMaxThresh = 0; + COMPRESSION_TYPE ct = COMPRESSION_TYPE_NONE; + HANDLE hCab = NULL; + + if (argc < 1) + { + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Must specify: outCabPath [compressionType] [fileCount] [maxSizePerCabInMB [maxThreshold]]"); + } + else + { + wzCabPath = argv[0]; + wzCabName = PathFile(wzCabPath); + + hr = PathGetDirectory(wzCabPath, &sczCabDir); + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Could not parse directory from path: %ls", wzCabPath); + + if (argc > 1) + { + UINT uiCompressionType; + hr = StrStringToUInt32(argv[1], 0, &uiCompressionType); + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Could not parse compression type as number: %ls", argv[1]); + + ct = (uiCompressionType > 4) ? COMPRESSION_TYPE_HIGH : static_cast(uiCompressionType); + } + + if (argc > 2) + { + hr = StrStringToUInt32(argv[2], 0, &uiFileCount); + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Could not parse file count as number: %ls", argv[2]); + } + + if (argc > 3) + { + hr = StrStringToUInt32(argv[3], 0, &uiMaxSize); + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Could not parse max size as number: %ls", argv[3]); + } + + if (argc > 4) + { + hr = StrStringToUInt32(argv[4], 0, &uiMaxThresh); + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Could not parse max threshold as number: %ls", argv[4]); + } + } + + hr = CabCBegin(wzCabName, sczCabDir, uiFileCount, uiMaxSize, uiMaxThresh, ct, &hCab); + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to initialize cabinet: %ls", wzCabPath); + + hr = CompressFiles(hCab); + ExitOnFailure(hr, "failed to compress files into cabinet: %ls", wzCabPath); + + hr = CabCFinish(hCab, CabNamesCallback); + hCab = NULL; // once finish is called, the handle is invalid. + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to compress cabinet: %ls", wzCabPath); + + +LExit: + if (hCab) + { + CabCCancel(hCab); + } + ReleaseStr(sczCabDir); + + return hr; +} + + +static HRESULT CompressFiles( + __in HANDLE hCab +) +{ + HRESULT hr = S_OK; + LPWSTR sczLine = NULL; + LPWSTR* rgsczSplit = NULL; + UINT cSplit = 0; + MSIFILEHASHINFO hashInfo = { sizeof(MSIFILEHASHINFO) }; + + for (;;) + { + hr = ConsoleReadW(&sczLine); + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to read smartcab line from stdin"); + + if (!*sczLine) + { + break; + } + + hr = StrSplitAllocArray(&rgsczSplit, &cSplit, sczLine, L"\t"); + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to split smartcab line from stdin: %ls", sczLine); + + if (cSplit != 2 && cSplit != 6) + { + hr = E_INVALIDARG; + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to split smartcab line into hash x 4, token, source file: %ls", sczLine); + } + + LPCWSTR wzFilePath = rgsczSplit[0]; + LPCWSTR wzToken = rgsczSplit[1]; + PMSIFILEHASHINFO pHashInfo = NULL; + + if (cSplit == 6) + { + for (int i = 0; i < 4; ++i) + { + LPCWSTR wzHash = rgsczSplit[i + 2]; + + hr = StrStringToInt32(wzHash, 0, reinterpret_cast(hashInfo.dwData + i)); + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to parse hash: %ls for file: %ls", wzHash, wzFilePath); + } + + pHashInfo = &hashInfo; + } + + hr = CabCAddFile(wzFilePath, wzToken, pHashInfo, hCab); + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to add file: %ls", wzFilePath); + + ReleaseNullStrArray(rgsczSplit, cSplit); + } + +LExit: + ReleaseNullStrArray(rgsczSplit, cSplit); + ReleaseStr(sczLine); + + return hr; +} + + +// Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method +// First argument is the name of splitting cabinet without extension e.g. "cab1" +// Second argument is name of the new cabinet that would be formed by splitting e.g. "cab1b.cab" +// Third argument is the file token of the first file present in the splitting cabinet +static void __stdcall CabNamesCallback( + __in LPWSTR wzFirstCabName, + __in LPWSTR wzNewCabName, + __in LPWSTR wzFileToken +) +{ + ConsoleWriteLine(CONSOLE_COLOR_NORMAL, "%ls\t%ls\t%ls", wzFirstCabName, wzNewCabName, wzFileToken); +} diff --git a/src/wixnative/wixnative.cpp b/src/wixnative/wixnative.cpp new file mode 100644 index 00000000..7bd8dbca --- /dev/null +++ b/src/wixnative/wixnative.cpp @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +int __cdecl wmain(int argc, LPWSTR argv[]) +{ + HRESULT hr = E_INVALIDARG; + + ConsoleInitialize(); + + if (argc < 2) + { + ConsoleWriteError(hr, CONSOLE_COLOR_RED, "Must specify a command"); + } + else if (CSTR_EQUAL == ::CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, argv[1], -1, L"smartcab", -1)) + { + hr = SmartCabCommand(argc - 2, argv + 2); + } + else if (CSTR_EQUAL == ::CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, argv[1], -1, L"extractcab", -1)) + { + hr = ExtractCabCommand(argc - 2, argv + 2); + } + else if (CSTR_EQUAL == ::CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, argv[1], -1, L"enumcab", -1)) + { + hr = EnumCabCommand(argc - 2, argv + 2); + } + else if (CSTR_EQUAL == ::CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, argv[1], -1, L"resetacls", -1)) + { + hr = ResetAclsCommand(argc - 2, argv + 2); + } + else + { + ConsoleWriteError(hr, CONSOLE_COLOR_RED, "Unknown command: %ls", argv[1]); + } + + ConsoleUninitialize(); + return HRESULT_CODE(hr); +} diff --git a/src/wixnative/wixnative.vcxproj b/src/wixnative/wixnative.vcxproj new file mode 100644 index 00000000..2a4ce3d5 --- /dev/null +++ b/src/wixnative/wixnative.vcxproj @@ -0,0 +1,80 @@ + + + + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + {8497EC72-B8D0-4272-A9D0-7E9D871CEFBF} + Application + Console + wixnative + v141 + Unicode + Native component of WixToolset.Core + + + + + + + + + + + + + crypt32.lib;cabinet.lib;msi.lib + + + + + + 4996 + + + Create + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + -- cgit v1.2.3-55-g6feb