mirror of
https://github.com/pbatard/rufus.git
synced 2024-08-14 23:57:05 +00:00
[vhd] add experimental save to Full Flash Update (FFU) image support
* Full Flash Update (FFU) image support was added to dism with Windows 10 1709 and is an alternate way to save a virtual hard disk for restoration. * While more modern than VHD/VHDX, FFU creation only works for drives with file systems that Windows natively recognizes (FAT, NTFS) and that look like Windows installation media, so you can forget about FFU'ing a Linux disk. * The other *intentional* drawback that Microsoft added is that they don't want anybody but themselves being able to create and restore FFU images, so, even as they have nice FfuApplyImage()/FfuCaptureImage() calls in FfuProvider.dll they have decided not to make these public. * This means that, since we don't have time to spend on figuring and direct hooking internal DLL calls for x86_32, x86_64, ARM and ARM64 (and worrying that Microsoft may ever so slightly change their DLL between revs to break our hooks), we just call on dism.exe behind the scenes to create the FFU.
This commit is contained in:
parent
f9370e002e
commit
0b1c68635a
10 changed files with 126 additions and 34 deletions
|
@ -605,6 +605,7 @@ t MSG_340 "a \"Security Violation\" screen"
|
|||
t MSG_341 "a Windows Recovery Screen (BSOD) with '%s'"
|
||||
t MSG_342 "Compressed VHDX Image"
|
||||
t MSG_343 "Uncompressed VHD Image"
|
||||
t MSG_344 "Full Flash Update Image"
|
||||
# The following messages are for the Windows Store listing only and are not used by the application
|
||||
t MSG_900 "Rufus is a utility that helps format and create bootable USB flash drives, such as USB keys/pendrives, memory sticks, etc."
|
||||
t MSG_901 "Official site: %s"
|
||||
|
|
|
@ -897,7 +897,7 @@ BOOL GetDevices(DWORD devnum)
|
|||
break;
|
||||
}
|
||||
|
||||
if (GetDriveLabel(drive_index, drive_letters, &label)) {
|
||||
if (GetDriveLabel(drive_index, drive_letters, &label, FALSE)) {
|
||||
if ((props.is_SCSI) && (!props.is_UASP) && (!props.is_VHD)) {
|
||||
if (!props.is_Removable) {
|
||||
// Non removables should have been eliminated above, but since we
|
||||
|
|
|
@ -1314,7 +1314,7 @@ BOOL IsDriveLetterInUse(const char drive_letter)
|
|||
* Return the drive letter and volume label
|
||||
* If the drive doesn't have a volume assigned, space is returned for the letter
|
||||
*/
|
||||
BOOL GetDriveLabel(DWORD DriveIndex, char* letters, char** label)
|
||||
BOOL GetDriveLabel(DWORD DriveIndex, char* letters, char** label, BOOL bSilent)
|
||||
{
|
||||
HANDLE hPhysical;
|
||||
DWORD size, error;
|
||||
|
@ -1350,10 +1350,10 @@ BOOL GetDriveLabel(DWORD DriveIndex, char* letters, char** label)
|
|||
if (DeviceIoControl(hPhysical, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, NULL, 0, &size, NULL))
|
||||
AutorunLabel = get_token_data_file("label", AutorunPath);
|
||||
else if (GetLastError() == ERROR_NOT_READY)
|
||||
uprintf("Ignoring 'autorun.inf' label for drive %c: No media", toupper(letters[0]));
|
||||
suprintf("Ignoring 'autorun.inf' label for drive %c: No media", toupper(letters[0]));
|
||||
safe_closehandle(hPhysical);
|
||||
if (AutorunLabel != NULL) {
|
||||
uprintf("Using 'autorun.inf' label for drive %c: '%s'", toupper(letters[0]), AutorunLabel);
|
||||
suprintf("Using 'autorun.inf' label for drive %c: '%s'", toupper(letters[0]), AutorunLabel);
|
||||
static_strcpy(VolumeLabel, AutorunLabel);
|
||||
safe_free(AutorunLabel);
|
||||
*label = VolumeLabel;
|
||||
|
|
|
@ -396,7 +396,7 @@ UINT GetDriveTypeFromIndex(DWORD DriveIndex);
|
|||
char GetUnusedDriveLetter(void);
|
||||
BOOL IsDriveLetterInUse(const char drive_letter);
|
||||
char RemoveDriveLetters(DWORD DriveIndex, BOOL bUseLast, BOOL bSilent);
|
||||
BOOL GetDriveLabel(DWORD DriveIndex, char* letter, char** label);
|
||||
BOOL GetDriveLabel(DWORD DriveIndex, char* letters, char** label, BOOL bSilent);
|
||||
uint64_t GetDriveSize(DWORD DriveIndex);
|
||||
BOOL IsMediaPresent(DWORD DriveIndex);
|
||||
BOOL AnalyzeMBR(HANDLE hPhysicalDrive, const char* TargetName, BOOL bSilent);
|
||||
|
|
|
@ -1670,7 +1670,7 @@ BOOL DumpFatDir(const char* path, int32_t cluster)
|
|||
buf = libfat_get_sector(lf_fs, s);
|
||||
if (buf == NULL)
|
||||
FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_SECTOR_NOT_FOUND;
|
||||
if (FormatStatus)
|
||||
if (IS_ERROR(FormatStatus))
|
||||
goto out;
|
||||
size = MIN(LIBFAT_SECTOR_SIZE, diritem.size - written);
|
||||
if (!WriteFileWithRetry(handle, buf, size, &size, WRITE_RETRIES) ||
|
||||
|
@ -1724,7 +1724,7 @@ static HANDLE mounted_handle = INVALID_HANDLE_VALUE;
|
|||
|
||||
char* MountISO(const char* path)
|
||||
{
|
||||
VIRTUAL_STORAGE_TYPE vtype = { 1, VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT };
|
||||
VIRTUAL_STORAGE_TYPE vtype = { VIRTUAL_STORAGE_TYPE_DEVICE_ISO, VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT };
|
||||
ATTACH_VIRTUAL_DISK_PARAMETERS vparams = { 0 };
|
||||
DWORD r;
|
||||
wchar_t wtmp[128];
|
||||
|
|
|
@ -450,7 +450,7 @@ typedef struct {
|
|||
* to define an 'ext_t my_extensions' variable initialized with the relevant attributes.
|
||||
*/
|
||||
typedef struct ext_t {
|
||||
const size_t count;
|
||||
size_t count;
|
||||
const char* filename;
|
||||
const char** extension;
|
||||
const char** description;
|
||||
|
@ -652,7 +652,8 @@ extern char* FileDialog(BOOL save, char* path, const ext_t* ext, DWORD options);
|
|||
extern BOOL FileIO(enum file_io_type io_type, char* path, char** buffer, DWORD* size);
|
||||
extern unsigned char* GetResource(HMODULE module, char* name, char* type, const char* desc, DWORD* len, BOOL duplicate);
|
||||
extern DWORD GetResourceSize(HMODULE module, char* name, char* type, const char* desc);
|
||||
extern DWORD RunCommand(const char* cmdline, const char* dir, BOOL log);
|
||||
extern DWORD RunCommandWithProgress(const char* cmdline, const char* dir, BOOL log, int msg);
|
||||
#define RunCommand(cmd, dir, log) RunCommandWithProgress(cmd, dir, log, 0)
|
||||
extern BOOL CompareGUID(const GUID *guid1, const GUID *guid2);
|
||||
extern BOOL MountRegistryHive(const HKEY key, const char* pszHiveName, const char* pszHivePath);
|
||||
extern BOOL UnmountRegistryHive(const HKEY key, const char* pszHiveName);
|
||||
|
|
10
src/rufus.rc
10
src/rufus.rc
|
@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
|||
IDD_DIALOG DIALOGEX 12, 12, 232, 326
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
EXSTYLE WS_EX_ACCEPTFILES
|
||||
CAPTION "Rufus 4.2.2061"
|
||||
CAPTION "Rufus 4.2.2062"
|
||||
FONT 9, "Segoe UI Symbol", 400, 0, 0x0
|
||||
BEGIN
|
||||
LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP
|
||||
|
@ -392,8 +392,8 @@ END
|
|||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 4,2,2061,0
|
||||
PRODUCTVERSION 4,2,2061,0
|
||||
FILEVERSION 4,2,2062,0
|
||||
PRODUCTVERSION 4,2,2062,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
@ -411,13 +411,13 @@ BEGIN
|
|||
VALUE "Comments", "https://rufus.ie"
|
||||
VALUE "CompanyName", "Akeo Consulting"
|
||||
VALUE "FileDescription", "Rufus"
|
||||
VALUE "FileVersion", "4.2.2061"
|
||||
VALUE "FileVersion", "4.2.2062"
|
||||
VALUE "InternalName", "Rufus"
|
||||
VALUE "LegalCopyright", "© 2011-2023 Pete Batard (GPL v3)"
|
||||
VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html"
|
||||
VALUE "OriginalFilename", "rufus-4.2.exe"
|
||||
VALUE "ProductName", "Rufus"
|
||||
VALUE "ProductVersion", "4.2.2061"
|
||||
VALUE "ProductVersion", "4.2.2062"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
|
53
src/stdfn.c
53
src/stdfn.c
|
@ -26,6 +26,7 @@
|
|||
#include <gpedit.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "re.h"
|
||||
#include "rufus.h"
|
||||
#include "missing.h"
|
||||
#include "resource.h"
|
||||
|
@ -706,14 +707,19 @@ DWORD GetResourceSize(HMODULE module, char* name, char* type, const char* desc)
|
|||
}
|
||||
|
||||
// Run a console command, with optional redirection of stdout and stderr to our log
|
||||
DWORD RunCommand(const char* cmd, const char* dir, BOOL log)
|
||||
// as well as optional progress reporting if msg is not 0.
|
||||
DWORD RunCommandWithProgress(const char* cmd, const char* dir, BOOL log, int msg)
|
||||
{
|
||||
DWORD ret, dwRead, dwAvail, dwPipeSize = 4096;
|
||||
DWORD i, ret, dwRead, dwAvail, dwPipeSize = 4096;
|
||||
STARTUPINFOA si = { 0 };
|
||||
PROCESS_INFORMATION pi = { 0 };
|
||||
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
|
||||
HANDLE hOutputRead = INVALID_HANDLE_VALUE, hOutputWrite = INVALID_HANDLE_VALUE;
|
||||
int match_length;
|
||||
static char* output;
|
||||
// For detecting typical dism.exe commandline progress report of type:
|
||||
// "\r[==== 8.0% ]\r\n"
|
||||
re_t pattern = re_compile("\\s*\\[[= ]+[\\d\\.]+%[= ]+\\]\\s*");
|
||||
|
||||
si.cb = sizeof(si);
|
||||
if (log) {
|
||||
|
@ -737,17 +743,57 @@ DWORD RunCommand(const char* cmd, const char* dir, BOOL log)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (log) {
|
||||
if (log || msg != 0) {
|
||||
if (msg != 0)
|
||||
UpdateProgressWithInfoInit(NULL, FALSE);
|
||||
while (1) {
|
||||
// Check for user cancel
|
||||
if (IS_ERROR(FormatStatus) && (SCODE_CODE(FormatStatus) == ERROR_CANCELLED)) {
|
||||
if (!TerminateProcess(pi.hProcess, ERROR_CANCELLED)) {
|
||||
uprintf("Could not terminate command: %s", WindowsErrorString());
|
||||
} else switch (WaitForSingleObject(pi.hProcess, 5000)) {
|
||||
case WAIT_TIMEOUT:
|
||||
uprintf("Command did not terminate within timeout duration");
|
||||
break;
|
||||
case WAIT_OBJECT_0:
|
||||
uprintf("Command was terminated by user");
|
||||
break;
|
||||
default:
|
||||
uprintf("Error while waiting for command to be terminated: %s", WindowsErrorString());
|
||||
break;
|
||||
}
|
||||
ret = ERROR_CANCELLED;
|
||||
goto out;
|
||||
}
|
||||
// coverity[string_null]
|
||||
if (PeekNamedPipe(hOutputRead, NULL, dwPipeSize, NULL, &dwAvail, NULL)) {
|
||||
if (dwAvail != 0) {
|
||||
output = malloc(dwAvail + 1);
|
||||
if ((output != NULL) && (ReadFile(hOutputRead, output, dwAvail, &dwRead, NULL)) && (dwRead != 0)) {
|
||||
output[dwAvail] = 0;
|
||||
// Process a commandline progress bar into a percentage
|
||||
if ((msg != 0) && (re_matchp(pattern, output, &match_length) != -1)) {
|
||||
float f = 0.0f;
|
||||
i = 0;
|
||||
next_progress_line:
|
||||
for (; (i < dwAvail) && (output[i] < '0' || output[i] > '9'); i++);
|
||||
IGNORE_RETVAL(sscanf(&output[i], "%f*", &f));
|
||||
UpdateProgressWithInfo(OP_FORMAT, msg, (uint64_t)(f * 100.0f), 100 * 100ULL);
|
||||
// Go to next line
|
||||
while ((++i < dwAvail) && (output[i] != '\n') && (output[i] != '\r'));
|
||||
while ((++i < dwAvail) && ((output[i] == '\n') || (output[i] == '\r')));
|
||||
// Print additional lines, if any
|
||||
if (i < dwAvail) {
|
||||
// Might have two consecutive progress lines in our buffer
|
||||
if (re_matchp(pattern, &output[i], &match_length) != -1)
|
||||
goto next_progress_line;
|
||||
uprintf("%s", &output[i]);
|
||||
}
|
||||
} else if (log) {
|
||||
// output may contain a '%' so don't feed it as a naked format string
|
||||
uprintf("%s", output);
|
||||
}
|
||||
}
|
||||
free(output);
|
||||
}
|
||||
}
|
||||
|
@ -756,6 +802,7 @@ DWORD RunCommand(const char* cmd, const char* dir, BOOL log)
|
|||
Sleep(100);
|
||||
};
|
||||
} else {
|
||||
// TODO: Detect user cancellation here?
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
}
|
||||
|
||||
|
|
66
src/vhd.c
66
src/vhd.c
|
@ -896,7 +896,7 @@ PF_TYPE_DECL(WINAPI, DWORD, GetVirtualDiskOperationProgress, (HANDLE, LPOVERLAPP
|
|||
// to backup a physical disk to VHD/VHDX. Now if this could also be used to create an
|
||||
// ISO from optical media that would be swell, but no matter what I tried, it didn't
|
||||
// seem possible...
|
||||
static DWORD WINAPI SaveImageThread(void* param)
|
||||
static DWORD WINAPI SaveVHDThread(void* param)
|
||||
{
|
||||
IMG_SAVE* img_save = (IMG_SAVE*)param;
|
||||
HANDLE handle = INVALID_HANDLE_VALUE;
|
||||
|
@ -906,7 +906,7 @@ static DWORD WINAPI SaveImageThread(void* param)
|
|||
STOPGAP_CREATE_VIRTUAL_DISK_PARAMETERS vparams = { 0 };
|
||||
VIRTUAL_DISK_PROGRESS vprogress = { 0 };
|
||||
OVERLAPPED overlapped = { 0 };
|
||||
DWORD result, flags;
|
||||
DWORD r = ERROR_NOT_FOUND, flags;
|
||||
|
||||
PF_INIT_OR_OUT(CreateVirtualDisk, VirtDisk);
|
||||
PF_INIT_OR_OUT(GetVirtualDiskOperationProgress, VirtDisk);
|
||||
|
@ -938,16 +938,16 @@ static DWORD WINAPI SaveImageThread(void* param)
|
|||
// CreateVirtualDisk() does not have an overwrite flag...
|
||||
DeleteFileW(wDst);
|
||||
|
||||
result = pfCreateVirtualDisk(&vtype, wDst, VIRTUAL_DISK_ACCESS_NONE, NULL,
|
||||
r = pfCreateVirtualDisk(&vtype, wDst, VIRTUAL_DISK_ACCESS_NONE, NULL,
|
||||
flags, 0, (PCREATE_VIRTUAL_DISK_PARAMETERS)&vparams, &overlapped, &handle);
|
||||
if (result != ERROR_SUCCESS && result != ERROR_IO_PENDING) {
|
||||
SetLastError(result);
|
||||
if (r != ERROR_SUCCESS && r != ERROR_IO_PENDING) {
|
||||
SetLastError(r);
|
||||
uprintf("Could not create virtual disk: %s", WindowsErrorString());
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (result == ERROR_IO_PENDING) {
|
||||
while ((result = WaitForSingleObject(overlapped.hEvent, 100)) == WAIT_TIMEOUT) {
|
||||
if (r == ERROR_IO_PENDING) {
|
||||
while ((r = WaitForSingleObject(overlapped.hEvent, 100)) == WAIT_TIMEOUT) {
|
||||
if (IS_ERROR(FormatStatus) && (SCODE_CODE(FormatStatus) == ERROR_CANCELLED)) {
|
||||
CancelIoEx(handle, &overlapped);
|
||||
goto out;
|
||||
|
@ -957,12 +957,13 @@ static DWORD WINAPI SaveImageThread(void* param)
|
|||
UpdateProgressWithInfo(OP_FORMAT, MSG_261, vprogress.CurrentValue, vprogress.CompletionValue);
|
||||
}
|
||||
}
|
||||
if (result != WAIT_OBJECT_0) {
|
||||
if (r != WAIT_OBJECT_0) {
|
||||
uprintf("Could not save virtual disk: %s", WindowsErrorString());
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
r = 0;
|
||||
UpdateProgressWithInfo(OP_FORMAT, MSG_261, SelectedDrive.DiskSize, SelectedDrive.DiskSize);
|
||||
uprintf("Operation complete.");
|
||||
|
||||
|
@ -974,7 +975,36 @@ out:
|
|||
safe_free(img_save->DevicePath);
|
||||
safe_free(img_save->ImagePath);
|
||||
PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)TRUE, 0);
|
||||
ExitThread(0);
|
||||
ExitThread(r);
|
||||
}
|
||||
|
||||
// FfuProvider.dll has some nice FfuApplyImage()/FfuCaptureImage() calls... which
|
||||
// Microsoft decided not make public!
|
||||
// Considering that trying to both figure out how to use these internal function
|
||||
// calls, as well as how to properly hook into the DLL for every arch/every release
|
||||
// of Windows, would be a massive timesink, we just take a shortcut by calling dism
|
||||
// directly, as imperfect as such a solution might be...
|
||||
static DWORD WINAPI SaveFFUThread(void* param)
|
||||
{
|
||||
DWORD r;
|
||||
IMG_SAVE* img_save = (IMG_SAVE*)param;
|
||||
char cmd[MAX_PATH + 128], *letter = "", *label;
|
||||
|
||||
GetDriveLabel(SelectedDrive.DeviceNumber, letter, &label, TRUE);
|
||||
static_sprintf(cmd, "dism /capture-ffu /capturedrive=%s /imagefile=\"%s\" "
|
||||
"/name:\"%s\" /description:\"Created by %s (%s)\"",
|
||||
img_save->DevicePath, img_save->ImagePath, label, APPLICATION_NAME, RUFUS_URL);
|
||||
uprintf("Running command: '%s", cmd);
|
||||
r = RunCommandWithProgress(cmd, sysnative_dir, TRUE, MSG_261);
|
||||
if (r != 0 && !IS_ERROR(FormatStatus)) {
|
||||
SetLastError(r);
|
||||
uprintf("Failed to create FFU image: %s", WindowsErrorString());
|
||||
FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_WINDOWS) | SCODE_CODE(r);
|
||||
}
|
||||
safe_free(img_save->DevicePath);
|
||||
safe_free(img_save->ImagePath);
|
||||
PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)TRUE, 0);
|
||||
ExitThread(r);
|
||||
}
|
||||
|
||||
void SaveVHD(void)
|
||||
|
@ -983,7 +1013,8 @@ void SaveVHD(void)
|
|||
char filename[128];
|
||||
char path[MAX_PATH];
|
||||
int DriveIndex = ComboBox_GetCurSel(hDeviceList);
|
||||
EXT_DECL(img_ext, filename, __VA_GROUP__("*.vhdx", "*.vhd"), __VA_GROUP__(lmprintf(MSG_342), lmprintf(MSG_343)));
|
||||
EXT_DECL(img_ext, filename, __VA_GROUP__("*.vhdx", "*.vhd", "*.ffu"),
|
||||
__VA_GROUP__(lmprintf(MSG_342), lmprintf(MSG_343), lmprintf(MSG_344)));
|
||||
ULARGE_INTEGER free_space;
|
||||
|
||||
if ((DriveIndex < 0) || (format_thread != NULL))
|
||||
|
@ -992,9 +1023,17 @@ void SaveVHD(void)
|
|||
static_sprintf(filename, "%s.vhdx", rufus_drive[DriveIndex].label);
|
||||
img_save.DeviceNum = (DWORD)ComboBox_GetItemData(hDeviceList, DriveIndex);
|
||||
img_save.DevicePath = GetPhysicalName(img_save.DeviceNum);
|
||||
// FFU support started with Windows 10 1709 (build 16299) and requires GPT
|
||||
// TODO: Better check for FFU compatibility
|
||||
if (WindowsVersion.Major < 10 || WindowsVersion.BuildNumber < 16299 ||
|
||||
SelectedDrive.PartitionStyle != PARTITION_STYLE_GPT)
|
||||
img_ext.count = 2;
|
||||
img_save.ImagePath = FileDialog(TRUE, NULL, &img_ext, 0);
|
||||
img_save.Type = safe_strstr(img_save.ImagePath, ".vhdx") != NULL ?
|
||||
VIRTUAL_STORAGE_TYPE_DEVICE_VHDX : VIRTUAL_STORAGE_TYPE_DEVICE_VHD;
|
||||
img_save.Type = VIRTUAL_STORAGE_TYPE_DEVICE_VHD;
|
||||
if (safe_strstr(img_save.ImagePath, ".vhdx") != NULL)
|
||||
img_save.Type = VIRTUAL_STORAGE_TYPE_DEVICE_VHD;
|
||||
else if (safe_strstr(img_save.ImagePath, ".ffu") != NULL)
|
||||
img_save.Type = VIRTUAL_STORAGE_TYPE_DEVICE_FFU;
|
||||
img_save.BufSize = DD_BUFFER_SIZE;
|
||||
img_save.DeviceSize = SelectedDrive.DiskSize;
|
||||
if (img_save.DevicePath != NULL && img_save.ImagePath != NULL) {
|
||||
|
@ -1009,7 +1048,8 @@ void SaveVHD(void)
|
|||
EnableControls(FALSE, FALSE);
|
||||
FormatStatus = 0;
|
||||
InitProgress(TRUE);
|
||||
format_thread = CreateThread(NULL, 0, SaveImageThread, &img_save, 0, NULL);
|
||||
format_thread = CreateThread(NULL, 0, img_save.Type == VIRTUAL_STORAGE_TYPE_DEVICE_FFU ?
|
||||
SaveFFUThread : SaveVHDThread, &img_save, 0, NULL);
|
||||
if (format_thread != NULL) {
|
||||
uprintf("\r\nSave to VHD operation started");
|
||||
PrintInfo(0, -1);
|
||||
|
|
|
@ -72,7 +72,10 @@
|
|||
#define MBR_SIZE 512 // Might need to review this once we see bootable 4k systems
|
||||
|
||||
// TODO: Remove this once MinGW has been updated
|
||||
#ifndef VIRTUAL_STORAGE_TYPE_DEVICE_VHDX
|
||||
#define VIRTUAL_STORAGE_TYPE_DEVICE_VHDX 3
|
||||
#endif
|
||||
#define VIRTUAL_STORAGE_TYPE_DEVICE_FFU 99
|
||||
#define CREATE_VIRTUAL_DISK_VERSION_2 2
|
||||
#define CREATE_VIRTUAL_DISK_FLAG_CREATE_BACKING_STORAGE 8
|
||||
|
||||
|
|
Loading…
Reference in a new issue