From 088dc648a3478e2cacdbdab1cb1782556642ee69 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sun, 3 Sep 2017 16:51:39 -0700 Subject: Initial commit --- src/Cpp.Build.props | 1 + src/Directory.Build.props | 1 + src/WixToolset.Core.Native/CabInterop.cs | 312 +++++++++++++++++++++ .../WixToolset.Core.Native.csproj | 23 ++ .../WixToolset.Core.Native.nuspec | 24 ++ src/winterop/packages.config | 5 + src/winterop/precomp.h | 12 + .../runtime.win-xxx.WixToolset.Core.Native.nuspec | 19 ++ src/winterop/winterop.cpp | 216 ++++++++++++++ src/winterop/winterop.def | 18 ++ src/winterop/winterop.vcxproj | 79 ++++++ 11 files changed, 710 insertions(+) create mode 100644 src/WixToolset.Core.Native/CabInterop.cs create mode 100644 src/WixToolset.Core.Native/WixToolset.Core.Native.csproj create mode 100644 src/WixToolset.Core.Native/WixToolset.Core.Native.nuspec create mode 100644 src/winterop/packages.config create mode 100644 src/winterop/precomp.h create mode 100644 src/winterop/runtime.win-xxx.WixToolset.Core.Native.nuspec create mode 100644 src/winterop/winterop.cpp create mode 100644 src/winterop/winterop.def create mode 100644 src/winterop/winterop.vcxproj (limited to 'src') diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props index 1e4d4cbc..453aa442 100644 --- a/src/Cpp.Build.props +++ b/src/Cpp.Build.props @@ -3,6 +3,7 @@ + Win32 $(OutputPath) $(BaseIntermediateOutputPath)$(Platform)\ $(OutputPath)$(Platform)\ diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 48ba462d..63ad5d6e 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,6 +4,7 @@ Debug + AnyCPU $(MSBuildThisFileDirectory)..\build\obj\$(MSBuildProjectName)\ $(MSBuildThisFileDirectory)..\build\$(Configuration)\ diff --git a/src/WixToolset.Core.Native/CabInterop.cs b/src/WixToolset.Core.Native/CabInterop.cs new file mode 100644 index 00000000..6ad11c67 --- /dev/null +++ b/src/WixToolset.Core.Native/CabInterop.cs @@ -0,0 +1,312 @@ +// 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. + +namespace WixToolset.Core.Native +{ + using System; + using System.Diagnostics.CodeAnalysis; + using System.Text; + using System.Runtime.InteropServices; + // using WixToolset.Msi; + // using WixToolset.Msi.Interop; + + /// + /// The native methods. + /// + public sealed class NativeMethods + { + /// + /// Starts creating a cabinet. + /// + /// Name of cabinet to create. + /// Directory to create cabinet in. + /// Maximum number of files that will be added to cabinet. + /// Maximum size of the cabinet. + /// Maximum threshold in the cabinet. + /// Type of compression to use in the cabinet. + /// Handle to opened cabinet. + [DllImport("winterop.dll", EntryPoint = "CreateCabBegin", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)] + public static extern void CreateCabBegin(string cabinetName, string cabinetDirectory, uint maxFiles, uint maxSize, uint maxThreshold, uint compressionType, out IntPtr contextHandle); + + /// + /// Adds a file to an open cabinet. + /// + /// Full path to file to add to cabinet. + /// Name of file in cabinet. + /// Handle to open cabinet. + // [DllImport("winterop.dll", EntryPoint = "CreateCabAddFile", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)] + // public static extern void CreateCabAddFile(string file, string token, MsiInterop.MSIFILEHASHINFO fileHash, IntPtr contextHandle); + + /// + /// Closes a cabinet. + /// + /// Handle to open cabinet to close. + /// Address of Binder's cabinet split callback + [DllImport("winterop.dll", EntryPoint = "CreateCabFinish", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)] + public static extern void CreateCabFinish(IntPtr contextHandle, IntPtr newCabNamesCallBackAddress); + + /// + /// Cancels cabinet creation. + /// + /// Handle to open cabinet to cancel. + [DllImport("winterop.dll", EntryPoint = "CreateCabCancel", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)] + public static extern void CreateCabCancel(IntPtr contextHandle); + + /// + /// Initializes cabinet extraction. + /// + [DllImport("winterop.dll", EntryPoint = "ExtractCabBegin", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)] + public static extern void ExtractCabBegin(); + + /// + /// Extracts files from cabinet. + /// + /// Path to cabinet to extract files from. + /// Directory to extract files to. + [DllImport("winterop.dll", EntryPoint = "ExtractCab", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true, PreserveSig = false)] + public static extern void ExtractCab(string cabinet, string extractDirectory); + + /// + /// Cleans up after cabinet extraction. + /// + [DllImport("winterop.dll", EntryPoint = "ExtractCabFinish", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] + public static extern void ExtractCabFinish(); + + /// + /// Initializes cabinet enumeration. + /// + [DllImport("winterop.dll", EntryPoint = "EnumerateCabBegin", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)] + public static extern void EnumerateCabBegin(); + + /// + /// Enumerates files from cabinet. + /// + /// Path to cabinet to enumerate files from. + /// callback that gets each file. + [DllImport("winterop.dll", EntryPoint = "EnumerateCab", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true, PreserveSig = false)] + public static extern void EnumerateCab(string cabinet, CabInterop.PFNNOTIFY notify); + + /// + /// Cleans up after cabinet enumeration. + /// + [DllImport("winterop.dll", EntryPoint = "EnumerateCabFinish", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] + public static extern void EnumerateCabFinish(); + + /// + /// Resets the DACL on an array of files to "empty". + /// + /// Array of file reset ACL to "empty". + /// Number of file paths in array. + [DllImport("winterop.dll", EntryPoint = "ResetAcls", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)] + public static extern void ResetAcls(string[] files, uint fileCount); + + /// + /// Gets the hash of the pCertContext->pCertInfo->SubjectPublicKeyInfo using ::CryptHashPublicKeyInfo() which does not seem + /// to be exposed by .NET Frameowkr. + /// + /// Pointer to a CERT_CONTEXT struct with public key information to hash. + /// Number of file paths in array. + [DllImport("winterop.dll", EntryPoint = "HashPublicKeyInfo", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)] + public static extern void HashPublicKeyInfo(IntPtr certContext, byte[] publicKeyInfoHashed, ref uint sizePublicKeyInfoHashed); + + /// + /// Converts file time to a local file time. + /// + /// file time + /// local file time + /// true if successful, false otherwise + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool FileTimeToLocalFileTime(ref long fileTime, ref long localTime); + + /// + /// Converts file time to a MS-DOS time. + /// + /// file time + /// MS-DOS date + /// MS-DOS time + /// true if successful, false otherwise + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool FileTimeToDosDateTime(ref long fileTime, out ushort wFatDate, out ushort wFatTime); + } + + /// + /// Interop class for the winterop.dll. + /// + public static class CabInterop + { + /// + /// Delegate type that's called by cabinet api for every file in cabinet. + /// + /// NOTIFICATIONTYPE + /// NOTIFICATION + /// 0 for success, -1 otherwise + public delegate Int32 PFNNOTIFY(NOTIFICATIONTYPE fdint, NOTIFICATION pfdin); + + /// + /// Wraps FDINOTIFICATIONTYPE. + /// + public enum NOTIFICATIONTYPE : int + { + /// Info about the cabinet. + CABINET_INFO, + /// One or more files are continued. + PARTIAL_FILE, + /// Called for each file in cabinet. + COPY_FILE, + /// Called after all of the data has been written to a target file. + CLOSE_FILE_INFO, + /// A file is continued to the next cabinet. + NEXT_CABINET, + /// Called once after a call to FDICopy() starts scanning a CAB's CFFILE entries, and again when there are no more CFFILE entries. + ENUMERATE, + } + + /// + /// Converts DateTime to MS-DOS date and time which cabinet uses. + /// + /// DateTime + /// MS-DOS date + /// MS-DOS time + public static void DateTimeToCabDateAndTime(DateTime dateTime, out ushort cabDate, out ushort cabTime) + { + // dateTime.ToLocalTime() does not match FileTimeToLocalFileTime() for some reason. + // so we need to call FileTimeToLocalFileTime() from kernel32.dll. + long filetime = dateTime.ToFileTime(); + long localTime = 0; + NativeMethods.FileTimeToLocalFileTime(ref filetime, ref localTime); + NativeMethods.FileTimeToDosDateTime(ref localTime, out cabDate, out cabTime); + } + + /// + /// Wraps FDINOTIFICATION. + /// + [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public class NOTIFICATION + { + private int cb; + [MarshalAs(UnmanagedType.LPStr)] + private string psz1; + [MarshalAs(UnmanagedType.LPStr)] + private string psz2; + [MarshalAs(UnmanagedType.LPStr)] + private string psz3; + private IntPtr pv; + + private IntPtr hf; + + private ushort date; + private ushort time; + private ushort attribs; + private ushort setID; + private ushort cabinet; + private ushort folder; + private int fdie; + + /// + /// Uncompressed size of file. + /// + public int Cb + { + get { return this.cb; } + } + + /// + /// File name in cabinet. + /// + public String Psz1 + { + get { return this.psz1; } + } + + /// + /// Name of next disk. + /// + public string Psz2 + { + get { return this.psz2; } + } + + /// + /// Points to a 256 character buffer. + /// + public string Psz3 + { + get { return this.psz3; } + } + + /// + /// Value for client. + /// + public IntPtr Pv + { + get { return this.pv; } + } + + /// + /// Not used. + /// + public Int32 Hf + { + get { return (Int32)this.hf; } + } + + /// + /// Last modified MS-DOS date. + /// + public ushort Date + { + get { return this.date; } + } + + /// + /// Last modified MS-DOS time. + /// + public ushort Time + { + get { return this.time; } + } + + /// + /// File attributes. + /// + public ushort Attribs + { + get { return this.attribs; } + } + + /// + /// Cabinet set ID (a random 16-bit number). + /// + public ushort SetID + { + get { return this.setID; } + } + + /// + /// Cabinet number within cabinet set (0-based). + /// + public ushort Cabinet + { + get { return this.cabinet; } + } + + /// + /// File's folder index. + /// + public ushort Folder + { + get { return this.folder; } + } + + /// + /// Error code. + /// + public int Fdie + { + get { return this.fdie; } + } + } + } +} diff --git a/src/WixToolset.Core.Native/WixToolset.Core.Native.csproj b/src/WixToolset.Core.Native/WixToolset.Core.Native.csproj new file mode 100644 index 00000000..858b9be2 --- /dev/null +++ b/src/WixToolset.Core.Native/WixToolset.Core.Native.csproj @@ -0,0 +1,23 @@ + + + + netstandard2.0 + $(MSBuildThisFileName).nuspec + + + + + + + + + + + $(OutputPath) + Configuration=$(Configuration);Id=$(MSBuildThisFileName);Version=$(BuildVersionSimple);Authors=$(Authors);Copyright=$(Copyright);Description=$(Description) + + + diff --git a/src/WixToolset.Core.Native/WixToolset.Core.Native.nuspec b/src/WixToolset.Core.Native/WixToolset.Core.Native.nuspec new file mode 100644 index 00000000..68d154c8 --- /dev/null +++ b/src/WixToolset.Core.Native/WixToolset.Core.Native.nuspec @@ -0,0 +1,24 @@ + + + + $id$ + $version$ + $authors$ + $authors$ + https://github.com/wixtoolset/Core.Native/blob/master/LICENSE.TXT + https://github.com/wixtoolset/Core.Native + false + $description$ + $copyright$ + + + + + + + + + + + + diff --git a/src/winterop/packages.config b/src/winterop/packages.config new file mode 100644 index 00000000..b11fe210 --- /dev/null +++ b/src/winterop/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/winterop/precomp.h b/src/winterop/precomp.h new file mode 100644 index 00000000..eba996c7 --- /dev/null +++ b/src/winterop/precomp.h @@ -0,0 +1,12 @@ +#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 "dutil.h" +#include "fileutil.h" +#include "memutil.h" +#include "strutil.h" +#include "cabcutil.h" +#include "cabutil.h" diff --git a/src/winterop/runtime.win-xxx.WixToolset.Core.Native.nuspec b/src/winterop/runtime.win-xxx.WixToolset.Core.Native.nuspec new file mode 100644 index 00000000..87cf0919 --- /dev/null +++ b/src/winterop/runtime.win-xxx.WixToolset.Core.Native.nuspec @@ -0,0 +1,19 @@ + + + + $id$ + $version$ + $authors$ + $authors$ + https://github.com/wixtoolset/Core.Native/blob/master/LICENSE.TXT + https://github.com/wixtoolset/Core.Native + false + $description$ + $copyright$ + + + + + + + diff --git a/src/winterop/winterop.cpp b/src/winterop/winterop.cpp new file mode 100644 index 00000000..12d8ca3f --- /dev/null +++ b/src/winterop/winterop.cpp @@ -0,0 +1,216 @@ +// 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 HashPublicKeyInfo( + __in PCERT_CONTEXT pCertContext, + __in_ecount(*pcbSubjectKeyIndentifier) BYTE* rgbSubjectKeyIdentifier, + __inout DWORD* pcbSubjectKeyIndentifier + ) +{ + HRESULT hr = S_OK; + + if (!::CryptHashPublicKeyInfo(NULL, CALG_SHA1, 0, X509_ASN_ENCODING, &pCertContext->pCertInfo->SubjectPublicKeyInfo, rgbSubjectKeyIdentifier, pcbSubjectKeyIndentifier)) + { + ExitWithLastError(hr, "Failed to hash public key information."); + } + +LExit: + return hr; +} + +HRESULT ResetAcls( + __in LPCWSTR pwzFiles[], + __in DWORD cFiles + ) +{ + HRESULT hr = S_OK; + ACL* pacl = NULL; + DWORD cbAcl = sizeof(ACL); + + OSVERSIONINFO osvi; + + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (!::GetVersionExA(&osvi)) + { + ExitOnLastError(hr, "failed to get OS version"); + } + + // If we're running on NT 4 or earlier, or ME or earlier, don't reset ACLs. + if (4 >= osvi.dwMajorVersion) + { + ExitFunction1(hr = S_FALSE); + } + + // create an empty (not NULL!) ACL to use on all the files + pacl = static_cast(MemAlloc(cbAcl, FALSE)); + ExitOnNull(pacl, hr, E_OUTOFMEMORY, "failed to allocate ACL"); + +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::InitializeAcl(pacl, cbAcl, ACL_REVISION)) +#pragma prefast(op) + { + ExitOnLastError(hr, "failed to initialize ACL"); + } + + // reset the existing security permissions on each file + for (DWORD i = 0; i < cFiles; ++i) + { + hr = ::SetNamedSecurityInfoW(const_cast(pwzFiles[i]), 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) + { + ExitOnFailure(hr = HRESULT_FROM_WIN32(hr), "failed to set security descriptor for file: %S", pwzFiles[i]); + } + } + + // Setting to S_OK because we could end with ERROR_FILE_NOT_FOUND or ERROR_PATH_NOT_FOUND as valid return values. + hr = S_OK; + + AssertSz(::IsValidAcl(pacl), "ResetAcls() - created invalid ACL"); + +LExit: + if (pacl) + { + MemFree(pacl); + } + + return hr; +} + + +HRESULT CreateCabBegin( + __in LPCWSTR wzCab, + __in LPCWSTR wzCabDir, + __in DWORD dwMaxFiles, + __in DWORD dwMaxSize, + __in DWORD dwMaxThresh, + __in COMPRESSION_TYPE ct, + __out HANDLE *phContext + ) +{ + return CabCBegin(wzCab, wzCabDir, dwMaxFiles, dwMaxSize, dwMaxThresh, ct, phContext); +} + + +HRESULT CreateCabAddFile( + __in LPCWSTR wzFile, + __in_opt LPCWSTR wzToken, + __in_opt PMSIFILEHASHINFO pmfHash, + __in HANDLE hContext + ) +{ + return CabCAddFile(wzFile, wzToken, pmfHash, hContext); +} + + +HRESULT CreateCabAddFiles( + __in LPCWSTR pwzFiles[], + __in LPCWSTR pwzTokens[], + __in PMSIFILEHASHINFO pmfHash[], + __in DWORD cFiles, + __in HANDLE hContext + ) +{ + HRESULT hr = S_OK; + DWORD i; + + Assert(pwzFiles); + Assert(hContext); + + for (i = 0; i < cFiles; i++) + { + hr = CreateCabAddFile( + pwzFiles[i], + pwzTokens ? pwzTokens[i] : NULL, + pmfHash[i], + hContext + ); + ExitOnFailure(hr, "Failed to add file %S to cab", pwzFiles[i]); + } + +LExit: + return hr; +} + + +HRESULT CreateCabFinish( + __in HANDLE hContext, + __in_opt FileSplitCabNamesCallback newCabNamesCallBackAddress + ) +{ + // Convert address into Binder callback function + return CabCFinish(hContext, newCabNamesCallBackAddress); +} + + +void CreateCabCancel( + __in HANDLE hContext + ) +{ + CabCCancel(hContext); +} + + +HRESULT ExtractCabBegin() +{ + return CabInitialize(FALSE); +} + + +HRESULT ExtractCab( + __in LPCWSTR wzCabinet, + __in LPCWSTR wzExtractDir + ) +{ + return CabExtract(wzCabinet, L"*", wzExtractDir, NULL, NULL, 0); +} + + +void ExtractCabFinish() +{ + CabUninitialize(); + return; +} + + +HRESULT EnumerateCabBegin() +{ + return CabInitialize(FALSE); +} + + +HRESULT EnumerateCab( + __in LPCWSTR wzCabinet, + __in STDCALL_PFNFDINOTIFY pfnNotify + ) +{ + return CabEnumerate(wzCabinet, L"*", pfnNotify, 0); +} + + +void EnumerateCabFinish() +{ + CabUninitialize(); + return; +} + + +BOOL WINAPI DllMain( + __in HINSTANCE /*hInstance*/, + __in DWORD dwReason, + __in LPVOID /*lpvReserved*/ + ) +{ + switch(dwReason) + { + case DLL_PROCESS_ATTACH: + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + + return TRUE; +} diff --git a/src/winterop/winterop.def b/src/winterop/winterop.def new file mode 100644 index 00000000..dffa6268 --- /dev/null +++ b/src/winterop/winterop.def @@ -0,0 +1,18 @@ +; 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. + +LIBRARY "winterop.dll" + +EXPORTS + CreateCabBegin + CreateCabCancel + CreateCabAddFile + CreateCabAddFiles + CreateCabFinish + EnumerateCabBegin + EnumerateCab + EnumerateCabFinish + ExtractCabBegin + ExtractCab + ExtractCabFinish + ResetAcls + HashPublicKeyInfo diff --git a/src/winterop/winterop.vcxproj b/src/winterop/winterop.vcxproj new file mode 100644 index 00000000..6274a063 --- /dev/null +++ b/src/winterop/winterop.vcxproj @@ -0,0 +1,79 @@ + + + + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + {26D45E58-E703-431D-B67E-493C72C9DA0B} + DynamicLibrary + winterop + v141 + MultiByte + winterop.def + 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