[vhd] add write support for .vhdx and .ffu images

* Also move VHD mounting function calls from iso.c to vhd.c and remove unused VHD footer elements.
This commit is contained in:
Pete Batard 2023-07-06 19:47:26 +01:00
parent f411d526d6
commit 5bbcba8534
No known key found for this signature in database
GPG Key ID: 38E0CF5E69EDD671
10 changed files with 226 additions and 223 deletions

View File

@ -36,6 +36,7 @@
#endif
#include "rufus.h"
#include "format.h"
#include "missing.h"
#include "resource.h"
#include "settings.h"
@ -73,7 +74,7 @@ extern const int nb_steps[FS_MAX];
extern uint32_t dur_mins, dur_secs;
extern uint32_t wim_nb_files, wim_proc_files, wim_extra_files;
extern BOOL force_large_fat32, enable_ntfs_compression, lock_drive, zero_drive, fast_zeroing, enable_file_indexing;
extern BOOL write_as_image, use_vds, write_as_esp, is_vds_available;
extern BOOL write_as_image, use_vds, write_as_esp, is_vds_available, has_ffu_support;
uint8_t *grub2_buf = NULL, *sec_buf = NULL;
long grub2_len;
@ -1154,6 +1155,7 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, BOOL bZeroDrive)
int64_t bled_ret;
uint8_t* buffer = NULL;
uint32_t zero_data, *cmp_buffer = NULL;
char* vhd_path = NULL;
int throttle_fast_zeroing = 0, read_bufnum = 0, proc_bufnum = 1;
if (SelectedDrive.SectorSize < 512) {
@ -1273,7 +1275,7 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, BOOL bZeroDrive)
if (i > WRITE_RETRIES)
goto out;
}
} else if (img_report.compression_type != BLED_COMPRESSION_NONE) {
} else if (img_report.compression_type != BLED_COMPRESSION_NONE && img_report.compression_type < BLED_COMPRESSION_MAX) {
uprintf("Writing compressed image:");
hSourceImage = CreateFileU(image_path, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
@ -1310,8 +1312,17 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, BOOL bZeroDrive)
goto out;
}
} else {
hSourceImage = CreateFileAsync(image_path, GENERIC_READ, FILE_SHARE_READ,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN);
assert(img_report.compression_type != IMG_COMPRESSION_FFU);
// VHD/VHDX require mounting the image first
if (img_report.compression_type == IMG_COMPRESSION_VHD ||
img_report.compression_type == IMG_COMPRESSION_VHDX) {
vhd_path = VhdMountImage(image_path);
if (vhd_path == NULL)
goto out;
}
hSourceImage = CreateFileAsync(vhd_path != NULL ? vhd_path : image_path, GENERIC_READ,
FILE_SHARE_READ, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN);
if (hSourceImage == NULL) {
uprintf("Could not open image '%s': %s", image_path, WindowsErrorString());
FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_OPEN_FAILED;
@ -1397,10 +1408,12 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, BOOL bZeroDrive)
RefreshDriveLayout(hPhysicalDrive);
ret = TRUE;
out:
if (img_report.compression_type != BLED_COMPRESSION_NONE)
if (img_report.compression_type != BLED_COMPRESSION_NONE && img_report.compression_type < BLED_COMPRESSION_MAX)
safe_closehandle(hSourceImage);
else
CloseFileAsync(hSourceImage);
if (vhd_path != NULL)
VhdUnmountImage();
safe_mm_free(buffer);
safe_mm_free(cmp_buffer);
return ret;
@ -1627,7 +1640,24 @@ DWORD WINAPI FormatThread(void* param)
// Write an image file
if ((boot_type == BT_IMAGE) && write_as_image) {
WriteDrive(hPhysicalDrive, FALSE);
// Special case for FFU images
if (img_report.compression_type == IMG_COMPRESSION_FFU) {
char cmd[MAX_PATH + 128], *physical;
// Should have been filtered out beforehand
assert(has_ffu_support);
safe_unlockclose(hPhysicalDrive);
physical = GetPhysicalName(SelectedDrive.DeviceNumber);
static_sprintf(cmd, "dism /Apply-Ffu /ApplyDrive:%s /ImageFile:\"%s\"", physical, image_path);
uprintf("Running command: '%s", cmd);
cr = RunCommandWithProgress(cmd, sysnative_dir, TRUE, MSG_261);
if (cr != 0 && !IS_ERROR(FormatStatus)) {
SetLastError(cr);
uprintf("Failed to apply FFU image: %s", WindowsErrorString());
FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_WINDOWS) | SCODE_CODE(cr);
}
} else {
WriteDrive(hPhysicalDrive, FALSE);
}
goto out;
}

View File

