2019-08-21 02:58:11 +03:00
# include "MSIXWindows.hpp"
# include "UnpackProvider.hpp"
# include "ApplyACLsProvider.hpp"
# include <TraceLoggingProvider.h>
# include "msixmgrLogger.hpp"
# include "..\msixmgrLib\GeneralUtil.hpp"
# include <shlobj_core.h>
# include <CommCtrl.h>
# include <map>
# include <iostream>
2020-08-23 03:35:32 +03:00
# include <filesystem>
# include "MsixErrors.hpp"
2019-08-21 02:58:11 +03:00
using namespace MsixCoreLib ;
using namespace std ;
namespace MsixCoreLib
{
2020-08-23 03:35:32 +03:00
// Purpose:
// - Unpacks either a single package/bundle or multiple packages/bundles from a specified
// source directory.
//
// Notes:
// - skippedFiles will be populated with the file paths of file objects in a specified
// source directory that are not packages or bundles. If all file objects in the directory
// are packages OR the given source path is for a single package/bundle, we expect that
// skippedFiles will be empty.
// - This function swallows errors from unpacking individual packages and bundles and adds
// the file paths of these packages/bundles to the failedPackages vector and adds the
// error code to the failedPackagesErrors vectors
HRESULT Unpack (
_In_ std : : wstring source ,
_In_ std : : wstring destination ,
_In_ bool isApplyACLs ,
_In_ bool validateSignature ,
_Inout_ std : : vector < std : : wstring > & skippedFiles ,
_Inout_ std : : vector < std : : wstring > & failedPackages ,
_Inout_ std : : vector < HRESULT > & failedPackagesErrors )
{
filesystem : : path sourcePath ( source ) ;
bool isDirectory = filesystem : : is_directory ( sourcePath ) ;
if ( isDirectory )
{
RETURN_IF_FAILED ( UnpackPackagesFromDirectory (
source ,
destination ,
isApplyACLs ,
validateSignature ,
skippedFiles ,
failedPackages ,
failedPackagesErrors ) ) ;
}
else
{
HRESULT hrUnpack = UnpackPackageOrBundle ( source , destination , isApplyACLs , validateSignature ) ;
if ( FAILED ( hrUnpack ) )
{
failedPackages . push_back ( source ) ;
failedPackagesErrors . push_back ( hrUnpack ) ;
}
}
return S_OK ;
}
// Purpose:
// - Unpacks the packages in the specified source directory to the given destination.
//
// Notes:
// - Files in the source directory that are not packages will be ignored and their file
// path will be added to the vector of skipped packages
// - Packages and bundles that fail to get unpacked will have their file path added to the vector
// of failedPackages and the associated error code will be added to the failedPackagesErrors
// vector at the corresponding index.
// - This function swallows errors from failing to unpack individual packages and should return
// S_OK in almost all cases, except when the given source path is not the path to a directory
HRESULT UnpackPackagesFromDirectory (
_In_ std : : wstring source ,
_In_ std : : wstring destination ,
_In_ bool isApplyACLs ,
_In_ bool validateSignature ,
_Inout_ std : : vector < std : : wstring > & skippedFiles ,
_Inout_ std : : vector < std : : wstring > & failedPackages ,
_Inout_ std : : vector < HRESULT > & failedPackagesErrors )
{
if ( ! filesystem : : is_directory ( filesystem : : path ( source ) ) )
{
return E_INVALIDARG ;
}
for ( const auto & entry : filesystem : : directory_iterator ( source ) )
{
auto fullFilePath = entry . path ( ) . wstring ( ) ;
if ( entry . is_regular_file ( ) )
{
// Swallow the error and add to list of failed packages and their associated errors
HRESULT hrUnpack = UnpackPackageOrBundle ( fullFilePath , destination , isApplyACLs , validateSignature ) ;
if ( FAILED ( hrUnpack ) )
{
failedPackages . push_back ( fullFilePath ) ;
failedPackagesErrors . push_back ( hrUnpack ) ;
}
}
else
{
skippedFiles . push_back ( fullFilePath ) ;
}
}
return S_OK ;
}
HRESULT UnpackPackageOrBundle (
_In_ std : : wstring source ,
_In_ std : : wstring destination ,
_In_ bool isApplyACLs ,
_In_ bool validateSignature )
{
HRESULT hr = S_OK ;
if ( IsPackageFile ( source ) )
{
hr = MsixCoreLib : : UnpackPackage ( source , destination , isApplyACLs , validateSignature ) ;
}
else if ( IsBundleFile ( source ) )
{
hr = MsixCoreLib : : UnpackBundle ( source , destination , isApplyACLs , validateSignature ) ;
}
else
{
return E_INVALIDARG ;
}
return hr ;
}
2019-08-21 02:58:11 +03:00
HRESULT UnpackPackage (
_In_ std : : wstring packageFilePath ,
_In_ std : : wstring destination ,
2019-10-01 21:20:01 +03:00
_In_ bool isApplyACLs ,
_In_ bool validateSignature )
2019-08-21 02:58:11 +03:00
{
MSIX_PACKUNPACK_OPTION unpackOption = MSIX_PACKUNPACK_OPTION_UNPACKWITHFLATSTRUCTURE ;
2019-10-01 21:20:01 +03:00
MSIX_VALIDATION_OPTION validationOption = validateSignature ? MSIX_VALIDATION_OPTION_FULL : MSIX_VALIDATION_OPTION_SKIPSIGNATURE ;
2019-08-21 02:58:11 +03:00
auto unpackDestinationUTF8 = utf16_to_utf8 ( destination ) ;
std : : vector < char > unpackDestination ( unpackDestinationUTF8 . c_str ( ) , unpackDestinationUTF8 . c_str ( ) + unpackDestinationUTF8 . size ( ) + 1 ) ;
ComPtr < IAppxFactory > factory ;
RETURN_IF_FAILED ( CoCreateAppxFactoryWithHeap ( MyAllocate , MyFree , validationOption , & factory ) ) ;
ComPtr < IStream > stream ;
RETURN_IF_FAILED ( CreateStreamOnFileUTF16 ( packageFilePath . c_str ( ) , true , & stream ) ) ;
ComPtr < IAppxPackageReader > reader ;
RETURN_IF_FAILED ( factory - > CreatePackageReader ( stream . Get ( ) , & reader ) ) ;
RETURN_IF_FAILED ( UnpackPackageFromPackageReader (
unpackOption ,
reader . Get ( ) ,
& unpackDestination [ 0 ] ) ) ;
2019-09-19 01:37:02 +03:00
ComPtr < IAppxManifestReader > manifestReader ;
RETURN_IF_FAILED ( reader - > GetManifest ( & manifestReader ) ) ;
2019-08-21 02:58:11 +03:00
2019-09-19 01:37:02 +03:00
ComPtr < IAppxManifestPackageId > packageId ;
RETURN_IF_FAILED ( manifestReader - > GetPackageId ( & packageId ) ) ;
2019-08-21 02:58:11 +03:00
2019-09-19 01:37:02 +03:00
Text < WCHAR > packageFullName ;
RETURN_IF_FAILED ( packageId - > GetPackageFullName ( & packageFullName ) ) ;
RETURN_IF_FAILED ( OutputPackageDependencies ( manifestReader . Get ( ) , packageFullName . content ) ) ;
2019-08-21 02:58:11 +03:00
2019-09-19 01:37:02 +03:00
if ( isApplyACLs )
{
std : : vector < std : : wstring > packageFolders ;
2019-08-21 02:58:11 +03:00
std : : wstring packageFolderName = destination + L " \\ " + packageFullName . Get ( ) ;
packageFolders . push_back ( packageFolderName ) ;
RETURN_IF_FAILED ( ApplyACLs ( packageFolders ) ) ;
}
return S_OK ;
}
HRESULT UnpackBundle (
_In_ std : : wstring packageFilePath ,
_In_ std : : wstring destination ,
2019-10-01 21:20:01 +03:00
_In_ bool isApplyACLs ,
_In_ bool validateSignature )
2019-08-21 02:58:11 +03:00
{
MSIX_APPLICABILITY_OPTIONS applicabilityOption = static_cast < MSIX_APPLICABILITY_OPTIONS > ( MSIX_APPLICABILITY_NONE ) ;
MSIX_PACKUNPACK_OPTION unpackOption = MSIX_PACKUNPACK_OPTION_UNPACKWITHFLATSTRUCTURE ;
2019-10-01 21:20:01 +03:00
MSIX_VALIDATION_OPTION validationOption = validateSignature ? MSIX_VALIDATION_OPTION_FULL : MSIX_VALIDATION_OPTION_SKIPSIGNATURE ;
2019-08-21 02:58:11 +03:00
auto unpackDestinationUTF8 = utf16_to_utf8 ( destination ) ;
std : : vector < char > unpackDestination ( unpackDestinationUTF8 . c_str ( ) , unpackDestinationUTF8 . c_str ( ) + unpackDestinationUTF8 . size ( ) + 1 ) ;
ComPtr < IAppxBundleFactory > factory ;
RETURN_IF_FAILED ( CoCreateAppxBundleFactoryWithHeap ( MyAllocate , MyFree , validationOption , applicabilityOption , & factory ) ) ;
ComPtr < IStream > stream ;
RETURN_IF_FAILED ( CreateStreamOnFileUTF16 ( packageFilePath . c_str ( ) , true , & stream ) ) ;
ComPtr < IAppxBundleReader > reader ;
RETURN_IF_FAILED ( factory - > CreateBundleReader ( stream . Get ( ) , & reader ) ) ;
RETURN_IF_FAILED ( UnpackBundleFromBundleReader (
unpackOption ,
reader . Get ( ) ,
& unpackDestination [ 0 ] ) ) ;
2019-09-19 01:37:02 +03:00
ComPtr < IAppxBundleManifestReader > bundleManifestReader ;
RETURN_IF_FAILED ( reader - > GetManifest ( & bundleManifestReader ) ) ;
// Determine whether the bundle's main package has any package dependencies and if so,
// output those dependencies to the user
ComPtr < IAppxBundleManifestPackageInfoEnumerator > packageEnumerator ;
ComPtr < IAppxBundleManifestPackageInfo > bundlePackageInfo ;
RETURN_IF_FAILED ( bundleManifestReader - > GetPackageInfoItems ( & packageEnumerator ) ) ;
BOOL hasCurrent = FALSE ;
for ( packageEnumerator - > GetHasCurrent ( & hasCurrent ) ; hasCurrent ; packageEnumerator - > MoveNext ( & hasCurrent ) )
{
RETURN_IF_FAILED ( packageEnumerator - > GetCurrent ( & bundlePackageInfo ) ) ;
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE packageType ;
RETURN_IF_FAILED ( bundlePackageInfo - > GetPackageType ( & packageType ) ) ;
if ( packageType = = APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_APPLICATION )
{
Text < WCHAR > packageFileName ;
RETURN_IF_FAILED ( bundlePackageInfo - > GetFileName ( & packageFileName ) ) ;
ComPtr < IAppxFile > applicationPackageFile ;
RETURN_IF_FAILED ( reader - > GetPayloadPackage ( packageFileName . content , & applicationPackageFile ) ) ;
ComPtr < IStream > applicationPackageStream ;
RETURN_IF_FAILED ( applicationPackageFile - > GetStream ( & applicationPackageStream ) ) ;
ComPtr < IAppxFactory > factory ;
RETURN_IF_FAILED ( CoCreateAppxFactoryWithHeap ( MyAllocate , MyFree , validationOption , & factory ) ) ;
ComPtr < IAppxPackageReader > packageReader ;
RETURN_IF_FAILED ( factory - > CreatePackageReader ( applicationPackageStream . Get ( ) , & packageReader ) ) ;
ComPtr < IAppxManifestReader > manifestReader ;
RETURN_IF_FAILED ( packageReader - > GetManifest ( & manifestReader ) ) ;
ComPtr < IAppxManifestPackageId > packageId ;
RETURN_IF_FAILED ( manifestReader - > GetPackageId ( & packageId ) ) ;
Text < WCHAR > packageFullName ;
RETURN_IF_FAILED ( packageId - > GetPackageFullName ( & packageFullName ) ) ;
RETURN_IF_FAILED ( OutputPackageDependencies ( manifestReader . Get ( ) , packageFullName . content ) ) ;
break ;
}
}
2019-08-21 02:58:11 +03:00
if ( isApplyACLs )
{
2019-09-19 01:37:02 +03:00
std : : vector < std : : wstring > packageFolders ;
2019-08-21 02:58:11 +03:00
// Determine the name of the folder created for the unpacked bundle, and add this name to our list of package folders
Text < WCHAR > bundleFullName ;
ComPtr < IAppxManifestPackageId > bundleId ;
RETURN_IF_FAILED ( bundleManifestReader - > GetPackageId ( & bundleId ) ) ;
RETURN_IF_FAILED ( bundleId - > GetPackageFullName ( & bundleFullName ) ) ;
std : : wstring bundleFolderName = destination + L " \\ " + bundleFullName . Get ( ) ;
packageFolders . push_back ( bundleFolderName ) ;
// Now we must determine the names of the folders created for each package in the bundle
// To do so, we can use the bundle reader's GetPayloadPackages API, which will tell us the names of all the package FILES
// that were unpacked. While we could then create a stream for each of these files to determine their package full names, this could
// be potentially costly. Instead, we will use the bundle manifest reader's GetPackageInfoItems API, which will give us easy access
// to <package file name, package full name> pairs for ALL packages in the bundle (not necessarily the ones that were unpacked). Once
// we have built up a map of these file name -> full name pairs, we can quickly determine the package full name for each file name
// returned by GetPayloadPackages. Append the package full name to the destination to get the folder name for the unpacked package, then
// add this folder name to our list of package folder names.
std : : map < std : : wstring , std : : wstring > packagesMap ;
2019-09-19 01:37:02 +03:00
2019-08-21 02:58:11 +03:00
ComPtr < IAppxBundleManifestPackageInfoEnumerator > packages ;
ComPtr < IAppxBundleManifestPackageInfo > currentPackage ;
RETURN_IF_FAILED ( bundleManifestReader - > GetPackageInfoItems ( & packages ) ) ;
2019-09-19 01:37:02 +03:00
hasCurrent = FALSE ;
2019-08-21 02:58:11 +03:00
// Populate the packagesMap with package file name, package full name pairs
for ( packages - > GetHasCurrent ( & hasCurrent ) ; hasCurrent ; packages - > MoveNext ( & hasCurrent ) )
{
RETURN_IF_FAILED ( packages - > GetCurrent ( & currentPackage ) ) ;
ComPtr < IAppxManifestPackageId > manifestPackageID ;
RETURN_IF_FAILED ( currentPackage - > GetPackageId ( & manifestPackageID ) ) ;
Text < WCHAR > packageFullName ;
RETURN_IF_FAILED ( manifestPackageID - > GetPackageFullName ( & packageFullName ) ) ;
Text < WCHAR > packageFileName ;
RETURN_IF_FAILED ( currentPackage - > GetFileName ( & packageFileName ) ) ;
std : : wstring stdPackageFileName = packageFileName . Get ( ) ;
std : : wstring stdPackageFullName = packageFullName . Get ( ) ;
packagesMap [ stdPackageFileName ] = stdPackageFullName ;
}
// Use GetPayloadPackages to enumerate over the package files that actually got unpacked
// after applicability logic was applied.
hasCurrent = false ;
ComPtr < IAppxFilesEnumerator > packageFilesEnumerator ;
RETURN_IF_FAILED ( reader - > GetPayloadPackages ( & packageFilesEnumerator ) ) ;
RETURN_IF_FAILED ( packageFilesEnumerator - > GetHasCurrent ( & hasCurrent ) ) ;
while ( hasCurrent )
{
ComPtr < IAppxFile > file ;
RETURN_IF_FAILED ( packageFilesEnumerator - > GetCurrent ( & file ) ) ;
Text < WCHAR > packageFileName ;
RETURN_IF_FAILED ( file - > GetName ( & packageFileName ) ) ;
std : : wstring stdPackageFileName = packageFileName . Get ( ) ;
auto packageFullName = packagesMap [ packageFileName . Get ( ) ] ;
std : : wstring packageFolderName = destination + L " \\ " + packageFullName ;
packageFolders . push_back ( packageFolderName ) ;
RETURN_IF_FAILED ( packageFilesEnumerator - > MoveNext ( & hasCurrent ) ) ;
}
RETURN_IF_FAILED ( ApplyACLs ( packageFolders ) ) ;
}
return S_OK ;
}
2019-09-19 01:37:02 +03:00
HRESULT OutputPackageDependencies (
_In_ IAppxManifestReader * manifestReader ,
_In_ LPWSTR packageFullName )
{
ComPtr < IAppxManifestPackageDependenciesEnumerator > dependencyEnumerator ;
ComPtr < IAppxManifestPackageDependency > dependency ;
BOOL hasCurrent = FALSE ;
BOOL hasPackageDependencies = FALSE ;
RETURN_IF_FAILED ( manifestReader - > GetPackageDependencies ( & dependencyEnumerator ) ) ;
for ( dependencyEnumerator - > GetHasCurrent ( & hasCurrent ) ; hasCurrent = = TRUE ; dependencyEnumerator - > MoveNext ( & hasCurrent ) )
{
if ( ! hasPackageDependencies )
{
hasPackageDependencies = TRUE ;
std : : wcout < < std : : endl ;
std : : wcout < < " [Warning] The app " < < packageFullName < < " depends on the following packages to run correctly. Please ensure these package dependencies are installed on the target machine or included beside the app package: " < < std : : endl ;
std : : wcout < < std : : endl ;
}
RETURN_IF_FAILED ( dependencyEnumerator - > GetCurrent ( & dependency ) ) ;
Text < WCHAR > packageDependencyName ;
RETURN_IF_FAILED ( dependency - > GetName ( & packageDependencyName ) ) ;
std : : wcout < < packageDependencyName . content < < std : : endl ;
}
return S_OK ;
}
2019-08-21 02:58:11 +03:00
}