mirror of
https://github.com/pbatard/rufus.git
synced 2025-05-29 05:55:30 -04:00
[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:
parent
f411d526d6
commit
5bbcba8534
10 changed files with 226 additions and 223 deletions
215
src/vhd.c
215
src/vhd.c
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue