Add ability to create VHD/VHDX files and unpack packages to them (#379)

* Added ability to create, mount, and unmount VHDs

* Refactoring; Accept -vhdSize on VHD creation; Retrieve drive letter on mount

* Handle vhdSize values

* Enabled creation of VHDX files

* Updated help strings

* Delete broken VHD on VHD creation failures

* Don't delete vhd on vhd creation failure if failure is ERROR_FILE_EXISTS

* Adjusted min/max vhd siez

* Expose option to mount VHD as RO or RW.

* Modify func signatures in VHDProvider

* Added updated CreateCIM and WVDUtilities binaries
This commit is contained in:
stephenk-msft 2020-10-04 18:41:13 -07:00 коммит произвёл GitHub
Родитель 55cea3902c
Коммит e00e737ba5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 357 добавлений и 20 удалений

Двоичные данные
MsixCore/Dependencies/x64/CreateCIM.dll

Двоичный файл не отображается.

Двоичные данные
MsixCore/Dependencies/x64/CreateCIM.pdb

Двоичный файл не отображается.

Двоичные данные
MsixCore/Dependencies/x64/WVDUtilities.dll Normal file

Двоичный файл не отображается.

Двоичные данные
MsixCore/Dependencies/x64/WVDUtilities.pdb Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -165,6 +165,31 @@ std::map<std::wstring, Options, CaseInsensitiveLess> CommandLineInterface::s_opt
}
commandLineInterface->SetWVDFileType(utf8_to_utf16(fileType));
return S_OK;
}),
},
{
L"-vhdSize",
Option(true, IDS_STRING_HELP_OPTION_UNPACK_VHDSIZE,
[&](CommandLineInterface* commandLineInterface, const std::string& vhdSize)
{
if (commandLineInterface->m_operationType != OperationType::Unpack)
{
return E_INVALIDARG;
}
ULONGLONG maxVhdSizeMB = 2040000ull; // MAX SIZE: 2040 GB --> 2040000 MB
errno = 0;
ULONGLONG vhdSizeUll = strtoull(vhdSize.c_str(), NULL, 10 /*base*/);
if ((vhdSizeUll == ULLONG_MAX && errno == ERANGE) ||
vhdSizeUll > maxVhdSizeMB ||
vhdSizeUll < 5ull)
{
std::wcout << "\nInvalid VHD size. Specified value must be at least 5 MB and at most 2040000 MB\n" << std::endl;
return E_INVALIDARG;
}
commandLineInterface->m_vhdSize = vhdSizeUll;
return S_OK;
}),
}
@ -249,6 +274,30 @@ std::map<std::wstring, Options, CaseInsensitiveLess> CommandLineInterface::s_opt
commandLineInterface->SetWVDFileType(utf8_to_utf16(fileType));
return S_OK;
}),
},
{
L"-readOnly",
Option(true, IDS_STRING_HELP_OPTION_MOUNT_READONLY,
[&](CommandLineInterface* commandLineInterface, const std::string& readOnly)
{
if (commandLineInterface->m_operationType != OperationType::MountImage)
{
return E_INVALIDARG;
}
if (CaseInsensitiveEquals(utf8_to_utf16(readOnly), std::wstring(L"true")))
{
commandLineInterface->m_readOnly = true;
}
else if (CaseInsensitiveEquals(utf8_to_utf16(readOnly), std::wstring(L"false")))
{
commandLineInterface->m_readOnly = false;
}
else
{
return E_INVALIDARG;
}
return S_OK;
}),
}
})
},
@ -265,6 +314,19 @@ std::map<std::wstring, Options, CaseInsensitiveLess> CommandLineInterface::s_opt
return S_OK;
},
{
{
L"-imagePath",
Option(true, IDS_STRING_HELP_OPTION_MOUNTIMAGE_IMAGEPATH,
[&](CommandLineInterface* commandLineInterface, const std::string& imagePath)
{
if (commandLineInterface->m_operationType != OperationType::UnmountImage)
{
return E_INVALIDARG;
}
commandLineInterface->m_mountImagePath = utf8_to_utf16(imagePath);
return S_OK;
}),
},
{
L"-volumeId",
Option(true, IDS_STRING_HELP_OPTION_UNMOUNTIMAGE_VOLUMEID,

Просмотреть файл

@ -94,6 +94,7 @@ public:
bool IsApplyACLs() { return m_applyACLs; }
bool IsValidateSignature() { return m_validateSignature; }
bool IsCreate() { return m_create; }
bool isMountReadOnly() { return m_readOnly; }
std::wstring GetPackageFilePathToInstall() { return m_packageFilePath; }
std::wstring GetPackageFullName() { return m_packageFullName; }
std::wstring GetUnpackDestination() { return m_unpackDestination; }
@ -102,6 +103,7 @@ public:
std::wstring GetVolumeId() { return m_volumeId; }
WVDFileType GetFileType() { return m_fileType; }
OperationType GetOperationType() { return m_operationType; }
ULONGLONG GetVHDSize() { return m_vhdSize; }
private:
int m_argc = 0;
char ** m_argv = nullptr;
@ -119,7 +121,9 @@ private:
bool m_applyACLs;
bool m_validateSignature;
bool m_create = false;
bool m_readOnly = true;
WVDFileType m_fileType = WVDFileType::NotSpecified;
ULONGLONG m_vhdSize = 0;
OperationType m_operationType = OperationType::Undefined;

Просмотреть файл

@ -0,0 +1,113 @@
#include "MSIXWindows.hpp"
#include "VHDProvider.hpp"
#include "msixmgrLogger.hpp"
#include "..\msixmgrLib\GeneralUtil.hpp"
#include <string>
#include <iostream>
#include "InstallUI.hpp"
using namespace MsixCoreLib;
using namespace std;
namespace MsixCoreLib
{
class WVDUtilitiesDll
{
public:
WVDUtilitiesDll::WVDUtilitiesDll()
{
}
WVDUtilitiesDll::~WVDUtilitiesDll()
{
if (module != nullptr)
{
FreeLibrary(module);
}
}
HRESULT load()
{
module = LoadLibrary(L"wvdutilities.dll");
if (module == nullptr)
{
std::wcout << std::endl;
std::wcout << "Failed to load wvdutilities.dll. Please confirm the dll is next to this exe." << std::endl;
std::wcout << std::endl;
return HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND);
}
return S_OK;
}
HMODULE get()
{
return module;
}
private:
HMODULE module;
};
HRESULT CreateAndMountVHD(
_In_ const std::wstring& vhdFilePath,
_In_ ULONGLONG sizeMBs,
_In_ bool isVHD,
_Inout_ std::wstring& driveLetter)
{
WVDUtilitiesDll wvdUtilities;
RETURN_IF_FAILED(wvdUtilities.load());
typedef HRESULT(STDMETHODCALLTYPE *CREATEANDMOUNTVHD)(
const std::wstring& vhdFilePath,
ULONGLONG sizeMBs,
bool isVHD,
std::wstring& driveLetter);
CREATEANDMOUNTVHD CreateAndMountVHD =
reinterpret_cast<CREATEANDMOUNTVHD>
(GetProcAddress(wvdUtilities.get(), "CreateAndMountVHD"));
RETURN_IF_FAILED(CreateAndMountVHD(vhdFilePath, sizeMBs, isVHD, driveLetter));
return S_OK;
}
HRESULT UnmountVHD(
_In_ const std::wstring& vhdFilePath)
{
WVDUtilitiesDll wvdUtilities;
RETURN_IF_FAILED(wvdUtilities.load());
typedef HRESULT(STDMETHODCALLTYPE *UNMOUNTVHD)(const std::wstring& vhdFilePath);
UNMOUNTVHD UnmountVHD =
reinterpret_cast<UNMOUNTVHD>
(GetProcAddress(wvdUtilities.get(), "UnmountVHD"));
RETURN_IF_FAILED(UnmountVHD(vhdFilePath));
return S_OK;
}
HRESULT MountVHD(
_In_ const std::wstring& vhdFilePath,
_In_ bool readOnly,
_Inout_ std::wstring& driveLetter)
{
WVDUtilitiesDll wvdUtilities;
RETURN_IF_FAILED(wvdUtilities.load());
typedef HRESULT(STDMETHODCALLTYPE *MOUNTVHD)(const std::wstring& vhdFilePath, bool readOnly, std::wstring& driveLetter);
MOUNTVHD MountVHD =
reinterpret_cast<MOUNTVHD>
(GetProcAddress(wvdUtilities.get(), "MountVHD"));
RETURN_IF_FAILED(MountVHD(vhdFilePath, readOnly, driveLetter));
return S_OK;
}
}

Просмотреть файл

@ -0,0 +1,19 @@
#pragma once
#include <string>
namespace MsixCoreLib
{
HRESULT CreateAndMountVHD(
_In_ const std::wstring& vhdFilePath,
_In_ ULONGLONG sizeMBs,
_In_ bool isVHD,
_Inout_ std::wstring& driveLetter);
HRESULT UnmountVHD(
_In_ const std::wstring& vhdFilePath);
HRESULT MountVHD(
_In_ const std::wstring& vhdFilePath,
_In_ bool readOnly,
_Inout_ std::wstring& driveLetter);
}

Просмотреть файл

@ -19,6 +19,7 @@
#include <VersionHelpers.h>
#include "UnpackProvider.hpp"
#include "ApplyACLsProvider.hpp"
#include "VHDProvider.hpp"
#include "CIMProvider.hpp"
#include "MsixErrors.hpp"
#include <filesystem>
@ -311,6 +312,10 @@ int main(int argc, char * argv[])
WVDFileType fileType = cli.GetFileType();
bool createFile = cli.IsCreate();
std::vector<std::wstring> skippedFiles;
std::vector<std::wstring> failedPackages;
std::vector<HRESULT> failedPackagesErrors;
if (fileType == WVDFileType::CIM)
{
if (rootDirectory.empty() || fileType == WVDFileType::NotSpecified)
@ -359,9 +364,6 @@ int main(int argc, char * argv[])
return E_UNEXPECTED;
}
std::vector<std::wstring> skippedFiles;
std::vector<std::wstring> failedPackages;
std::vector<HRESULT> failedPackagesErrors;
RETURN_IF_FAILED(MsixCoreLib::Unpack(
packageSourcePath,
tempDirPathString,
@ -387,7 +389,7 @@ int main(int argc, char * argv[])
if (FAILED(hrCreateCIM))
{
std::wcout << std::endl;
std::wcout << "Creating the CIM file " << unpackDestination << " failed with HRESULT 0x" << std::hex << hr << std::endl;
std::wcout << "Creating the CIM file " << unpackDestination << " failed with HRESULT 0x" << std::hex << hrCreateCIM << std::endl;
std::wcout << std::endl;
return hrCreateCIM;
}
@ -406,13 +408,82 @@ int main(int argc, char * argv[])
{
if (createFile)
{
return ERROR_NOT_SUPPORTED;
if (!(EndsWith(unpackDestination, L".vhd") || (EndsWith(unpackDestination, L".vhdx"))))
{
std::wcout << std::endl;
std::wcout << "Invalid VHD file name. File name must have .vhd or .vhdx file extension" << std::endl;
std::wcout << std::endl;
return E_INVALIDARG;
}
else
{
if (cli.GetVHDSize() == 0)
{
std::wcout << std::endl;
std::wcout << "VHD size was not specified. Please provide a vhd size in MB using the -vhdSize option" << std::endl;
std::wcout << std::endl;
return E_INVALIDARG;
}
std::wstring driveLetter;
HRESULT hrCreateVHD = MsixCoreLib::CreateAndMountVHD(unpackDestination, cli.GetVHDSize(), fileType == WVDFileType::VHD, driveLetter);
if (FAILED(hrCreateVHD))
{
std::wcout << std::endl;
std::wcout << "Creating the VHD(X) file " << unpackDestination << " failed with HRESULT 0x" << std::hex << hrCreateVHD << std::endl;
if (hrCreateVHD != HRESULT_FROM_WIN32(ERROR_FILE_EXISTS))
{
// Best effort to unmount and delete the VHD file
if (std::filesystem::exists(std::filesystem::path(unpackDestination.c_str())))
{
MsixCoreLib::UnmountVHD(unpackDestination);
if (_wremove(unpackDestination.c_str()) != 0)
{
std::wcout << "Failed best-effort attempt to delete the incomplete VHD(X) file: " << unpackDestination << " Please do not use this file." << std::endl;
}
else
{
std::wcout << "Best-effort attempt to delete the incomplete VHD(X) file " << unpackDestination << " succeeded." << std::endl;
}
}
}
std::wcout << std::endl;
return hrCreateVHD;
}
// Unpack to the mounted VHD
std::wstring mountedUnpackDest = driveLetter + L":\\" + cli.GetRootDirectory();
RETURN_IF_FAILED(MsixCoreLib::Unpack(
packageSourcePath,
mountedUnpackDest,
cli.IsApplyACLs(),
cli.IsValidateSignature(),
skippedFiles,
failedPackages,
failedPackagesErrors
));
HRESULT hrUnmount = MsixCoreLib::UnmountVHD(unpackDestination);
if (FAILED(hrUnmount))
{
std::wcout << std::endl;
std::wcout << "Unmounting the VHD " << unpackDestination << " failed with HRESULT 0x" << std::hex << hrCreateVHD << std::endl;
std::wcout << "Ignoring as non-fatal error.." << std::endl;
std::wcout << std::endl;
}
OutputUnpackFailures(packageSourcePath, skippedFiles, failedPackages, failedPackagesErrors);
std::wcout << std::endl;
std::wcout << "Finished unpacking packages to: " << unpackDestination << std::endl;
std::wcout << std::endl;
}
}
else
{
std::vector<std::wstring> skippedFiles;
std::vector<std::wstring> failedPackages;
std::vector<HRESULT> failedPackagesErrors;
RETURN_IF_FAILED(MsixCoreLib::Unpack(
packageSourcePath,
unpackDestination,
@ -440,7 +511,8 @@ int main(int argc, char * argv[])
}
case OperationType::MountImage:
{
if (cli.GetFileType() == WVDFileType::CIM)
WVDFileType fileType = cli.GetFileType();
if (fileType == WVDFileType::CIM)
{
if (cli.GetVolumeId().empty())
{
@ -459,7 +531,6 @@ int main(int argc, char * argv[])
std::wcout << std::endl;
return E_UNEXPECTED;
}
HRESULT hrMountCIM = MsixCoreLib::MountCIM(cli.GetMountImagePath(), volumeIdFromString);
if (FAILED(hrMountCIM))
@ -481,10 +552,39 @@ int main(int argc, char * argv[])
std::wcout << std::endl;
}
}
else if (fileType == WVDFileType::VHD || fileType == WVDFileType::VHDX)
{
if (cli.GetMountImagePath().empty())
{
std::wcout << std::endl;
std::wcout << "Please provide the path to the image you would like to mount." << std::endl;
std::wcout << std::endl;
return E_INVALIDARG;
}
std::wstring driveLetter;
HRESULT hrMountVHD = MsixCoreLib::MountVHD(cli.GetMountImagePath(), cli.isMountReadOnly(), driveLetter);
if (FAILED(hrMountVHD))
{
std::wcout << std::endl;
std::wcout << "Mounting the VHD(X) file " << cli.GetMountImagePath() << " failed with HRESULT 0x" << std::hex << hrMountVHD << std::endl;
std::wcout << std::endl;
return hrMountVHD;
}
else
{
bool isVHD = cli.GetFileType() == WVDFileType::VHD;
std::wcout << std::endl;
std::wcout << "Image " << cli.GetMountImagePath() << " successfully mounted to " << driveLetter << ":\\" << std::endl;
std::wcout << "To unmount, run the following command: " << std::endl;
std::wcout << "msixmgr.exe -unmountimage -imagePath " << cli.GetMountImagePath() << " -filetype VHD" << (isVHD ? "" : "X") << std::endl;
std::wcout << std::endl;
}
}
else
{
std::wcout << std::endl;
std::wcout << "Please specify one of the following supported file types for the -MountImage command: {CIM}" << std::endl;
std::wcout << "Please specify one of the following supported file types for the -MountImage command: {VHD, VHDX, CIM}" << std::endl;
std::wcout << std::endl;
return ERROR_NOT_SUPPORTED;
}
@ -492,7 +592,8 @@ int main(int argc, char * argv[])
}
case OperationType::UnmountImage:
{
if (cli.GetFileType() == WVDFileType::CIM)
WVDFileType fileType = cli.GetFileType();
if (fileType == WVDFileType::CIM)
{
if (cli.GetVolumeId().empty())
{
@ -528,10 +629,36 @@ int main(int argc, char * argv[])
std::wcout << std::endl;
}
}
else if (fileType == WVDFileType::VHD || fileType == WVDFileType::VHDX)
{
if (cli.GetMountImagePath().empty())
{
std::wcout << std::endl;
std::wcout << "Please provide the path to the image you would like to unmount." << std::endl;
std::wcout << std::endl;
return E_INVALIDARG;
}
HRESULT hrUnmountVHD = MsixCoreLib::UnmountVHD(cli.GetMountImagePath());
if (FAILED(hrUnmountVHD))
{
std::wcout << std::endl;
std::wcout << "Unmounting the VHD " << cli.GetMountImagePath() << " failed with HRESULT 0x" << std::hex << hrUnmountVHD << std::endl;
std::wcout << std::endl;
return hrUnmountVHD;
}
else
{
std::wcout << std::endl;
std::wcout << "Successfully unmounted the VHD " << cli.GetMountImagePath() << std::endl;
std::wcout << std::endl;
}
}
else
{
std::wcout << std::endl;
std::wcout << "Please specify one of the following supported file types for the -UnmountImage command: {CIM}" << std::endl;
std::wcout << "Please specify one of the following supported file types for the -UnmountImage command: {VHD, VHDX, CIM}" << std::endl;
std::wcout << std::endl;
return ERROR_NOT_SUPPORTED;
}

Просмотреть файл

@ -75,7 +75,7 @@ BEGIN
IDS_STRING_HELP_OPTION_FINDPACKAGE
"Find package with the specific package full name."
IDS_STRING_HELP_OPTION_HELP "Display this help text."
IDS_STRING_HELPTEXT_USAGE " Usage:\n ------\n \tmsixmgr.exe [options]\n \tmsixmgr.exe -Unpack -packagePath <path to package> -destination <output folder> [-applyacls]\n \tmsixmgr.exe -ApplyACLs -packagePath <package folder path>\n\n"
IDS_STRING_HELPTEXT_USAGE " Usage:\n ------\n \tmsixmgr.exe [options]\n \tmsixmgr.exe -Unpack -packagePath <path to package> -destination <output folder> [-applyacls] [-create] [-vhdSize <size in MB>] [-filetype <CIM | VHD | VHDX>] [-rootDirectory <rootDirectory>] \n \tmsixmgr.exe -ApplyACLs -packagePath <package folder path>\n \tmsixmgr.exe -MountImage -imagePath <path to image file> -fileType [ VHD | VHDX ]\n \tmsixmgr.exe -MountImage -imagePath <path to image file> -volumeId <GUID> -fileType CIM\n\n"
IDS_STRING_UI_CANCEL "Cancel"
END
@ -127,12 +127,14 @@ BEGIN
IDS_STRING_HELP_OPTION_UNPACK_CREATE "optional parameter that creates a new image with the specified -filetype and unpacks the packages to that image"
IDS_STRING_HELP_OPTION_UNPACK_ROOTDIRECTORY "root directory on an image to unpack packages to. Required parameter for unpacking to new and existing CIM files"
IDS_STRING_HELP_OPTION_UNPACK_FILETYPE "the type of file to unpack packages to. Valid file types include {VHD, VHDX, CIM}. This is a required parameter when unpacking to CIM files"
IDS_STRING_HELP_OPTION_MOUNTIMAGE "Mounts an image of the specified file type"
IDS_STRING_HELP_OPTION_MOUNTIMAGE_IMAGEPATH "the path to the image file to mount"
IDS_STRING_HELP_OPTION_MOUNTIMAGE_VOLUMEID "a GUID (specified without curly braces) to associate with the mounted image. Use this GUID to unmount the mounted image"
IDS_STRING_HELP_OPTION_UNMOUNTIMAGE "Unmounts the image associated with the provided volume id"
IDS_STRING_HELP_OPTION_UNMOUNTIMAGE_VOLUMEID "the GUID (specified without curly braces) associated with the image to unmount"
IDS_STRING_HELP_OPTION_MOUNT_FILETYPE "the type of file to mount or unmount. The following file types are currently supported: CIM"
IDS_STRING_HELP_OPTION_MOUNTIMAGE "Mounts an image of the specified file type. Supported file types include {VHD, VHDX, CIM}."
IDS_STRING_HELP_OPTION_MOUNTIMAGE_IMAGEPATH "the path to the image file to mount or unmount."
IDS_STRING_HELP_OPTION_MOUNTIMAGE_VOLUMEID "a GUID (specified without curly braces) to associate with the mounted image. Use this GUID to unmount the mounted image. Use only for CIM files."
IDS_STRING_HELP_OPTION_UNMOUNTIMAGE "Unmounts the VHD, VHDX, or CIM image"
IDS_STRING_HELP_OPTION_UNMOUNTIMAGE_VOLUMEID "the GUID (specified without curly braces) associated with the image to unmount. Use only for CIM files."
IDS_STRING_HELP_OPTION_MOUNT_FILETYPE "the type of file to mount or unmount. The following file types are currently supported: {VHD, VHDX, CIM}"
IDS_STRING_HELP_OPTION_UNPACK_VHDSIZE "the desired size of the VHD or VHDX file in MB. Must be between 5 and 2040000 MB. Use only for VHD or VHDX files"
IDS_STRING_HELP_OPTION_MOUNT_READONLY "boolean (true of false) indicating whether a VHD(X) should be mounted as read only. If not specified, the image is mounted as read-only by default"
END

Просмотреть файл

@ -235,6 +235,7 @@ del "$(OutDir)msixmgr_LN.exe"
<ClInclude Include="UnpackProvider.hpp" />
<ClInclude Include="Util.hpp" />
<ClInclude Include="msixmgrLogger.hpp" />
<ClInclude Include="VHDProvider.hpp" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ApplyACLsProvider.cpp" />
@ -245,6 +246,7 @@ del "$(OutDir)msixmgr_LN.exe"
<ClCompile Include="Util.cpp" />
<ClCompile Include="msixmgr.cpp" />
<ClCompile Include="msixmgrLogger.cpp" />
<ClCompile Include="VHDProvider.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="msixmgr.rc" />

Просмотреть файл

@ -39,6 +39,9 @@
<ClInclude Include="CIMProvider.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="VHDProvider.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="InstallUI.cpp">
@ -65,6 +68,9 @@
<ClCompile Include="CIMProvider.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="VHDProvider.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="msixmgr.rc">

Просмотреть файл

@ -65,6 +65,8 @@
#define IDS_STRING_HELP_OPTION_UNMOUNTIMAGE 160
#define IDS_STRING_HELP_OPTION_UNMOUNTIMAGE_VOLUMEID 161
#define IDS_STRING_HELP_OPTION_MOUNT_FILETYPE 162
#define IDS_STRING_HELP_OPTION_UNPACK_VHDSIZE 163
#define IDS_STRING_HELP_OPTION_MOUNT_READONLY 164
// Next default values for new objects
//