From d4c9f2dfa15e0abd87c57dbe0740b985a00ba3a7 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Sat, 1 Jul 2023 20:39:28 +0100 Subject: [PATCH] [vhd] enable saving device to compressed VHDX * Now that we don't have to deal with Windows 7, we can use CreateVirtualDisk() to automatically dump a physical disk to VHD/VHDX, so do just that * Also move the relevant VHD/ISO imaging call to the appropriate source. --- .mingw/virtdisk.def | 2 + res/loc/rufus.loc | 2 + src/format.c | 123 ---------------------- src/iso.c | 156 +++++++++++++++++++++++++++- src/missing.h | 4 - src/net.c | 3 +- src/rufus.c | 104 +------------------ src/rufus.h | 6 +- src/rufus.rc | 10 +- src/ui.h | 2 + src/vhd.c | 241 ++++++++++++++++++++++++++------------------ src/vhd.h | 47 ++++++++- 12 files changed, 361 insertions(+), 339 deletions(-) diff --git a/.mingw/virtdisk.def b/.mingw/virtdisk.def index 5b727b9d..4476016c 100644 --- a/.mingw/virtdisk.def +++ b/.mingw/virtdisk.def @@ -3,3 +3,5 @@ EXPORTS GetVirtualDiskPhysicalPath@12 DetachVirtualDisk@12 OpenVirtualDisk@24 + CreateVirtualDisk@36 + GetVirtualDiskOperationProgress@12 diff --git a/res/loc/rufus.loc b/res/loc/rufus.loc index ba7a4877..50295d9e 100644 --- a/res/loc/rufus.loc +++ b/res/loc/rufus.loc @@ -603,6 +603,8 @@ t MSG_339 "Rufus detected that the ISO you have selected contains a UEFI bootloa "- If you obtained it from a trusted source, you should try to locate a more up to date version, that will not produce this warning." 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" # 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" diff --git a/src/format.c b/src/format.c index 36eb270c..ea43ce8e 100644 --- a/src/format.c +++ b/src/format.c @@ -1977,126 +1977,3 @@ out: PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)TRUE, 0); ExitThread(0); } - -DWORD WINAPI SaveImageThread(void* param) -{ - BOOL s; - DWORD rSize, wSize; - IMG_SAVE *img_save = (IMG_SAVE*)param; - HANDLE hPhysicalDrive = INVALID_HANDLE_VALUE; - HANDLE hDestImage = INVALID_HANDLE_VALUE; - LARGE_INTEGER li; - uint8_t *buffer = NULL; - uint64_t wb; - int i; - - PrintInfoDebug(0, MSG_225); - switch (img_save->Type) { - case IMG_SAVE_TYPE_VHD: - hPhysicalDrive = GetPhysicalHandle(img_save->DeviceNum, TRUE, FALSE, FALSE); - break; - case IMG_SAVE_TYPE_ISO: - hPhysicalDrive = CreateFileA(img_save->DevicePath, GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); - break; - default: - uprintf("Invalid image type"); - } - if (hPhysicalDrive == INVALID_HANDLE_VALUE) { - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; - goto out; - } - - // Write an image file - // We may have poked the MBR and other stuff, so need to rewind - li.QuadPart = 0; - if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN)) - uprintf("Warning: Unable to rewind device position - wrong data might be copied!"); - hDestImage = CreateFileU(img_save->ImagePath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (hDestImage == INVALID_HANDLE_VALUE) { - uprintf("Could not open image '%s': %s", img_save->ImagePath, WindowsErrorString()); - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; - goto out; - } - - buffer = (uint8_t*)_mm_malloc(img_save->BufSize, 16); - if (buffer == NULL) { - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_ENOUGH_MEMORY; - uprintf("could not allocate buffer"); - goto out; - } - - uprintf("Will use a buffer size of %s", SizeToHumanReadable(img_save->BufSize, FALSE, FALSE)); - uprintf("Saving to image '%s'...", img_save->ImagePath); - - // Don't bother trying for something clever, using double buffering overlapped and whatnot: - // With Windows' default optimizations, sync read + sync write for sequential operations - // will be as fast, if not faster, than whatever async scheme you can come up with. - UpdateProgressWithInfoInit(NULL, FALSE); - for (wb = 0; ; wb += wSize) { - if (img_save->Type == IMG_SAVE_TYPE_ISO) { - // Optical drives do not appear to increment the sectors to read automatically - li.QuadPart = wb; - if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN)) - uprintf("Warning: Unable to set device position - wrong data might be copied!"); - } - s = ReadFile(hPhysicalDrive, buffer, - (DWORD)MIN(img_save->BufSize, img_save->DeviceSize - wb), &rSize, NULL); - if (!s) { - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_READ_FAULT; - uprintf("Read error: %s", WindowsErrorString()); - goto out; - } - if (rSize == 0) - break; - UpdateProgressWithInfo(OP_FORMAT, MSG_261, wb, img_save->DeviceSize); - for (i = 1; i <= WRITE_RETRIES; i++) { - CHECK_FOR_USER_CANCEL; - s = WriteFile(hDestImage, buffer, rSize, &wSize, NULL); - if ((s) && (wSize == rSize)) - break; - if (s) - uprintf("Write error: Wrote %d bytes, expected %d bytes", wSize, rSize); - else - uprintf("Write error: %s", WindowsErrorString()); - if (i < WRITE_RETRIES) { - li.QuadPart = wb; - uprintf("Retrying in %d seconds...", WRITE_TIMEOUT / 1000); - Sleep(WRITE_TIMEOUT); - if (!SetFilePointerEx(hDestImage, li, NULL, FILE_BEGIN)) { - uprintf("Write error: Could not reset position - %s", WindowsErrorString()); - goto out; - } - } else { - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; - goto out; - } - Sleep(200); - } - if (i > WRITE_RETRIES) - goto out; - } - if (wb != img_save->DeviceSize) { - uprintf("Error: wrote %s, expected %s", SizeToHumanReadable(wb, FALSE, FALSE), - SizeToHumanReadable(img_save->DeviceSize, FALSE, FALSE)); - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; - goto out; - } - if (img_save->Type == IMG_SAVE_TYPE_VHD) { - uprintf("Appending VHD footer..."); - if (!AppendVHDFooter(img_save->ImagePath)) { - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; - goto out; - } - } - uprintf("Operation complete (Wrote %s).", SizeToHumanReadable(wb, FALSE, FALSE)); - -out: - safe_free(img_save->ImagePath); - safe_mm_free(buffer); - safe_closehandle(hDestImage); - safe_unlockclose(hPhysicalDrive); - PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)TRUE, 0); - ExitThread(0); -} diff --git a/src/iso.c b/src/iso.c index 19c85ceb..4372a249 100644 --- a/src/iso.c +++ b/src/iso.c @@ -43,6 +43,8 @@ #include #include "rufus.h" +#include "ui.h" +#include "drive.h" #include "libfat.h" #include "missing.h" #include "resource.h" @@ -82,6 +84,7 @@ RUFUS_IMG_REPORT img_report; int64_t iso_blocking_status = -1; extern BOOL preserve_timestamps, enable_ntfs_compression; extern char* archive_path; +extern HANDLE format_thread; BOOL enable_iso = TRUE, enable_joliet = TRUE, enable_rockridge = TRUE, has_ldlinux_c32; #define ISO_BLOCKING(x) do {x; iso_blocking_status++; } while(0) static const char* psz_extract_dir; @@ -1713,7 +1716,7 @@ static HANDLE mounted_handle = INVALID_HANDLE_VALUE; char* MountISO(const char* path) { VIRTUAL_STORAGE_TYPE vtype = { 1, VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT }; - ATTACH_VIRTUAL_DISK_PARAMETERS vparams = {0}; + ATTACH_VIRTUAL_DISK_PARAMETERS vparams = { 0 }; DWORD r; wchar_t wtmp[128]; ULONG size = ARRAYSIZE(wtmp); @@ -1766,3 +1769,154 @@ void UnMountISO(void) 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) +{ + BOOL s; + DWORD rSize, wSize; + IMG_SAVE* img_save = (IMG_SAVE*)param; + HANDLE hPhysicalDrive = INVALID_HANDLE_VALUE; + HANDLE hDestImage = INVALID_HANDLE_VALUE; + LARGE_INTEGER li; + uint8_t* buffer = NULL; + uint64_t wb; + int i; + + assert(img_save->Type == VIRTUAL_STORAGE_TYPE_DEVICE_ISO); + + PrintInfoDebug(0, MSG_225); + hPhysicalDrive = CreateFileA(img_save->DevicePath, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (hPhysicalDrive == INVALID_HANDLE_VALUE) { + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_OPEN_FAILED; + goto out; + } + + // In case someone poked the disc before us + li.QuadPart = 0; + if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN)) + uprintf("Warning: Unable to rewind device position - wrong data might be copied!"); + hDestImage = CreateFileU(img_save->ImagePath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hDestImage == INVALID_HANDLE_VALUE) { + uprintf("Could not open image '%s': %s", img_save->ImagePath, WindowsErrorString()); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_OPEN_FAILED; + goto out; + } + + buffer = (uint8_t*)_mm_malloc(img_save->BufSize, 16); + if (buffer == NULL) { + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_NOT_ENOUGH_MEMORY; + uprintf("Could not allocate buffer"); + goto out; + } + + uprintf("Will use a buffer size of %s", SizeToHumanReadable(img_save->BufSize, FALSE, FALSE)); + uprintf("Saving to image '%s'...", img_save->ImagePath); + + // Don't bother trying for something clever, using double buffering overlapped and whatnot: + // With Windows' default optimizations, sync read + sync write for sequential operations + // will be as fast, if not faster, than whatever async scheme you can come up with. + UpdateProgressWithInfoInit(NULL, FALSE); + for (wb = 0; ; wb += wSize) { + // Optical drives do not appear to increment the sectors to read automatically + li.QuadPart = wb; + if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN)) + uprintf("Warning: Unable to set device position - wrong data might be copied!"); + s = ReadFile(hPhysicalDrive, buffer, + (DWORD)MIN(img_save->BufSize, img_save->DeviceSize - wb), &rSize, NULL); + if (!s) { + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_READ_FAULT; + uprintf("Read error: %s", WindowsErrorString()); + goto out; + } + if (rSize == 0) + break; + UpdateProgressWithInfo(OP_FORMAT, MSG_261, wb, img_save->DeviceSize); + for (i = 1; i <= WRITE_RETRIES; i++) { + CHECK_FOR_USER_CANCEL; + s = WriteFile(hDestImage, buffer, rSize, &wSize, NULL); + if ((s) && (wSize == rSize)) + break; + if (s) + uprintf("Write error: Wrote %d bytes, expected %d bytes", wSize, rSize); + else + uprintf("Write error: %s", WindowsErrorString()); + if (i < WRITE_RETRIES) { + li.QuadPart = wb; + uprintf("Retrying in %d seconds...", WRITE_TIMEOUT / 1000); + Sleep(WRITE_TIMEOUT); + if (!SetFilePointerEx(hDestImage, li, NULL, FILE_BEGIN)) { + uprintf("Write error: Could not reset position - %s", WindowsErrorString()); + goto out; + } + } else { + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_WRITE_FAULT; + goto out; + } + Sleep(200); + } + if (i > WRITE_RETRIES) + goto out; + } + if (wb != img_save->DeviceSize) { + uprintf("Error: wrote %s, expected %s", SizeToHumanReadable(wb, FALSE, FALSE), + SizeToHumanReadable(img_save->DeviceSize, FALSE, FALSE)); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_WRITE_FAULT; + goto out; + } + uprintf("Operation complete (Wrote %s).", SizeToHumanReadable(wb, FALSE, FALSE)); + +out: + safe_free(img_save->ImagePath); + safe_mm_free(buffer); + safe_closehandle(hDestImage); + safe_unlockclose(hPhysicalDrive); + PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)TRUE, 0); + ExitThread(0); +} + +void SaveISO(void) +{ + static IMG_SAVE img_save = { 0 }; + char filename[33] = "disc_image.iso"; + EXT_DECL(img_ext, filename, __VA_GROUP__("*.iso"), __VA_GROUP__(lmprintf(MSG_036))); + + if (op_in_progress || (format_thread != NULL)) + return; + + img_save.Type = VIRTUAL_STORAGE_TYPE_DEVICE_ISO; + if (!GetOpticalMedia(&img_save)) { + uprintf("No dumpable optical media found."); + return; + } + // Adjust the buffer size according to the disc size so that we get a decent speed. + for (img_save.BufSize = 32 * MB; + (img_save.BufSize > 8 * MB) && (img_save.DeviceSize <= img_save.BufSize * 64); + img_save.BufSize /= 2); + if ((img_save.Label != NULL) && (img_save.Label[0] != 0)) + static_sprintf(filename, "%s.iso", img_save.Label); + + img_save.ImagePath = FileDialog(TRUE, NULL, &img_ext, 0); + if (img_save.ImagePath == NULL) + return; + + uprintf("ISO media size %s", SizeToHumanReadable(img_save.DeviceSize, FALSE, FALSE)); + SendMessage(hMainDialog, UM_PROGRESS_INIT, 0, 0); + FormatStatus = 0; + // Disable all controls except cancel + EnableControls(FALSE, FALSE); + InitProgress(TRUE); + format_thread = CreateThread(NULL, 0, SaveISOThread, &img_save, 0, NULL); + if (format_thread != NULL) { + uprintf("\r\nSave to ISO operation started"); + PrintInfo(0, -1); + SendMessage(hMainDialog, UM_TIMER_START, 0, 0); + } else { + uprintf("Unable to start ISO save thread"); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_CANT_START_THREAD); + safe_free(img_save.ImagePath); + PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)FALSE, 0); + } +} diff --git a/src/missing.h b/src/missing.h index 17bdc605..54d6e342 100644 --- a/src/missing.h +++ b/src/missing.h @@ -185,10 +185,6 @@ static __inline uint16_t remap16(uint16_t src, uint16_t* map, const BOOL reverse #define ERROR_OFFSET_ALIGNMENT_VIOLATION 327 #endif -/* The following is used for native ISO mounting in Windows 8 or later */ -#define VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT \ - { 0xEC984AECL, 0xA0F9, 0x47e9, { 0x90, 0x1F, 0x71, 0x41, 0x5A, 0x66, 0x34, 0x5B } } - /* RISC-V is still bleeding edge */ #ifndef IMAGE_FILE_MACHINE_RISCV32 #define IMAGE_FILE_MACHINE_RISCV32 0x5032 diff --git a/src/net.c b/src/net.c index f0ce93e9..a6748cb7 100644 --- a/src/net.c +++ b/src/net.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "rufus.h" #include "missing.h" @@ -1038,7 +1039,7 @@ static DWORD WINAPI DownloadISOThread(LPVOID param) #pragma warning(default: 6386) #endif EXT_DECL(img_ext, GetShortName(url), __VA_GROUP__("*.iso"), __VA_GROUP__(lmprintf(MSG_036))); - img_save.Type = IMG_SAVE_TYPE_ISO; + img_save.Type = VIRTUAL_STORAGE_TYPE_DEVICE_ISO; img_save.ImagePath = FileDialog(TRUE, NULL, &img_ext, 0); if (img_save.ImagePath == NULL) { goto out; diff --git a/src/rufus.c b/src/rufus.c index 8cf95d29..7f5e70ca 100755 --- a/src/rufus.c +++ b/src/rufus.c @@ -87,7 +87,6 @@ static int selected_pt = -1, selected_fs = FS_UNKNOWN, preselected_fs = FS_UNKNO static int image_index = 0, select_index = 0; static RECT relaunch_rc = { -65536, -65536, 0, 0}; static UINT uMBRChecked = BST_UNCHECKED; -static HANDLE format_thread = NULL; static HWND hSelectImage = NULL, hStart = NULL; static char szTimer[12] = "00:00:00"; static unsigned int timer; @@ -121,7 +120,7 @@ WORD selected_langid = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); DWORD MainThreadId; HWND hDeviceList, hPartitionScheme, hTargetSystem, hFileSystem, hClusterSize, hLabel, hBootType, hNBPasses, hLog = NULL; HWND hImageOption, hLogDialog = NULL, hProgress = NULL, hDiskID; -HANDLE dialog_handle = NULL; +HANDLE dialog_handle = NULL, format_thread = NULL; BOOL is_x86_64, use_own_c32[NB_OLD_C32] = { FALSE, FALSE }, mbr_selected_by_user = FALSE, lock_drive = TRUE; BOOL op_in_progress = TRUE, right_to_left_mode = FALSE, has_uefi_csm = FALSE, its_a_me_mario = FALSE; BOOL enable_HDDs = FALSE, enable_VHDs = TRUE, enable_ntfs_compression = FALSE, no_confirmation_on_cancel = FALSE; @@ -862,7 +861,7 @@ static void EnableBootOptions(BOOL enable, BOOL remove_checkboxes) } // Toggle controls according to operation -static void EnableControls(BOOL enable, BOOL remove_checkboxes) +void EnableControls(BOOL enable, BOOL remove_checkboxes) { op_in_progress = !enable; @@ -2161,104 +2160,6 @@ static void PrintStatusTimeout(const char* str, BOOL val) PrintStatus(STATUS_MSG_TIMEOUT, (val)?MSG_250:MSG_251, str); } -static void SaveVHD(void) -{ - static IMG_SAVE img_save = { 0 }; - char filename[128]; - char path[MAX_PATH]; - int DriveIndex = ComboBox_GetCurSel(hDeviceList); - EXT_DECL(img_ext, filename, __VA_GROUP__("*.vhd"), __VA_GROUP__(lmprintf(MSG_095))); - ULARGE_INTEGER free_space; - - if ((DriveIndex < 0) || (format_thread != NULL)) - return; - - static_sprintf(filename, "%s.vhd", rufus_drive[DriveIndex].label); - img_save.Type = IMG_SAVE_TYPE_VHD; - img_save.DeviceNum = (DWORD)ComboBox_GetItemData(hDeviceList, DriveIndex); - img_save.ImagePath = FileDialog(TRUE, NULL, &img_ext, 0); - img_save.BufSize = DD_BUFFER_SIZE; - img_save.DeviceSize = SelectedDrive.DiskSize; - if (img_save.ImagePath != NULL) { - // Reset all progress bars - SendMessage(hMainDialog, UM_PROGRESS_INIT, 0, 0); - FormatStatus = 0; - free_space.QuadPart = 0; - if ((GetVolumePathNameA(img_save.ImagePath, path, sizeof(path))) - && (GetDiskFreeSpaceExA(path, &free_space, NULL, NULL)) - && ((LONGLONG)free_space.QuadPart > (SelectedDrive.DiskSize + 512))) { - // Disable all controls except cancel - EnableControls(FALSE, FALSE); - FormatStatus = 0; - InitProgress(TRUE); - format_thread = CreateThread(NULL, 0, SaveImageThread, &img_save, 0, NULL); - if (format_thread != NULL) { - uprintf("\r\nSave to VHD operation started"); - PrintInfo(0, -1); - SendMessage(hMainDialog, UM_TIMER_START, 0, 0); - } else { - uprintf("Unable to start VHD save thread"); - FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_CANT_START_THREAD); - safe_free(img_save.ImagePath); - PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)FALSE, 0); - } - } else { - if (free_space.QuadPart == 0) { - uprintf("Unable to isolate drive name for VHD save"); - FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_PATH_NOT_FOUND; - } else { - uprintf("The VHD size is too large for the target drive"); - FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_FILE_TOO_LARGE; - } - safe_free(img_save.ImagePath); - PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)FALSE, 0); - } - } -} - -static void SaveISO(void) -{ - static IMG_SAVE img_save = { 0 }; - char filename[33] = "disc_image.iso"; - EXT_DECL(img_ext, filename, __VA_GROUP__("*.iso"), __VA_GROUP__(lmprintf(MSG_036))); - - if (op_in_progress || (format_thread != NULL)) - return; - - img_save.Type = IMG_SAVE_TYPE_ISO; - if (!GetOpticalMedia(&img_save)) { - uprintf("No dumpable optical media found."); - return; - } - // Adjust the buffer size according to the disc size so that we get a decent speed. - for (img_save.BufSize = 32 * MB; - (img_save.BufSize > 8 * MB) && (img_save.DeviceSize <= img_save.BufSize * 64); - img_save.BufSize /= 2); - if ((img_save.Label != NULL) && (img_save.Label[0] != 0)) - static_sprintf(filename, "%s.iso", img_save.Label); - uprintf("ISO media size %s", SizeToHumanReadable(img_save.DeviceSize, FALSE, FALSE)); - - img_save.ImagePath = FileDialog(TRUE, NULL, &img_ext, 0); - if (img_save.ImagePath == NULL) - return; - SendMessage(hMainDialog, UM_PROGRESS_INIT, 0, 0); - FormatStatus = 0; - // Disable all controls except cancel - EnableControls(FALSE, FALSE); - InitProgress(TRUE); - format_thread = CreateThread(NULL, 0, SaveImageThread, &img_save, 0, NULL); - if (format_thread != NULL) { - uprintf("\r\nSave to ISO operation started"); - PrintInfo(0, -1); - SendMessage(hMainDialog, UM_TIMER_START, 0, 0); - } else { - uprintf("Unable to start ISO save thread"); - FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_CANT_START_THREAD); - safe_free(img_save.ImagePath); - PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)FALSE, 0); - } -} - // Check for conflicting processes accessing the drive. // If bPrompt is true, ask the user whether they want to proceed. // dwTimeOut is the maximum amount of time we allow for this call to execute (in ms) @@ -2764,6 +2665,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA MyDialogBox(hMainInstance, IDD_UPDATE_POLICY, hDlg, UpdateCallback); break; case IDC_HASH: + // TODO: Move this as a fn call in hash.c? if ((format_thread == NULL) && (image_path != NULL)) { FormatStatus = 0; no_confirmation_on_cancel = TRUE; diff --git a/src/rufus.h b/src/rufus.h index dddb5851..b5c24ca2 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -71,6 +71,7 @@ #define PATCH_PROGRESS_TOTAL 207 #define MAX_LOG_SIZE 0x7FFFFFFE #define MAX_REFRESH 25 // How long we should wait to refresh UI elements (in ms) +#define MARQUEE_TIMER_REFRESH 10 // Time between progress bar marquee refreshes, in ms #define MAX_GUID_STRING_LENGTH 40 #define MAX_PARTITIONS 16 // Maximum number of partitions we handle #define MAX_ESP_TOGGLE 8 // Maximum number of entries we record to toggle GPT ESP back and forth @@ -88,7 +89,6 @@ #define WRITE_TIMEOUT 5000 // How long we should wait between write retries (in ms) #define SEARCH_PROCESS_TIMEOUT 10000 // How long we should search for conflicting processes before giving up (in ms) #define NET_SESSION_TIMEOUT 3500 // How long we should wait to connect, send or receive internet data -#define MARQUEE_TIMER_REFRESH 10 // Time between progress bar marquee refreshes, in ms #define FS_DEFAULT FS_FAT32 #define SINGLE_CLUSTERSIZE_DEFAULT 0x00000100 #define BADLOCKS_PATTERN_TYPES 5 @@ -433,9 +433,6 @@ typedef struct { char* release_notes; } RUFUS_UPDATE; -#define IMG_SAVE_TYPE_VHD 1 -#define IMG_SAVE_TYPE_ISO 2 - typedef struct { DWORD Type; DWORD DeviceNum; @@ -726,7 +723,6 @@ extern HANDLE CreatePreallocatedFile(const char* lpFileName, DWORD dwDesiredAcce DWORD dwFlagsAndAttributes, LONGLONG fileSize); #define GetTextWidth(hDlg, id) GetTextSize(GetDlgItem(hDlg, id), NULL).cx -DWORD WINAPI SaveImageThread(void* param); DWORD WINAPI HashThread(void* param); /* Hash tables */ diff --git a/src/rufus.rc b/src/rufus.rc index 3140cb7a..60c3802b 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.2059" +CAPTION "Rufus 4.2.2060" 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,2059,0 - PRODUCTVERSION 4,2,2059,0 + FILEVERSION 4,2,2060,0 + PRODUCTVERSION 4,2,2060,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.2059" + VALUE "FileVersion", "4.2.2060" 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.2059" + VALUE "ProductVersion", "4.2.2060" END END BLOCK "VarFileInfo" diff --git a/src/ui.h b/src/ui.h index 0b944bc1..746aefc7 100644 --- a/src/ui.h +++ b/src/ui.h @@ -20,6 +20,7 @@ #include #include #include "resource.h" +#include "localization.h" #pragma once @@ -99,6 +100,7 @@ extern void ToggleAdvancedFormatOptions(BOOL enable); extern void ToggleImageOptions(void); extern void CreateSmallButtons(HWND hDlg); extern void CreateAdditionalControls(HWND hDlg); +extern void EnableControls(BOOL enable, BOOL remove_checkboxes); extern void InitProgress(BOOL bOnlyFormat); extern void ShowLanguageMenu(RECT rcExclude); extern void SetPassesTooltip(void); diff --git a/src/vhd.c b/src/vhd.c index 33a31e19..94c63188 100644 --- a/src/vhd.c +++ b/src/vhd.c @@ -18,13 +18,15 @@ */ #include +#include #include #include #include #include -#include "vhd.h" #include "rufus.h" +#include "ui.h" +#include "vhd.h" #include "missing.h" #include "resource.h" #include "msapi_utf8.h" @@ -45,7 +47,6 @@ PF_TYPE_DECL(WINAPI, BOOL, WIMGetImageInformation, (HANDLE, PVOID, PDWORD)); PF_TYPE_DECL(WINAPI, BOOL, WIMCloseHandle, (HANDLE)); PF_TYPE_DECL(WINAPI, DWORD, WIMRegisterMessageCallback, (HANDLE, FARPROC, PVOID)); PF_TYPE_DECL(WINAPI, DWORD, WIMUnregisterMessageCallback, (HANDLE, FARPROC)); -PF_TYPE_DECL(RPC_ENTRY, RPC_STATUS, UuidCreate, (UUID __RPC_FAR*)); typedef struct { int index; @@ -58,6 +59,8 @@ 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 RUFUS_DRIVE rufus_drive[MAX_DRIVES]; +extern HANDLE format_thread; static uint8_t wim_flags = 0; static uint32_t progress_report_mask; @@ -78,102 +81,6 @@ static BOOL Get7ZipPath(void) return FALSE; } -BOOL AppendVHDFooter(const char* vhd_path) -{ - const char creator_os[4] = VHD_FOOTER_CREATOR_HOST_OS_WINDOWS; - const char creator_app[4] = { 'r', 'u', 'f', 's' }; - BOOL r = FALSE; - DWORD size; - LARGE_INTEGER li; - HANDLE handle = INVALID_HANDLE_VALUE; - vhd_footer* footer = NULL; - uint64_t totalSectors; - uint16_t cylinders = 0; - uint8_t heads, sectorsPerTrack; - uint32_t cylinderTimesHeads; - uint32_t checksum; - size_t i; - - PF_INIT(UuidCreate, Rpcrt4); - handle = CreateFileU(vhd_path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - li.QuadPart = 0; - if ((handle == INVALID_HANDLE_VALUE) || (!SetFilePointerEx(handle, li, &li, FILE_END))) { - uprintf("Could not open image '%s': %s", vhd_path, WindowsErrorString()); - goto out; - } - footer = (vhd_footer*)calloc(1, sizeof(vhd_footer)); - if (footer == NULL) { - uprintf("Could not allocate VHD footer"); - goto out; - } - - memcpy(footer->cookie, conectix_str, sizeof(footer->cookie)); - footer->features = bswap_uint32(VHD_FOOTER_FEATURES_RESERVED); - footer->file_format_version = bswap_uint32(VHD_FOOTER_FILE_FORMAT_V1_0); - footer->data_offset = bswap_uint64(VHD_FOOTER_DATA_OFFSET_FIXED_DISK); - footer->timestamp = bswap_uint32((uint32_t)(_time64(NULL) - SECONDS_SINCE_JAN_1ST_2000)); - memcpy(footer->creator_app, creator_app, sizeof(creator_app)); - footer->creator_version = bswap_uint32((rufus_version[0]<<16)|rufus_version[1]); - memcpy(footer->creator_host_os, creator_os, sizeof(creator_os)); - footer->original_size = bswap_uint64(li.QuadPart); - footer->current_size = footer->original_size; - footer->disk_type = bswap_uint32(VHD_FOOTER_TYPE_FIXED_HARD_DISK); - if ((pfUuidCreate == NULL) || (pfUuidCreate(&footer->unique_id) != RPC_S_OK)) - uprintf("Warning: could not set VHD UUID"); - - // Compute CHS, as per the VHD specs - totalSectors = li.QuadPart / 512; - if (totalSectors > 65535 * 16 * 255) { - totalSectors = 65535 * 16 * 255; - } - - if (totalSectors >= 65535 * 16 * 63) { - sectorsPerTrack = 255; - heads = 16; - cylinderTimesHeads = (uint32_t)(totalSectors / sectorsPerTrack); - } else { - sectorsPerTrack = 17; - cylinderTimesHeads = (uint32_t)(totalSectors / sectorsPerTrack); - - heads = (cylinderTimesHeads + 1023) / 1024; - - if (heads < 4) { - heads = 4; - } - if (cylinderTimesHeads >= ((uint32_t)heads * 1024) || heads > 16) { - sectorsPerTrack = 31; - heads = 16; - cylinderTimesHeads = (uint32_t)(totalSectors / sectorsPerTrack); - } - if (cylinderTimesHeads >= ((uint32_t)heads * 1024)) { - sectorsPerTrack = 63; - heads = 16; - cylinderTimesHeads = (uint32_t)(totalSectors / sectorsPerTrack); - } - } - cylinders = cylinderTimesHeads / heads; - footer->disk_geometry.chs.cylinders = bswap_uint16(cylinders); - footer->disk_geometry.chs.heads = heads; - footer->disk_geometry.chs.sectors = sectorsPerTrack; - - // Compute the VHD footer checksum - for (checksum=0, i=0; ichecksum = bswap_uint32(~checksum); - - if (!WriteFileWithRetry(handle, footer, sizeof(vhd_footer), &size, WRITE_RETRIES)) { - uprintf("Could not write VHD footer: %s", WindowsErrorString()); - goto out; - } - r = TRUE; - -out: - safe_free(footer); - safe_closehandle(handle); - return r; -} - typedef struct { const char* ext; bled_compression_type type; @@ -977,3 +884,141 @@ BOOL WimApplyImage(const char* image, int index, const char* dst) wim_thread = NULL; return dw; } + +// 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 SaveImageThread(void* param) +{ + IMG_SAVE* img_save = (IMG_SAVE*)param; + HANDLE handle = INVALID_HANDLE_VALUE; + WCHAR* wDst = utf8_to_wchar(img_save->ImagePath); + WCHAR* wSrc = utf8_to_wchar(GetPhysicalName(img_save->DeviceNum)); + VIRTUAL_STORAGE_TYPE vtype = { img_save->Type, VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT }; + STOPGAP_CREATE_VIRTUAL_DISK_PARAMETERS vparams = { 0 }; + VIRTUAL_DISK_PROGRESS vprogress = { 0 }; + OVERLAPPED overlapped = { 0 }; + DWORD result, flags; + + assert(img_save->Type == VIRTUAL_STORAGE_TYPE_DEVICE_VHD || + img_save->Type == VIRTUAL_STORAGE_TYPE_DEVICE_VHDX); + + UpdateProgressWithInfoInit(NULL, FALSE); + + vparams.Version = CREATE_VIRTUAL_DISK_VERSION_2; + vparams.Version2.UniqueId = GUID_NULL; + vparams.Version2.BlockSizeInBytes = CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_BLOCK_SIZE; + vparams.Version2.SectorSizeInBytes = CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_SECTOR_SIZE; + vparams.Version2.PhysicalSectorSizeInBytes = SelectedDrive.SectorSize; + vparams.Version2.SourcePath = wSrc; + + // When CREATE_VIRTUAL_DISK_FLAG_CREATE_BACKING_STORAGE is specified with + // a source path, CreateVirtualDisk() automatically clones the source to + // the virtual disk. + flags = CREATE_VIRTUAL_DISK_FLAG_CREATE_BACKING_STORAGE; + // The following ensures that VHD images are stored uncompressed and can + // be used as DD images. + if (img_save->Type != VIRTUAL_STORAGE_TYPE_DEVICE_VHDX) + flags |= CREATE_VIRTUAL_DISK_FLAG_FULL_PHYSICAL_ALLOCATION; + // TODO: Use CREATE_VIRTUAL_DISK_FLAG_PREVENT_WRITES_TO_SOURCE_DISK? + + overlapped.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL); + + // CreateVirtualDisk() does not have an overwrite flag... + DeleteFileW(wDst); + + result = CreateVirtualDisk(&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); + uprintf("Could not create virtual disk: %s", WindowsErrorString()); + goto out; + } + + if (result == ERROR_IO_PENDING) { + while ((result = WaitForSingleObject(overlapped.hEvent, 100)) == WAIT_TIMEOUT) { + if (IS_ERROR(FormatStatus) && (SCODE_CODE(FormatStatus) == ERROR_CANCELLED)) { + CancelIoEx(handle, &overlapped); + goto out; + } + if (GetVirtualDiskOperationProgress(handle, &overlapped, &vprogress) == ERROR_SUCCESS) { + if (vprogress.OperationStatus == ERROR_IO_PENDING) + UpdateProgressWithInfo(OP_FORMAT, MSG_261, vprogress.CurrentValue, vprogress.CompletionValue); + } + } + if (result != WAIT_OBJECT_0) { + uprintf("Could not save virtual disk: %s", WindowsErrorString()); + goto out; + } + } + + UpdateProgressWithInfo(OP_FORMAT, MSG_261, SelectedDrive.DiskSize, SelectedDrive.DiskSize); + uprintf("Operation complete."); + +out: + safe_closehandle(handle); + safe_closehandle(overlapped.hEvent); + safe_free(wDst); + safe_free(wSrc); + safe_free(img_save->ImagePath); + PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)TRUE, 0); + ExitThread(0); +} + +void SaveVHD(void) +{ + static IMG_SAVE img_save = { 0 }; + 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))); + ULARGE_INTEGER free_space; + + if ((DriveIndex < 0) || (format_thread != NULL)) + return; + + static_sprintf(filename, "%s.vhdx", rufus_drive[DriveIndex].label); + img_save.DeviceNum = (DWORD)ComboBox_GetItemData(hDeviceList, DriveIndex); + 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.BufSize = DD_BUFFER_SIZE; + img_save.DeviceSize = SelectedDrive.DiskSize; + if (img_save.ImagePath != NULL) { + // Reset all progress bars + SendMessage(hMainDialog, UM_PROGRESS_INIT, 0, 0); + FormatStatus = 0; + free_space.QuadPart = 0; + if ((GetVolumePathNameA(img_save.ImagePath, path, sizeof(path))) + && (GetDiskFreeSpaceExA(path, &free_space, NULL, NULL)) + && ((LONGLONG)free_space.QuadPart > (SelectedDrive.DiskSize + 512))) { + // Disable all controls except cancel + EnableControls(FALSE, FALSE); + FormatStatus = 0; + InitProgress(TRUE); + format_thread = CreateThread(NULL, 0, SaveImageThread, &img_save, 0, NULL); + if (format_thread != NULL) { + uprintf("\r\nSave to VHD operation started"); + PrintInfo(0, -1); + SendMessage(hMainDialog, UM_TIMER_START, 0, 0); + } else { + uprintf("Unable to start VHD save thread"); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_CANT_START_THREAD); + safe_free(img_save.ImagePath); + PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)FALSE, 0); + } + } else { + if (free_space.QuadPart == 0) { + uprintf("Unable to isolate drive name for VHD save"); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_PATH_NOT_FOUND; + } else { + // TODO: Might be salvageable for compressed VHDX + uprintf("The VHD size is too large for the target drive"); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_FILE_TOO_LARGE; + } + safe_free(img_save.ImagePath); + PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)FALSE, 0); + } + } +} diff --git a/src/vhd.h b/src/vhd.h index 6ca01fc2..4eee18a8 100644 --- a/src/vhd.h +++ b/src/vhd.h @@ -19,6 +19,7 @@ #include #include +#include #pragma once @@ -70,6 +71,49 @@ #define MBR_SIZE 512 // Might need to review this once we see bootable 4k systems +// TODO: Remove this once MinGW has been updated +#define VIRTUAL_STORAGE_TYPE_DEVICE_VHDX 3 +#define CREATE_VIRTUAL_DISK_VERSION_2 2 +#define CREATE_VIRTUAL_DISK_FLAG_CREATE_BACKING_STORAGE 8 + +#ifndef CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_BLOCK_SIZE +#define CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_BLOCK_SIZE 0 +#endif + +#ifndef CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_SECTOR_SIZE +#define CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_SECTOR_SIZE 0 +#endif + +typedef struct +{ + CREATE_VIRTUAL_DISK_VERSION Version; + union + { + struct + { + GUID UniqueId; + ULONGLONG MaximumSize; + ULONG BlockSizeInBytes; + ULONG SectorSizeInBytes; + PCWSTR ParentPath; + PCWSTR SourcePath; + } Version1; + struct + { + GUID UniqueId; + ULONGLONG MaximumSize; + ULONG BlockSizeInBytes; + ULONG SectorSizeInBytes; + ULONG PhysicalSectorSizeInBytes; + PCWSTR ParentPath; + PCWSTR SourcePath; + OPEN_VIRTUAL_DISK_FLAG OpenFlags; + VIRTUAL_STORAGE_TYPE ParentVirtualStorageType; + VIRTUAL_STORAGE_TYPE SourceVirtualStorageType; + GUID ResiliencyGuid; + } Version2; + }; +} STOPGAP_CREATE_VIRTUAL_DISK_PARAMETERS; // From https://docs.microsoft.com/en-us/previous-versions/msdn10/dd834960(v=msdn.10) // as well as https://msfn.org/board/topic/150700-wimgapi-wimmountimage-progressbar/ @@ -139,4 +183,5 @@ extern BOOL WimUnmountImage(const char* image, int index, BOOL commit); extern char* GetExistingMountPoint(const char* image, int index); extern BOOL WimIsValidIndex(const char* image, int index); extern int8_t IsBootableImage(const char* path); -extern BOOL AppendVHDFooter(const char* vhd_path); +extern void SaveVHD(void); +extern void SaveISO(void);