From 5bbcba8534ac847df3191f44bd2757a3911ff172 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Thu, 6 Jul 2023 19:47:26 +0100 Subject: [PATCH] [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. --- src/format.c | 42 ++++++++-- src/format.h | 6 +- src/iso.c | 81 +------------------ src/rufus.c | 14 +++- src/rufus.h | 2 - src/rufus.rc | 10 +-- src/stdio.c | 6 +- src/vhd.c | 215 ++++++++++++++++++++++++++++++++++++--------------- src/vhd.h | 59 ++------------ src/wue.c | 14 ++-- 10 files changed, 226 insertions(+), 223 deletions(-) diff --git a/src/format.c b/src/format.c index ea43ce8e..e955f40e 100644 --- a/src/format.c +++ b/src/format.c @@ -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; } diff --git a/src/format.h b/src/format.h index 8bc514e8..c60b38d5 100644 --- a/src/format.h +++ b/src/format.h @@ -1,7 +1,7 @@ /* * Rufus: The Reliable USB Formatting Utility * Formatting function calls - * Copyright © 2011-2020 Pete Batard + * Copyright © 2011-2023 Pete Batard * * 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); diff --git a/src/iso.c b/src/iso.c index 1b01d655..25c9b670 100644 --- a/src/iso.c +++ b/src/iso.c @@ -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); diff --git a/src/rufus.c b/src/rufus.c index 0855d345..3acf3ae2 100755 --- a/src/rufus.c +++ b/src/rufus.c @@ -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) diff --git a/src/rufus.h b/src/rufus.h index 84ec91a3..53dc6251 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -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); diff --git a/src/rufus.rc b/src/rufus.rc index c468a3d4..681afaa9 100644 --- a/src/rufus.rc +++ b/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.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" diff --git a/src/stdio.c b/src/stdio.c index 073636ce..eaef1810 100644 --- a/src/stdio.c +++ b/src/stdio.c @@ -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++; diff --git a/src/vhd.c b/src/vhd.c index 6c6abfc0..1823fc84 100644 --- a/src/vhd.c +++ b/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= (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); diff --git a/src/vhd.h b/src/vhd.h index 6609b4aa..65ac06d9 100644 --- a/src/vhd.h +++ b/src/vhd.h @@ -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); diff --git a/src/wue.c b/src/wue.c index f0423312..62970dfa 100644 --- a/src/wue.c +++ b/src/wue.c @@ -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");