[vhd] fix truncated write operation when using a VHDX image as source

* The legacy code we used for writing disk images used the size of the source image as
  the maximum number of bytes we should copy, which is fine for uncompressed DD or VHD
  images, but not so much for compressed VHDX ones. So we now make sure to use the
  actual size of the virtual disk, which we obtain when mounting the VHD/VHDX.
* Also fix log progress update as well as a MinGW warning.
This commit is contained in:
Pete Batard 2024-03-04 00:41:40 +00:00
parent 026afa7e3d
commit 8738e7a7de
No known key found for this signature in database
GPG Key ID: 38E0CF5E69EDD671
4 changed files with 42 additions and 23 deletions

View File

@ -1151,7 +1151,7 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, BOOL bZeroDrive)
HANDLE hSourceImage = INVALID_HANDLE_VALUE; HANDLE hSourceImage = INVALID_HANDLE_VALUE;
DWORD i, read_size[NUM_BUFFERS], write_size, comp_size, buf_size; DWORD i, read_size[NUM_BUFFERS], write_size, comp_size, buf_size;
uint64_t wb, target_size = bZeroDrive ? SelectedDrive.DiskSize : img_report.image_size; uint64_t wb, target_size = bZeroDrive ? SelectedDrive.DiskSize : img_report.image_size;
uint64_t cur_value, last_value = UINT64_MAX; uint64_t cur_value, last_value = 0;
int64_t bled_ret; int64_t bled_ret;
uint8_t* buffer = NULL; uint8_t* buffer = NULL;
uint32_t zero_data, *cmp_buffer = NULL; uint32_t zero_data, *cmp_buffer = NULL;
@ -1197,11 +1197,9 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, BOOL bZeroDrive)
read_size[0] = buf_size; read_size[0] = buf_size;
for (wb = 0, write_size = 0; wb < target_size; wb += write_size) { for (wb = 0, write_size = 0; wb < target_size; wb += write_size) {
UpdateProgressWithInfo(OP_FORMAT, fast_zeroing ? MSG_306 : MSG_286, wb, target_size); UpdateProgressWithInfo(OP_FORMAT, fast_zeroing ? MSG_306 : MSG_286, wb, target_size);
cur_value = (wb * min(80, target_size)) / target_size; cur_value = (wb * 80) / target_size;
if (cur_value != last_value) { for (; cur_value > last_value && last_value < 80; last_value++)
last_value = cur_value;
uprintfs("+"); uprintfs("+");
}
// Don't overflow our projected size (mostly for VHDs) // Don't overflow our projected size (mostly for VHDs)
if (wb + read_size[0] > target_size) if (wb + read_size[0] > target_size)
read_size[0] = (DWORD)(target_size - wb); read_size[0] = (DWORD)(target_size - wb);
@ -1275,6 +1273,7 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, BOOL bZeroDrive)
if (i > WRITE_RETRIES) if (i > WRITE_RETRIES)
goto out; goto out;
} }
uprintfs("\r\n");
} else if (img_report.compression_type != BLED_COMPRESSION_NONE && img_report.compression_type < BLED_COMPRESSION_MAX) { } else if (img_report.compression_type != BLED_COMPRESSION_NONE && img_report.compression_type < BLED_COMPRESSION_MAX) {
uprintf("Writing compressed image:"); uprintf("Writing compressed image:");
hSourceImage = CreateFileU(image_path, GENERIC_READ, FILE_SHARE_READ, NULL, hSourceImage = CreateFileU(image_path, GENERIC_READ, FILE_SHARE_READ, NULL,
@ -1316,8 +1315,9 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, BOOL bZeroDrive)
// VHD/VHDX require mounting the image first // VHD/VHDX require mounting the image first
if (img_report.compression_type == IMG_COMPRESSION_VHD || if (img_report.compression_type == IMG_COMPRESSION_VHD ||
img_report.compression_type == IMG_COMPRESSION_VHDX) { img_report.compression_type == IMG_COMPRESSION_VHDX) {
vhd_path = VhdMountImage(image_path); // Since VHDX images are compressed, we need to obtain the actual size
if (vhd_path == NULL) vhd_path = VhdMountImageAndGetSize(image_path, &target_size);
if (vhd_path == NULL || target_size == 0)
goto out; goto out;
} }
@ -1346,11 +1346,12 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, BOOL bZeroDrive)
for (wb = 0; read_size[proc_bufnum] != 0; wb += read_size[proc_bufnum]) { for (wb = 0; read_size[proc_bufnum] != 0; wb += read_size[proc_bufnum]) {
// 0. Update the progress // 0. Update the progress
UpdateProgressWithInfo(OP_FORMAT, MSG_261, wb, target_size); UpdateProgressWithInfo(OP_FORMAT, MSG_261, wb, target_size);
cur_value = (wb * min(80, target_size)) / target_size; cur_value = (wb * 80) / target_size;
if (cur_value != last_value) { for ( ; cur_value > last_value && last_value < 80; last_value++)
last_value = cur_value;
uprintfs("+"); uprintfs("+");
}
if (wb >= target_size)
break;
// 1. Wait for the current read operation to complete (and update the read size) // 1. Wait for the current read operation to complete (and update the read size)
if ((!WaitFileAsync(hSourceImage, DRIVE_ACCESS_TIMEOUT)) || if ((!WaitFileAsync(hSourceImage, DRIVE_ACCESS_TIMEOUT)) ||
@ -1467,7 +1468,7 @@ DWORD WINAPI FormatThread(void* param)
extra_partitions |= XP_ESP | XP_MSR; extra_partitions |= XP_ESP | XP_MSR;
// If we have a bootable image with UEFI bootloaders and the target file system is NTFS or exFAT // If we have a bootable image with UEFI bootloaders and the target file system is NTFS or exFAT
// or the UEFI:NTFS option is selected, we add the UEFI:NTFS partition... // or the UEFI:NTFS option is selected, we add the UEFI:NTFS partition...
else if (((boot_type == BT_IMAGE) && IS_EFI_BOOTABLE(img_report)) && ((fs_type == FS_NTFS) || (fs_type == FS_EXFAT)) || else if ((((boot_type == BT_IMAGE) && IS_EFI_BOOTABLE(img_report)) && ((fs_type == FS_NTFS) || (fs_type == FS_EXFAT))) ||
(boot_type == BT_UEFI_NTFS)) { (boot_type == BT_UEFI_NTFS)) {
extra_partitions |= XP_UEFI_NTFS; extra_partitions |= XP_UEFI_NTFS;
// ...but only if we're not dealing with a Windows image in installer mode with target // ...but only if we're not dealing with a Windows image in installer mode with target

View File

@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG DIALOGEX 12, 12, 232, 326 IDD_DIALOG DIALOGEX 12, 12, 232, 326
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_ACCEPTFILES EXSTYLE WS_EX_ACCEPTFILES
CAPTION "Rufus 4.5.2113" CAPTION "Rufus 4.5.2114"
FONT 9, "Segoe UI Symbol", 400, 0, 0x0 FONT 9, "Segoe UI Symbol", 400, 0, 0x0
BEGIN BEGIN
LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP
@ -392,8 +392,8 @@ END
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,5,2113,0 FILEVERSION 4,5,2114,0
PRODUCTVERSION 4,5,2113,0 PRODUCTVERSION 4,5,2114,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -411,13 +411,13 @@ BEGIN
VALUE "Comments", "https://rufus.ie" VALUE "Comments", "https://rufus.ie"
VALUE "CompanyName", "Akeo Consulting" VALUE "CompanyName", "Akeo Consulting"
VALUE "FileDescription", "Rufus" VALUE "FileDescription", "Rufus"
VALUE "FileVersion", "4.5.2113" VALUE "FileVersion", "4.5.2114"
VALUE "InternalName", "Rufus" VALUE "InternalName", "Rufus"
VALUE "LegalCopyright", "© 2011-2024 Pete Batard (GPL v3)" VALUE "LegalCopyright", "© 2011-2024 Pete Batard (GPL v3)"
VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html"
VALUE "OriginalFilename", "rufus-4.5.exe" VALUE "OriginalFilename", "rufus-4.5.exe"
VALUE "ProductName", "Rufus" VALUE "ProductName", "Rufus"
VALUE "ProductVersion", "4.5.2113" VALUE "ProductVersion", "4.5.2114"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View File

@ -908,18 +908,19 @@ PF_TYPE_DECL(WINAPI, DWORD, AttachVirtualDisk, (HANDLE, PSECURITY_DESCRIPTOR,
ATTACH_VIRTUAL_DISK_FLAG, ULONG, PATTACH_VIRTUAL_DISK_PARAMETERS, LPOVERLAPPED)); 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, DetachVirtualDisk, (HANDLE, DETACH_VIRTUAL_DISK_FLAG, ULONG));
PF_TYPE_DECL(WINAPI, DWORD, GetVirtualDiskPhysicalPath, (HANDLE, PULONG, PWSTR)); PF_TYPE_DECL(WINAPI, DWORD, GetVirtualDiskPhysicalPath, (HANDLE, PULONG, PWSTR));
PF_TYPE_DECL(WINAPI, DWORD, GetVirtualDiskOperationProgress, (HANDLE, LPOVERLAPPED, PF_TYPE_DECL(WINAPI, DWORD, GetVirtualDiskOperationProgress, (HANDLE, LPOVERLAPPED, PVIRTUAL_DISK_PROGRESS));
PVIRTUAL_DISK_PROGRESS)); PF_TYPE_DECL(WINAPI, DWORD, GetVirtualDiskInformation, (HANDLE, PULONG, PGET_VIRTUAL_DISK_INFO, PULONG));
static char physical_path[128] = ""; static char physical_path[128] = "";
static HANDLE mounted_handle = INVALID_HANDLE_VALUE; static HANDLE mounted_handle = INVALID_HANDLE_VALUE;
// Mount an ISO or a VHD/VHDX image // Mount an ISO or a VHD/VHDX image and provide its size
// Returns the physical path of the mounted image or NULL on error. // Returns the physical path of the mounted image or NULL on error.
char* VhdMountImage(const char* path) char* VhdMountImageAndGetSize(const char* path, uint64_t* disk_size)
{ {
VIRTUAL_STORAGE_TYPE vtype = { VIRTUAL_STORAGE_TYPE_DEVICE_ISO, VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT }; VIRTUAL_STORAGE_TYPE vtype = { VIRTUAL_STORAGE_TYPE_DEVICE_ISO, VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT };
ATTACH_VIRTUAL_DISK_PARAMETERS vparams = { 0 }; ATTACH_VIRTUAL_DISK_PARAMETERS vparams = { 0 };
GET_VIRTUAL_DISK_INFO disk_info = { 0 };
DWORD r; DWORD r;
wchar_t wtmp[128]; wchar_t wtmp[128];
ULONG size = ARRAYSIZE(wtmp); ULONG size = ARRAYSIZE(wtmp);
@ -929,6 +930,8 @@ char* VhdMountImage(const char* path)
PF_INIT_OR_OUT(OpenVirtualDisk, VirtDisk); PF_INIT_OR_OUT(OpenVirtualDisk, VirtDisk);
PF_INIT_OR_OUT(AttachVirtualDisk, VirtDisk); PF_INIT_OR_OUT(AttachVirtualDisk, VirtDisk);
PF_INIT_OR_OUT(GetVirtualDiskPhysicalPath, VirtDisk); PF_INIT_OR_OUT(GetVirtualDiskPhysicalPath, VirtDisk);
if (disk_size != NULL)
PF_INIT_OR_OUT(GetVirtualDiskInformation, VirtDisk);
if (wpath == NULL) if (wpath == NULL)
return NULL; return NULL;
@ -967,6 +970,20 @@ char* VhdMountImage(const char* path)
goto out; goto out;
} }
wchar_to_utf8_no_alloc(wtmp, physical_path, sizeof(physical_path)); wchar_to_utf8_no_alloc(wtmp, physical_path, sizeof(physical_path));
if (disk_size != NULL) {
*disk_size = 0;
disk_info.Version = GET_VIRTUAL_DISK_INFO_SIZE;
size = sizeof(disk_info);
r = pfGetVirtualDiskInformation(mounted_handle, &size, &disk_info, NULL);
if (r != ERROR_SUCCESS) {
SetLastError(r);
uprintf("Could not obtain virtual size of mounted image '%s': %s", path, WindowsErrorString());
goto out;
}
*disk_size = disk_info.Size.VirtualSize;
}
ret = physical_path; ret = physical_path;
out: out:

View File

@ -1,7 +1,7 @@
/* /*
* Rufus: The Reliable USB Formatting Utility * Rufus: The Reliable USB Formatting Utility
* Virtual Disk Handling definitions and prototypes * Virtual Disk Handling definitions and prototypes
* Copyright © 2022 Pete Batard <pete@akeo.ie> * Copyright © 2022-2024 Pete Batard <pete@akeo.ie>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -135,7 +135,8 @@ extern BOOL WimUnmountImage(const char* image, int index, BOOL commit);
extern char* WimGetExistingMountPoint(const char* image, int index); extern char* WimGetExistingMountPoint(const char* image, int index);
extern BOOL WimIsValidIndex(const char* image, int index); extern BOOL WimIsValidIndex(const char* image, int index);
extern int8_t IsBootableImage(const char* path); extern int8_t IsBootableImage(const char* path);
extern char* VhdMountImage(const char* path); extern char* VhdMountImageAndGetSize(const char* path, uint64_t* disksize);
#define VhdMountImage(path) VhdMountImageAndGetSize(path, NULL)
extern void VhdUnmountImage(void); extern void VhdUnmountImage(void);
extern void VhdSaveImage(void); extern void VhdSaveImage(void);
extern void IsoSaveImage(void); extern void IsoSaveImage(void);