@ -1,7 +1,7 @@
/*
* Rufus: The Reliable USB Formatting Utility
* Formatting function calls
* Copyright © 2011-2020 Pete Batard <pete@akeo.ie>
* Copyright © 2011-2023 Pete Batard <pete@akeo.ie>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -111,6 +111,10 @@ typedef BOOLEAN (WINAPI* EnableVolumeCompression_t)(
ULONG CompressionFlags // FILE_SYSTEM_PROP_FLAG
);
#define IMG_COMPRESSION_FFU (BLED_COMPRESSION_MAX)
#define IMG_COMPRESSION_VHD (BLED_COMPRESSION_MAX + 1)
#define IMG_COMPRESSION_VHDX (BLED_COMPRESSION_MAX + 2)
BOOL WritePBR(HANDLE hLogicalDrive);
BOOL FormatLargeFAT32(DWORD DriveIndex, uint64_t PartitionOffset, DWORD ClusterSize, LPCSTR FSName, LPCSTR Label, DWORD Flags);
BOOL FormatExtFs(DWORD DriveIndex, uint64_t PartitionOffset, DWORD BlockSize, LPCSTR FSName, LPCSTR Label, DWORD Flags);

View File

@ -1726,83 +1726,8 @@ out:
return ret;
}
// VirtDisk API Prototypes since we can't use delay-loading because of MinGW
// See https://github.com/pbatard/rufus/issues/2272#issuecomment-1615976013
PF_TYPE_DECL(WINAPI, DWORD, OpenVirtualDisk, (PVIRTUAL_STORAGE_TYPE, PCWSTR,
VIRTUAL_DISK_ACCESS_MASK, OPEN_VIRTUAL_DISK_FLAG, POPEN_VIRTUAL_DISK_PARAMETERS, PHANDLE));
PF_TYPE_DECL(WINAPI, DWORD, AttachVirtualDisk, (HANDLE, PSECURITY_DESCRIPTOR,
ATTACH_VIRTUAL_DISK_FLAG, ULONG, PATTACH_VIRTUAL_DISK_PARAMETERS, LPOVERLAPPED));
PF_TYPE_DECL(WINAPI, DWORD, DetachVirtualDisk, (HANDLE, DETACH_VIRTUAL_DISK_FLAG, ULONG));
PF_TYPE_DECL(WINAPI, DWORD, GetVirtualDiskPhysicalPath, (HANDLE, PULONG, PWSTR));
static char physical_path[128] = "";
static HANDLE mounted_handle = INVALID_HANDLE_VALUE;
char* MountISO(const char* path)
{
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];
ULONG size = ARRAYSIZE(wtmp);
wconvert(path);
char* ret = NULL;
PF_INIT_OR_OUT(OpenVirtualDisk, VirtDisk);
PF_INIT_OR_OUT(AttachVirtualDisk, VirtDisk);
PF_INIT_OR_OUT(GetVirtualDiskPhysicalPath, VirtDisk);
if ((mounted_handle != NULL) && (mounted_handle != INVALID_HANDLE_VALUE))
UnMountISO();
r = pfOpenVirtualDisk(&vtype, wpath, VIRTUAL_DISK_ACCESS_READ | VIRTUAL_DISK_ACCESS_GET_INFO,
OPEN_VIRTUAL_DISK_FLAG_NONE, NULL, &mounted_handle);
if (r != ERROR_SUCCESS) {
SetLastError(r);
uprintf("Could not open ISO '%s': %s", path, WindowsErrorString());
goto out;
}
vparams.Version = ATTACH_VIRTUAL_DISK_VERSION_1;
r = pfAttachVirtualDisk(mounted_handle, NULL, ATTACH_VIRTUAL_DISK_FLAG_READ_ONLY |
ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER, 0, &vparams, NULL);
if (r != ERROR_SUCCESS) {
SetLastError(r);
uprintf("Could not mount ISO '%s': %s", path, WindowsErrorString());
goto out;
}
r = pfGetVirtualDiskPhysicalPath(mounted_handle, &size, wtmp);
if (r != ERROR_SUCCESS) {
SetLastError(r);
uprintf("Could not obtain physical path for mounted ISO '%s': %s", path, WindowsErrorString());
goto out;
}
wchar_to_utf8_no_alloc(wtmp, physical_path, sizeof(physical_path));
ret = physical_path;
out:
if (ret == NULL)
UnMountISO();
wfree(path);
return ret;
}
void UnMountISO(void)
{
PF_INIT_OR_OUT(DetachVirtualDisk, VirtDisk);
if ((mounted_handle == NULL) || (mounted_handle == INVALID_HANDLE_VALUE))
goto out;
pfDetachVirtualDisk(mounted_handle, DETACH_VIRTUAL_DISK_FLAG_NONE, 0);
safe_closehandle(mounted_handle);
out:
physical_path[0] = 0;
}
// TODO: If we can't get save to ISO from virtdisk, we might as well drop this
static DWORD WINAPI SaveISOThread(void* param)
static DWORD WINAPI IsoSaveImageThread(void* param)
{
BOOL s;
DWORD rSize, wSize;
@ -1908,7 +1833,7 @@ out:
ExitThread(0);
}
void SaveISO(void)
void IsoSaveImage(void)
{
static IMG_SAVE img_save = { 0 };
char filename[33] = "disc_image.iso";
@ -1939,7 +1864,7 @@ void SaveISO(void)
// Disable all controls except cancel
EnableControls(FALSE, FALSE);
InitProgress(TRUE);
format_thread = CreateThread(NULL, 0, SaveISOThread, &img_save, 0, NULL);
format_thread = CreateThread(NULL, 0, IsoSaveImageThread, &img_save, 0, NULL);
if (format_thread != NULL) {
uprintf("\r\nSave to ISO operation started");
PrintInfo(0, -1);

View File

@ -128,7 +128,7 @@ BOOL advanced_mode_device, advanced_mode_format, allow_dual_uefi_bios, detect_fa
BOOL usb_debug, use_fake_units, preserve_timestamps = FALSE, fast_zeroing = FALSE, app_changed_size = FALSE;
BOOL zero_drive = FALSE, list_non_usb_removable_drives = FALSE, enable_file_indexing, large_drive = FALSE;
BOOL write_as_image = FALSE, write_as_esp = FALSE, use_vds = FALSE, ignore_boot_marker = FALSE;
BOOL appstore_version = FALSE, is_vds_available = TRUE, persistent_log = FALSE;
BOOL appstore_version = FALSE, is_vds_available = TRUE, persistent_log = FALSE, has_ffu_support = FALSE;
float fScale = 1.0f;
int dialog_showing = 0, selection_default = BT_IMAGE, persistence_unit_selection = -1, imop_win_sel = 0;
int default_fs, fs_type, boot_type, partition_type, target_type;
@ -2588,8 +2588,11 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
img_provided = FALSE; // One off thing...
} else {
char* old_image_path = image_path;
char extensions[128] = "*.iso;*.img;*.vhd;*.vhdx;*.usb;*.bz2;*.bzip2;*.gz;*.lzma;*.xz;*.Z;*.zip;*.wim;*.esd;*.vtsi";
if (has_ffu_support)
strcat(extensions, ";*.ffu");
// If declared globaly, lmprintf(MSG_036) would be called on each message...
EXT_DECL(img_ext, NULL, __VA_GROUP__("*.iso;*.img;*.vhd;*.usb;*.bz2;*.bzip2;*.gz;*.lzma;*.xz;*.Z;*.zip;*.wim;*.esd;*.vtsi"),
EXT_DECL(img_ext, NULL, __VA_GROUP__(extensions),
__VA_GROUP__(lmprintf(MSG_036)));
image_path = FileDialog(FALSE, NULL, &img_ext, 0);
if (image_path == NULL) {
@ -2684,7 +2687,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
}
break;
case IDC_SAVE:
SaveVHD();
VhdSaveImage();
break;
case IDM_SELECT:
case IDM_DOWNLOAD:
@ -3695,6 +3698,9 @@ skip_args_processing:
// Detect CPU acceleration for SHA-1/SHA-256
cpu_has_sha1_accel = DetectSHA1Acceleration();
cpu_has_sha256_accel = DetectSHA256Acceleration();
// FFU support started with Windows 10 1709 (through FfuProvider.dll)
static_sprintf(tmp_path, "%s\\dism\\FfuProvider.dll", sysnative_dir);
has_ffu_support = (_accessU(tmp_path, 0) == 0);
relaunch:
ubprintf("Localization set to '%s'", selected_locale->txt[0]);
@ -3964,7 +3970,7 @@ extern int TestHashes(void);
}
// Alt-O => Save from Optical drive to ISO
if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'O')) {
SaveISO();
IsoSaveImage();
continue;
}
// Alt-P => Toggle GPT ESP to and from Basic Data type (Windows 10 or later)

View File

@ -652,8 +652,6 @@ extern int64_t ExtractISOFile(const char* iso, const char* iso_file, const char*
extern BOOL CopySKUSiPolicy(const char* drive_name);
extern BOOL HasEfiImgBootLoaders(void);
extern BOOL DumpFatDir(const char* path, int32_t cluster);
extern char* MountISO(const char* path);
extern void UnMountISO(void);
extern BOOL InstallSyslinux(DWORD drive_index, char drive_letter, int fs);
extern uint16_t GetSyslinuxVersion(char* buf, size_t buf_size, char** ext);
extern BOOL SetAutorun(const char* path);

View File

@ -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.2067"
CAPTION "Rufus 4.2.2068"
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,2067,0
PRODUCTVERSION 4,2,2067,0
FILEVERSION 4,2,2068,0
PRODUCTVERSION 4,2,2068,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.2067"
VALUE "FileVersion", "4.2.2068"
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.2067"
VALUE "ProductVersion", "4.2.2068"
END
END
BLOCK "VarFileInfo"

View File

@ -1235,7 +1235,7 @@ uint32_t ResolveDllAddress(dll_resolver_t* resolver)
// Check settings to see if we have existing data for these DLL calls.
for (i = 0; i < resolver->count; i++) {
static_sprintf(saved_id, "%s@%s%x:%s", _filenameU(resolver->path),
GuidToString(&info->Guid, FALSE), info->Age, resolver->name[i]);
GuidToString(&info->Guid, FALSE), (int)info->Age, resolver->name[i]);
resolver->address[i] = ReadSetting32(saved_id);
if (resolver->address[i] == 0)
break;
@ -1253,7 +1253,7 @@ uint32_t ResolveDllAddress(dll_resolver_t* resolver)
goto out;
static_sprintf(path, "%s\\%s", temp_dir, info->PdbName);
static_sprintf(url, "http://msdl.microsoft.com/download/symbols/%s/%s%x/%s",
info->PdbName, GuidToString(&info->Guid, FALSE), info->Age, info->PdbName);
info->PdbName, GuidToString(&info->Guid, FALSE), (int)info->Age, info->PdbName);
if (DownloadToFileOrBufferEx(url, path, SYMBOL_SERVER_USER_AGENT, NULL, hMainDialog, FALSE) < 200 * KB)
goto out;
@ -1280,7 +1280,7 @@ uint32_t ResolveDllAddress(dll_resolver_t* resolver)
r = 0;
for (i = 0; i < resolver->count; i++) {
static_sprintf(saved_id, "%s@%s%x:%s", _filenameU(resolver->path),
GuidToString(&info->Guid, FALSE), info->Age, resolver->name[i]);
GuidToString(&info->Guid, FALSE), (int)info->Age, resolver->name[i]);
if (resolver->address[i] != 0) {
WriteSetting32(saved_id, resolver->address[i]);
r++;

215
src/vhd.c
View File

@ -58,7 +58,7 @@ typedef struct {
uint32_t wim_nb_files, wim_proc_files, wim_extra_files;
HANDLE wim_thread = NULL;
extern int default_thread_priority;
extern BOOL ignore_boot_marker;
extern BOOL ignore_boot_marker, has_ffu_support;
extern RUFUS_DRIVE rufus_drive[MAX_DRIVES];
extern HANDLE format_thread;
@ -67,7 +67,6 @@ static uint32_t progress_report_mask;
static uint64_t progress_offset = 0, progress_total = 100;
static wchar_t wmount_path[MAX_PATH] = { 0 }, wmount_track[MAX_PATH] = { 0 };
static char sevenzip_path[MAX_PATH];
static const char conectix_str[] = VHD_FOOTER_COOKIE;
static BOOL count_files;
static int progress_op = OP_FILE_COPY, progress_msg = MSG_267;
@ -83,7 +82,7 @@ static BOOL Get7ZipPath(void)
typedef struct {
const char* ext;
bled_compression_type type;
uint8_t type;
} comp_assoc;
static comp_assoc file_assoc[] = {
@ -94,33 +93,75 @@ static comp_assoc file_assoc[] = {
{ ".bz2", BLED_COMPRESSION_BZIP2 },
{ ".xz", BLED_COMPRESSION_XZ },
{ ".vtsi", BLED_COMPRESSION_VTSI },
{ ".ffu", BLED_COMPRESSION_MAX },
{ ".vhd", BLED_COMPRESSION_MAX + 1 },
{ ".vhdx", BLED_COMPRESSION_MAX + 2 },
};
// For now we consider that an image that matches a known extension is bootable
// Look for a boot marker in the MBR area of the image
static BOOL IsCompressedBootableImage(const char* path)
{
char *p;
char *ext = NULL, *physical_disk = NULL;
unsigned char *buf = NULL;
int i;
FILE* fd = NULL;
BOOL r = FALSE;
int64_t dc;
int64_t dc = 0;
img_report.compression_type = BLED_COMPRESSION_NONE;
for (p = (char*)&path[strlen(path)-1]; (*p != '.') && (p != path); p--);
if (safe_strlen(path) > 4)
for (ext = (char*)&path[safe_strlen(path) - 1]; (*ext != '.') && (ext != path); ext--);
if (p == path)
return FALSE;
for (i = 0; i<ARRAYSIZE(file_assoc); i++) {
if (strcmp(p, file_assoc[i].ext) == 0) {
for (i = 0; i < ARRAYSIZE(file_assoc); i++) {
if (safe_stricmp(ext, file_assoc[i].ext) == 0) {
img_report.compression_type = file_assoc[i].type;
buf = malloc(MBR_SIZE);
if (buf == NULL)
return FALSE;
FormatStatus = 0;
bled_init(0, uprintf, NULL, NULL, NULL, NULL, &FormatStatus);
dc = bled_uncompress_to_buffer(path, (char*)buf, MBR_SIZE, file_assoc[i].type);
bled_exit();
if (img_report.compression_type < BLED_COMPRESSION_MAX) {
bled_init(0, uprintf, NULL, NULL, NULL, NULL, &FormatStatus);
dc = bled_uncompress_to_buffer(path, (char*)buf, MBR_SIZE, file_assoc[i].type);
bled_exit();
} else if (img_report.compression_type == BLED_COMPRESSION_MAX) {
// Dism, through FfuProvider.dll, can mount a .ffu as a physicaldrive, which we
// could then use to poke the MBR as we do for VHD... Except Microsoft did design
// dism to FAIL AND EXIT, after mounting the ffu as a virtual drive, if it doesn't
// find something that looks like Windows at the specified image index... which it
// usually won't in our case. So, curse Microsoft and their incredible short-
// sightedness (or, most likely in this case, intentional malice, by BREACHING the
// OS contract to keep useful disk APIs for their usage, and their usage only).
// Then again, considering that .ffu's are GPT based, the marker should always be
// present, so just check for the FFU signature and pretend there's a marker then.
if (has_ffu_support) {
fd = fopenU(path, "rb");
if (fd != NULL) {
dc = fread(buf, 1, MBR_SIZE, fd);
fclose(fd);
// The signature may not be constant, but since the only game in town to
// create FFU is dism, and dism appears to use "SignedImage " always,.we
// might as well use this to our advantage.
if (strncmp(&buf[4], "SignedImage ", 12) == 0) {
// At this stage, the buffer is only used for marker validation.
buf[0x1FE] = 0x55;
buf[0x1FF] = 0xAA;
}
} else
uprintf("Could not open %s: %d", path, errno);
} else {
uprintf(" An FFU image was selected, but this system does not have FFU support!");
}
} else {
physical_disk = VhdMountImage(path);
if (physical_disk != NULL) {
fd = fopenU(physical_disk, "rb");
if (fd != NULL) {
dc = fread(buf, 1, MBR_SIZE, fd);
fclose(fd);
}
}
VhdUnmountImage();
}
if (dc != MBR_SIZE) {
free(buf);
return FALSE;
@ -139,10 +180,7 @@ int8_t IsBootableImage(const char* path)
{
HANDLE handle = INVALID_HANDLE_VALUE;
LARGE_INTEGER liImageSize;
vhd_footer* footer = NULL;
DWORD size;
size_t i;
uint32_t checksum, old_checksum;
uint64_t wim_magic = 0;
LARGE_INTEGER ptr = { 0 };
int8_t is_bootable_img;
@ -171,40 +209,7 @@ int8_t IsBootableImage(const char* path)
if (img_report.is_windows_img)
goto out;
size = sizeof(vhd_footer);
if ((img_report.compression_type == BLED_COMPRESSION_NONE) && (img_report.image_size >= (512 + size))) {
footer = (vhd_footer*)malloc(size);
ptr.QuadPart = img_report.image_size - size;
if ( (footer == NULL) || (!SetFilePointerEx(handle, ptr, NULL, FILE_BEGIN)) ||
(!ReadFile(handle, footer, size, &size, NULL)) || (size != sizeof(vhd_footer)) ) {
uprintf(" Could not read VHD footer");
is_bootable_img = -3;
goto out;
}
if (memcmp(footer->cookie, conectix_str, sizeof(footer->cookie)) == 0) {
img_report.image_size -= sizeof(vhd_footer);
if ( (bswap_uint32(footer->file_format_version) != VHD_FOOTER_FILE_FORMAT_V1_0)
|| (bswap_uint32(footer->disk_type) != VHD_FOOTER_TYPE_FIXED_HARD_DISK)) {
uprintf(" Unsupported type of VHD image");
is_bootable_img = 0;
goto out;
}
// Might as well validate the checksum while we're at it
old_checksum = bswap_uint32(footer->checksum);
footer->checksum = 0;
for (checksum = 0, i = 0; i < sizeof(vhd_footer); i++)
checksum += ((uint8_t*)footer)[i];
checksum = ~checksum;
if (checksum != old_checksum)
uprintf(" Warning: VHD footer seems corrupted (checksum: %04X, expected: %04X)", old_checksum, checksum);
// Need to remove the footer from our payload
uprintf(" Image is a Fixed Hard Disk VHD file");
img_report.is_vhd = TRUE;
}
}
out:
safe_free(footer);
safe_closehandle(handle);
return is_bootable_img;
}
@ -405,7 +410,7 @@ char* WimMountImage(const char* image, int index)
mp.index = index;
// Try to unmount an existing stale image, if there is any
mount_path = GetExistingMountPoint(image, index);
mount_path = WimGetExistingMountPoint(image, index);
if (mount_path != NULL) {
uprintf("WARNING: Found stale '%s [%d]' image mounted on '%s' - Attempting to unmount it...",
image, index, mount_path);
@ -499,7 +504,7 @@ BOOL WimUnmountImage(const char* image, int index, BOOL commit)
// This basically parses HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WIMMount\Mounted Images
// to see if an instance exists with the image/index passed as parameter and returns
// the mount point of this image if found, or NULL otherwise.
char* GetExistingMountPoint(const char* image, int index)
char* WimGetExistingMountPoint(const char* image, int index)
{
static char path[MAX_PATH];
char class[MAX_PATH] = "", guid[40], key_name[MAX_PATH];
@ -886,17 +891,102 @@ BOOL WimApplyImage(const char* image, int index, const char* dst)
}
// VirtDisk API Prototypes since we can't use delay-loading because of MinGW
// See https://github.com/pbatard/rufus/issues/2272#issuecomment-1615976013
PF_TYPE_DECL(WINAPI, DWORD, CreateVirtualDisk, (PVIRTUAL_STORAGE_TYPE, PCWSTR,
VIRTUAL_DISK_ACCESS_MASK, PSECURITY_DESCRIPTOR, CREATE_VIRTUAL_DISK_FLAG, ULONG,
PCREATE_VIRTUAL_DISK_PARAMETERS, LPOVERLAPPED, PHANDLE));
PF_TYPE_DECL(WINAPI, DWORD, OpenVirtualDisk, (PVIRTUAL_STORAGE_TYPE, PCWSTR,
VIRTUAL_DISK_ACCESS_MASK, OPEN_VIRTUAL_DISK_FLAG, POPEN_VIRTUAL_DISK_PARAMETERS, PHANDLE));
PF_TYPE_DECL(WINAPI, DWORD, AttachVirtualDisk, (HANDLE, PSECURITY_DESCRIPTOR,
ATTACH_VIRTUAL_DISK_FLAG, ULONG, PATTACH_VIRTUAL_DISK_PARAMETERS, LPOVERLAPPED));
PF_TYPE_DECL(WINAPI, DWORD, DetachVirtualDisk, (HANDLE, DETACH_VIRTUAL_DISK_FLAG, ULONG));
PF_TYPE_DECL(WINAPI, DWORD, GetVirtualDiskPhysicalPath, (HANDLE, PULONG, PWSTR));
PF_TYPE_DECL(WINAPI, DWORD, GetVirtualDiskOperationProgress, (HANDLE, LPOVERLAPPED,
PVIRTUAL_DISK_PROGRESS));
static char physical_path[128] = "";
static HANDLE mounted_handle = INVALID_HANDLE_VALUE;
// Mount an ISO or a VHD/VHDX image
// Returns the physical path of the mounted image or NULL on error.
char* VhdMountImage(const char* path)
{
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];
ULONG size = ARRAYSIZE(wtmp);
wconvert(path);
char *ret = NULL, *ext = NULL;
PF_INIT_OR_OUT(OpenVirtualDisk, VirtDisk);
PF_INIT_OR_OUT(AttachVirtualDisk, VirtDisk);
PF_INIT_OR_OUT(GetVirtualDiskPhysicalPath, VirtDisk);
if (wpath == NULL)
return NULL;
if ((mounted_handle != NULL) && (mounted_handle != INVALID_HANDLE_VALUE))
VhdUnmountImage();
if (safe_strlen(path) > 4)
for (ext = (char*)&path[safe_strlen(path) - 1]; (*ext != '.') && (ext != path); ext--);
if (safe_stricmp(ext, ".vhdx") == 0)
vtype.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_VHDX;
else if (safe_stricmp(ext, ".vhdx") == 0)
vtype.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_VHD;
r = pfOpenVirtualDisk(&vtype, wpath, VIRTUAL_DISK_ACCESS_READ | VIRTUAL_DISK_ACCESS_GET_INFO,
OPEN_VIRTUAL_DISK_FLAG_NONE, NULL, &mounted_handle);
if (r != ERROR_SUCCESS) {
SetLastError(r);
uprintf("Could not open image '%s': %s", path, WindowsErrorString());
goto out;
}
vparams.Version = ATTACH_VIRTUAL_DISK_VERSION_1;
r = pfAttachVirtualDisk(mounted_handle, NULL, ATTACH_VIRTUAL_DISK_FLAG_READ_ONLY |
ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER, 0, &vparams, NULL);
if (r != ERROR_SUCCESS) {
SetLastError(r);
uprintf("Could not mount image '%s': %s", path, WindowsErrorString());
goto out;
}
r = pfGetVirtualDiskPhysicalPath(mounted_handle, &size, wtmp);
if (r != ERROR_SUCCESS) {
SetLastError(r);
uprintf("Could not obtain physical path for mounted image '%s': %s", path, WindowsErrorString());
goto out;
}
wchar_to_utf8_no_alloc(wtmp, physical_path, sizeof(physical_path));
ret = physical_path;
out:
if (ret == NULL)
VhdUnmountImage();
wfree(path);
return ret;
}
void VhdUnmountImage(void)
{
PF_INIT_OR_OUT(DetachVirtualDisk, VirtDisk);
if ((mounted_handle == NULL) || (mounted_handle == INVALID_HANDLE_VALUE))
goto out;
pfDetachVirtualDisk(mounted_handle, DETACH_VIRTUAL_DISK_FLAG_NONE, 0);
safe_closehandle(mounted_handle);
out:
physical_path[0] = 0;
}
// Since we no longer have to deal with Windows 7, we can call on CreateVirtualDisk()
// 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 SaveVHDThread(void* param)
static DWORD WINAPI VhdSaveImageThread(void* param)
{
IMG_SAVE* img_save = (IMG_SAVE*)param;
HANDLE handle = INVALID_HANDLE_VALUE;
@ -984,21 +1074,21 @@ out:
// 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)
static DWORD WINAPI FfuSaveImageThread(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)\"",
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());
uprintf("Failed to capture FFU image: %s", WindowsErrorString());
FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_WINDOWS) | SCODE_CODE(r);
}
safe_free(img_save->DevicePath);
@ -1007,7 +1097,7 @@ static DWORD WINAPI SaveFFUThread(void* param)
ExitThread(r);
}
void SaveVHD(void)
void VhdSaveImage(void)
{
static IMG_SAVE img_save = { 0 };
char filename[128];
@ -1023,9 +1113,8 @@ 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 (through FfuProvider.dll) and requires GPT
static_sprintf(path, "%s\\dism\\FfuProvider.dll", sysnative_dir);
if ((_accessU(path, 0) != 0) || SelectedDrive.PartitionStyle != PARTITION_STYLE_GPT)
// FFU support requires GPT
if (!has_ffu_support || SelectedDrive.PartitionStyle != PARTITION_STYLE_GPT)
img_ext.count = 2;
img_save.ImagePath = FileDialog(TRUE, NULL, &img_ext, 0);
img_save.Type = VIRTUAL_STORAGE_TYPE_DEVICE_VHD;
@ -1048,7 +1137,7 @@ void SaveVHD(void)
FormatStatus = 0;
InitProgress(TRUE);
format_thread = CreateThread(NULL, 0, img_save.Type == VIRTUAL_STORAGE_TYPE_DEVICE_FFU ?
SaveFFUThread : SaveVHDThread, &img_save, 0, NULL);
FfuSaveImageThread : VhdSaveImageThread, &img_save, 0, NULL);
if (format_thread != NULL) {
uprintf("\r\nSave to VHD operation started");
PrintInfo(0, -1);

View File

@ -23,23 +23,6 @@
#pragma once
#define VHD_FOOTER_COOKIE { 'c', 'o', 'n', 'e', 'c', 't', 'i', 'x' }
#define VHD_FOOTER_FEATURES_NONE 0x00000000
#define VHD_FOOTER_FEATURES_TEMPORARY 0x00000001
#define VHD_FOOTER_FEATURES_RESERVED 0x00000002
#define VHD_FOOTER_FILE_FORMAT_V1_0 0x00010000
#define VHD_FOOTER_DATA_OFFSET_FIXED_DISK 0xFFFFFFFFFFFFFFFFULL
#define VHD_FOOTER_CREATOR_HOST_OS_WINDOWS { 'W', 'i', '2', 'k' }
#define VHD_FOOTER_CREATOR_HOST_OS_MAC { 'M', 'a', 'c', ' ' }
#define VHD_FOOTER_TYPE_FIXED_HARD_DISK 0x00000002
#define VHD_FOOTER_TYPE_DYNAMIC_HARD_DISK 0x00000003
#define VHD_FOOTER_TYPE_DIFFER_HARD_DISK 0x00000004
#define WIM_MAGIC 0x0000004D4957534DULL // "MSWIM\0\0\0"
#define WIM_HAS_API_EXTRACT 1
#define WIM_HAS_7Z_EXTRACT 2
@ -142,40 +125,6 @@ enum WIMMessage {
WIM_MSG_ABORT_IMAGE = -1
};
/*
* VHD Fixed HD footer (Big Endian)
* http://download.microsoft.com/download/f/f/e/ffef50a5-07dd-4cf8-aaa3-442c0673a029/Virtual%20Hard%20Disk%20Format%20Spec_10_18_06.doc
* NB: If a dymamic implementation is needed, check the GPL v3 compatible C++ implementation from:
* https://sourceforge.net/p/urbackup/backend/ci/master/tree/fsimageplugin/
*/
#pragma pack(push, 1)
typedef struct vhd_footer {
char cookie[8];
uint32_t features;
uint32_t file_format_version;
uint64_t data_offset;
uint32_t timestamp;
char creator_app[4];
uint32_t creator_version;
char creator_host_os[4];
uint64_t original_size;
uint64_t current_size;
union {
uint32_t geometry;
struct {
uint16_t cylinders;
uint8_t heads;
uint8_t sectors;
} chs;
} disk_geometry;
uint32_t disk_type;
uint32_t checksum;
uuid_t unique_id;
uint8_t saved_state;
uint8_t reserved[427];
} vhd_footer;
#pragma pack(pop)
extern uint8_t WimExtractCheck(BOOL bSilent);
extern BOOL WimExtractFile(const char* wim_image, int index, const char* src, const char* dst, BOOL bSilent);
extern BOOL WimExtractFile_API(const char* image, int index, const char* src, const char* dst, BOOL bSilent);
@ -183,8 +132,10 @@ extern BOOL WimExtractFile_7z(const char* image, int index, const char* src, con
extern BOOL WimApplyImage(const char* image, int index, const char* dst);
extern char* WimMountImage(const char* image, int index);
extern BOOL WimUnmountImage(const char* image, int index, BOOL commit);
extern char* GetExistingMountPoint(const char* image, int index);
extern char* WimGetExistingMountPoint(const char* image, int index);
extern BOOL WimIsValidIndex(const char* image, int index);
extern int8_t IsBootableImage(const char* path);
extern void SaveVHD(void);
extern void SaveISO(void);
extern char* VhdMountImage(const char* path);
extern void VhdUnmountImage(void);
extern void VhdSaveImage(void);
extern void IsoSaveImage(void);

View File

@ -440,7 +440,7 @@ BOOL PopulateWindowsVersion(void)
// If we're not using a straight install.wim, we need to mount the ISO to access it
if (!img_report.is_windows_img) {
mounted_iso = MountISO(image_path);
mounted_iso = VhdMountImage(image_path);
if (mounted_iso == NULL) {
uprintf("Could not mount Windows ISO for build number detection");
return FALSE;
@ -468,7 +468,7 @@ BOOL PopulateWindowsVersion(void)
out:
DeleteFileU(xml_file);
if (!img_report.is_windows_img)
UnMountISO();
VhdUnmountImage();
return ((img_report.win_version.major != 0) && (img_report.win_version.build != 0));
}
@ -532,7 +532,7 @@ int SetWinToGoIndex(void)
// If we're not using a straight install.wim, we need to mount the ISO to access it
if (!img_report.is_windows_img) {
mounted_iso = MountISO(image_path);
mounted_iso = VhdMountImage(image_path);
if (mounted_iso == NULL) {
uprintf("Could not mount ISO for Windows To Go selection");
return -1;
@ -613,7 +613,7 @@ int SetWinToGoIndex(void)
out:
DeleteFileU(xml_file);
if (!img_report.is_windows_img)
UnMountISO();
VhdUnmountImage();
return wintogo_index;
}
@ -640,7 +640,7 @@ BOOL SetupWinToGo(DWORD DriveIndex, const char* drive_name, BOOL use_esp)
}
if (!img_report.is_windows_img) {
mounted_iso = MountISO(image_path);
mounted_iso = VhdMountImage(image_path);
if (mounted_iso == NULL) {
uprintf("Could not mount ISO for Windows To Go installation");
FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_ISO_EXTRACT);
@ -656,11 +656,11 @@ BOOL SetupWinToGo(DWORD DriveIndex, const char* drive_name, BOOL use_esp)
if (!IS_ERROR(FormatStatus))
FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_ISO_EXTRACT);
if (!img_report.is_windows_img)
UnMountISO();
VhdUnmountImage();
return FALSE;
}
if (!img_report.is_windows_img)
UnMountISO();
VhdUnmountImage();
if (use_esp) {
uprintf("Setting up EFI System Partition");