Skip to content

Commit

Permalink
[vhd] enable saving device to compressed VHDX
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
pbatard committed Jul 1, 2023
1 parent 1476e9c commit d4c9f2d
Show file tree
Hide file tree
Showing 12 changed files with 361 additions and 339 deletions.
2 changes: 2 additions & 0 deletions .mingw/virtdisk.def
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ EXPORTS
GetVirtualDiskPhysicalPath@12
DetachVirtualDisk@12
OpenVirtualDisk@24
CreateVirtualDisk@36
GetVirtualDiskOperationProgress@12
2 changes: 2 additions & 0 deletions res/loc/rufus.loc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
123 changes: 0 additions & 123 deletions src/format.c
Original file line number Diff line number Diff line change
Expand Up @@ -1977,126 +1977,3 @@ DWORD WINAPI FormatThread(void* param)
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);
}
156 changes: 155 additions & 1 deletion src/iso.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
#include <cdio/udf.h>

#include "rufus.h"
#include "ui.h"
#include "drive.h"
#include "libfat.h"
#include "missing.h"
#include "resource.h"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}
4 changes: 0 additions & 4 deletions src/missing.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <virtdisk.h>

#include "rufus.h"
#include "missing.h"
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit d4c9f2d

Please sign in to comment.