From c47ee4c435cf755b2f78606a8f0dbb787ac4f3de Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Fri, 30 Jun 2023 13:05:42 +0100 Subject: [PATCH 1/3] [cmp] update Bled to latest * Adds the provision of default buffer size in bled_init() --- src/bled/bled.c | 15 ++++++++++++--- src/bled/bled.h | 5 +++-- src/bled/decompress_gunzip.c | 7 ++++--- src/bled/decompress_uncompress.c | 4 ++-- src/bled/libbb.h | 2 +- src/format.c | 2 +- src/iso.c | 2 +- src/net.c | 2 +- src/rufus.rc | 10 +++++----- src/vhd.c | 2 +- 10 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/bled/bled.c b/src/bled/bled.c index f0bf30e6..7d1c813c 100644 --- a/src/bled/bled.c +++ b/src/bled/bled.c @@ -31,6 +31,7 @@ jmp_buf bb_error_jmp; char* bb_virtual_buf = NULL; size_t bb_virtual_len = 0, bb_virtual_pos = 0; int bb_virtual_fd = -1; +uint32_t BB_BUFSIZE = 0x10000; static long long int unpack_none(transformer_state_t *xstate) { @@ -265,7 +266,8 @@ int64_t bled_uncompress_from_buffer_to_buffer(const char* src, const size_t src_ } /* Initialize the library. - * When the parameters are not NULL you can: + * When the parameters are not NULL or zero you can: + * - specify the buffer size to use (must be larger than 64KB and a power of two) * - specify the printf-like function you want to use to output message * void print_function(const char* format, ...); * - specify the read/write functions you want to use; @@ -275,18 +277,25 @@ int64_t bled_uncompress_from_buffer_to_buffer(const char* src, const size_t src_ * void switch_function(const char* filename, const uint64_t filesize); * - point to an unsigned long variable, to be used to cancel operations when set to non zero */ -int bled_init(printf_t print_function, read_t read_function, write_t write_function, +int bled_init(uint32_t buffer_size, printf_t print_function, read_t read_function, write_t write_function, progress_t progress_function, switch_t switch_function, unsigned long* cancel_request) { if (bled_initialized) return -1; - bled_initialized = true; + BB_BUFSIZE = buffer_size; + /* buffer_size must be larger than 64 KB and a power of two */ + if (buffer_size < 0x10000 || (buffer_size & (buffer_size - 1)) != 0) { + if (buffer_size != 0 && print_function != NULL) + print_function("bled_init: invalid buffer_size, defaulting to 64 KB"); + BB_BUFSIZE = 0x10000; + } bled_printf = print_function; bled_read = read_function; bled_write = write_function; bled_progress = progress_function; bled_switch = switch_function; bled_cancel_request = cancel_request; + bled_initialized = true; return 0; } diff --git a/src/bled/bled.h b/src/bled/bled.h index 415ca927..480498cf 100644 --- a/src/bled/bled.h +++ b/src/bled/bled.h @@ -50,7 +50,8 @@ int64_t bled_uncompress_to_dir(const char* src, const char* dir, int type); int64_t bled_uncompress_from_buffer_to_buffer(const char* src, const size_t src_len, char* dst, size_t dst_len, int type); /* Initialize the library. - * When the parameters are not NULL you can: + * When the parameters are not NULL or zero you can: + * - specify the buffer size to use (must be larger than 64KB and a power of two) * - specify the printf-like function you want to use to output message * void print_function(const char* format, ...); * - specify the read/write functions you want to use; @@ -60,7 +61,7 @@ int64_t bled_uncompress_from_buffer_to_buffer(const char* src, const size_t src_ * void switch_function(const char* filename, const uint64_t filesize); * - point to an unsigned long variable, to be used to cancel operations when set to non zero */ -int bled_init(printf_t print_function, read_t read_function, write_t write_function, +int bled_init(uint32_t buffer_size, printf_t print_function, read_t read_function, write_t write_function, progress_t progress_function, switch_t switch_function, unsigned long* cancel_request); /* This call frees any resource used by the library */ diff --git a/src/bled/decompress_gunzip.c b/src/bled/decompress_gunzip.c index 94b4b374..1d3d832e 100644 --- a/src/bled/decompress_gunzip.c +++ b/src/bled/decompress_gunzip.c @@ -45,10 +45,11 @@ typedef struct huft_t { } v; } huft_t; +/* gunzip_window size--must be a power of two, and + * at least 32K for zip's deflate method */ +#define GUNZIP_WSIZE BB_BUFSIZE + enum { - /* gunzip_window size--must be a power of two, and - * at least 32K for zip's deflate method */ - GUNZIP_WSIZE = BB_BUFSIZE, /* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ BMAX = 16, /* maximum bit length of any code (16 for explode) */ N_MAX = 288, /* maximum number of codes in any set */ diff --git a/src/bled/decompress_uncompress.c b/src/bled/decompress_uncompress.c index df6cf263..a995a0db 100644 --- a/src/bled/decompress_uncompress.c +++ b/src/bled/decompress_uncompress.c @@ -27,10 +27,10 @@ /* Default input buffer size */ -#define IBUFSIZ BB_BUFSIZE +#define IBUFSIZ ((int)BB_BUFSIZE) /* Default output buffer size */ -#define OBUFSIZ BB_BUFSIZE +#define OBUFSIZ ((int)BB_BUFSIZE) /* Defines for third byte of header */ #define BIT_MASK 0x1f /* Mask for 'number of compresssion bits' */ diff --git a/src/bled/libbb.h b/src/bled/libbb.h index 836e63dc..7d178236 100644 --- a/src/bled/libbb.h +++ b/src/bled/libbb.h @@ -39,7 +39,6 @@ #include #include -#define BB_BUFSIZE 0x40000 #define ONE_TB 1099511627776ULL #define ENABLE_DESKTOP 1 @@ -105,6 +104,7 @@ typedef unsigned int uid_t; #define get_le16(ptr) (*(const uint16_t *)(ptr)) #endif +extern uint32_t BB_BUFSIZE; extern smallint bb_got_signal; extern uint32_t *global_crc32_table; extern jmp_buf bb_error_jmp; diff --git a/src/format.c b/src/format.c index fb571902..36eb270c 100644 --- a/src/format.c +++ b/src/format.c @@ -1290,7 +1290,7 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, BOOL bZeroDrive) } assert((uintptr_t)sec_buf % SelectedDrive.SectorSize == 0); sec_buf_pos = 0; - bled_init(uprintf, NULL, sector_write, update_progress, NULL, &FormatStatus); + bled_init(256 * KB, uprintf, NULL, sector_write, update_progress, NULL, &FormatStatus); bled_ret = bled_uncompress_with_handles(hSourceImage, hPhysicalDrive, img_report.compression_type); bled_exit(); uprintfs("\r\n"); diff --git a/src/iso.c b/src/iso.c index b74dad4d..19c85ceb 100644 --- a/src/iso.c +++ b/src/iso.c @@ -1283,7 +1283,7 @@ out: update_md5sum(); if (archive_path != NULL) { uprintf("● Adding files from %s", archive_path); - bled_init(NULL, NULL, NULL, NULL, alt_print_extracted_file, NULL); + bled_init(256 * KB, NULL, NULL, NULL, NULL, alt_print_extracted_file, NULL); bled_uncompress_to_dir(archive_path, dest_dir, BLED_COMPRESSION_ZIP); bled_exit(); } diff --git a/src/net.c b/src/net.c index e4c64bc7..f0ce93e9 100644 --- a/src/net.c +++ b/src/net.c @@ -948,7 +948,7 @@ static DWORD WINAPI DownloadISOThread(LPVOID param) free(sig); uprintf("Download signature is valid ✓"); uncompressed_size = *((uint64_t*)&compressed[5]); - if ((uncompressed_size < 1 * MB) && (bled_init(uprintf, NULL, NULL, NULL, NULL, &FormatStatus) >= 0)) { + if ((uncompressed_size < 1 * MB) && (bled_init(0, uprintf, NULL, NULL, NULL, NULL, &FormatStatus) >= 0)) { fido_script = malloc((size_t)uncompressed_size); size = bled_uncompress_from_buffer_to_buffer(compressed, dwCompressedSize, fido_script, (size_t)uncompressed_size, BLED_COMPRESSION_LZMA); bled_exit(); diff --git a/src/rufus.rc b/src/rufus.rc index c23564ad..05f7d7fd 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.2057" +CAPTION "Rufus 4.2.2058" 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,2057,0 - PRODUCTVERSION 4,2,2057,0 + FILEVERSION 4,2,2058,0 + PRODUCTVERSION 4,2,2058,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.2057" + VALUE "FileVersion", "4.2.2058" 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.2057" + VALUE "ProductVersion", "4.2.2058" END END BLOCK "VarFileInfo" diff --git a/src/vhd.c b/src/vhd.c index eb3d481d..33a31e19 100644 --- a/src/vhd.c +++ b/src/vhd.c @@ -211,7 +211,7 @@ static BOOL IsCompressedBootableImage(const char* path) if (buf == NULL) return FALSE; FormatStatus = 0; - bled_init(uprintf, NULL, NULL, NULL, NULL, &FormatStatus); + 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 (dc != MBR_SIZE) { From 1476e9cd8b48413928e29b2173647663879a4bc2 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Sat, 1 Jul 2023 20:22:10 +0100 Subject: [PATCH 2/3] [uefi] fix path of SKUSiPolicy.p7b for WOW * 32-bit x86 running on 64-bit x86 Windows needs to get SKUSiPolicy.p7b from sysnative. * Also fix automatic extension switching in file dialog and a small MinGW warning in Bled. --- src/bled/decompress_unzip.c | 2 +- src/pki.c | 3 ++- src/rufus.rc | 10 +++++----- src/stdlg.c | 11 ++++++++++- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/bled/decompress_unzip.c b/src/bled/decompress_unzip.c index fffdf3d2..ad698352 100644 --- a/src/bled/decompress_unzip.c +++ b/src/bled/decompress_unzip.c @@ -209,7 +209,7 @@ do { if (BB_BIG_ENDIAN) { \ (c64).fmt.cdf_offset = SWAP_LE64((c64).fmt.cdf_offset); \ }} while (0) -inline void BUG() { +inline void BUG(void) { /* Check the offset of the last element, not the length. This leniency * allows for poor packing, whereby the overall struct may be too long, * even though the elements are all in the right place. diff --git a/src/pki.c b/src/pki.c index 8f327325..2b4cc2da 100644 --- a/src/pki.c +++ b/src/pki.c @@ -812,7 +812,8 @@ BOOL ParseSKUSiPolicy(void) pe256ssp_size = 0; safe_free(pe256ssp); - static_sprintf(path, "%s\\SecureBootUpdates\\SKUSiPolicy.p7b", system_dir); + // Must use sysnative for WOW + static_sprintf(path, "%s\\SecureBootUpdates\\SKUSiPolicy.p7b", sysnative_dir); wpath = utf8_to_wchar(path); if (wpath == NULL) goto out; diff --git a/src/rufus.rc b/src/rufus.rc index 05f7d7fd..3140cb7a 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.2058" +CAPTION "Rufus 4.2.2059" 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,2058,0 - PRODUCTVERSION 4,2,2058,0 + FILEVERSION 4,2,2059,0 + PRODUCTVERSION 4,2,2059,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.2058" + VALUE "FileVersion", "4.2.2059" 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.2058" + VALUE "ProductVersion", "4.2.2059" END END BLOCK "VarFileInfo" diff --git a/src/stdlg.c b/src/stdlg.c index 1bb4e090..5316fb40 100644 --- a/src/stdlg.c +++ b/src/stdlg.c @@ -103,7 +103,7 @@ char* FileDialog(BOOL save, char* path, const ext_t* ext, DWORD options) IFileDialog *pfd = NULL; IShellItem *psiResult; COMDLG_FILTERSPEC* filter_spec = NULL; - wchar_t *wpath = NULL, *wfilename = NULL; + wchar_t *wpath = NULL, *wfilename = NULL, *wext = NULL; IShellItem *si_path = NULL; // Automatically freed if ((ext == NULL) || (ext->count == 0) || (ext->extension == NULL) || (ext->description == NULL)) @@ -162,11 +162,20 @@ char* FileDialog(BOOL save, char* path, const ext_t* ext, DWORD options) if (wfilename != NULL) { IFileDialog_SetFileName(pfd, wfilename); } + // Set a default extension so that when the user switches filters it gets + // automatically updated. Note that the IFileDialog::SetDefaultExtension() + // doc says the extension shouldn't be prefixed with unwanted characters + // but it appears to work regardless so we don't bother cleaning it. + wext = utf8_to_wchar((ext->extension == NULL) ? "" : ext->extension[0]); + if (wext != NULL) { + IFileDialog_SetDefaultExtension(pfd, wext); + } // Display the dialog hr = IFileDialog_Show(pfd, hMainDialog); // Cleanup + safe_free(wext); safe_free(wfilename); for (i = 0; i < ext->count; i++) { safe_free(filter_spec[i].pszSpec); From d4c9f2dfa15e0abd87c57dbe0740b985a00ba3a7 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Sat, 1 Jul 2023 20:39:28 +0100 Subject: [PATCH 3/3] [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);