diff --git a/res/loc/rufus.loc b/res/loc/rufus.loc index 50295d9e..c24d45c0 100644 --- a/res/loc/rufus.loc +++ b/res/loc/rufus.loc @@ -605,6 +605,7 @@ 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" +t MSG_344 "Full Flash Update 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/dev.c b/src/dev.c index 4a5d4e2c..856883f5 100644 --- a/src/dev.c +++ b/src/dev.c @@ -897,7 +897,7 @@ BOOL GetDevices(DWORD devnum) break; } - if (GetDriveLabel(drive_index, drive_letters, &label)) { + if (GetDriveLabel(drive_index, drive_letters, &label, FALSE)) { if ((props.is_SCSI) && (!props.is_UASP) && (!props.is_VHD)) { if (!props.is_Removable) { // Non removables should have been eliminated above, but since we diff --git a/src/drive.c b/src/drive.c index 13d21d96..f9f20bb0 100644 --- a/src/drive.c +++ b/src/drive.c @@ -1314,7 +1314,7 @@ BOOL IsDriveLetterInUse(const char drive_letter) * Return the drive letter and volume label * If the drive doesn't have a volume assigned, space is returned for the letter */ -BOOL GetDriveLabel(DWORD DriveIndex, char* letters, char** label) +BOOL GetDriveLabel(DWORD DriveIndex, char* letters, char** label, BOOL bSilent) { HANDLE hPhysical; DWORD size, error; @@ -1350,10 +1350,10 @@ BOOL GetDriveLabel(DWORD DriveIndex, char* letters, char** label) if (DeviceIoControl(hPhysical, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, NULL, 0, &size, NULL)) AutorunLabel = get_token_data_file("label", AutorunPath); else if (GetLastError() == ERROR_NOT_READY) - uprintf("Ignoring 'autorun.inf' label for drive %c: No media", toupper(letters[0])); + suprintf("Ignoring 'autorun.inf' label for drive %c: No media", toupper(letters[0])); safe_closehandle(hPhysical); if (AutorunLabel != NULL) { - uprintf("Using 'autorun.inf' label for drive %c: '%s'", toupper(letters[0]), AutorunLabel); + suprintf("Using 'autorun.inf' label for drive %c: '%s'", toupper(letters[0]), AutorunLabel); static_strcpy(VolumeLabel, AutorunLabel); safe_free(AutorunLabel); *label = VolumeLabel; diff --git a/src/drive.h b/src/drive.h index 16caa367..170408d5 100644 --- a/src/drive.h +++ b/src/drive.h @@ -396,7 +396,7 @@ UINT GetDriveTypeFromIndex(DWORD DriveIndex); char GetUnusedDriveLetter(void); BOOL IsDriveLetterInUse(const char drive_letter); char RemoveDriveLetters(DWORD DriveIndex, BOOL bUseLast, BOOL bSilent); -BOOL GetDriveLabel(DWORD DriveIndex, char* letter, char** label); +BOOL GetDriveLabel(DWORD DriveIndex, char* letters, char** label, BOOL bSilent); uint64_t GetDriveSize(DWORD DriveIndex); BOOL IsMediaPresent(DWORD DriveIndex); BOOL AnalyzeMBR(HANDLE hPhysicalDrive, const char* TargetName, BOOL bSilent); diff --git a/src/iso.c b/src/iso.c index a8db0e68..185af3f6 100644 --- a/src/iso.c +++ b/src/iso.c @@ -1670,7 +1670,7 @@ BOOL DumpFatDir(const char* path, int32_t cluster) buf = libfat_get_sector(lf_fs, s); if (buf == NULL) FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_SECTOR_NOT_FOUND; - if (FormatStatus) + if (IS_ERROR(FormatStatus)) goto out; size = MIN(LIBFAT_SECTOR_SIZE, diritem.size - written); if (!WriteFileWithRetry(handle, buf, size, &size, WRITE_RETRIES) || @@ -1724,7 +1724,7 @@ static HANDLE mounted_handle = INVALID_HANDLE_VALUE; char* MountISO(const char* path) { - VIRTUAL_STORAGE_TYPE vtype = { 1, 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 }; DWORD r; wchar_t wtmp[128]; diff --git a/src/rufus.h b/src/rufus.h index b5c24ca2..7bee5c06 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -450,7 +450,7 @@ typedef struct { * to define an 'ext_t my_extensions' variable initialized with the relevant attributes. */ typedef struct ext_t { - const size_t count; + size_t count; const char* filename; const char** extension; const char** description; @@ -652,7 +652,8 @@ extern char* FileDialog(BOOL save, char* path, const ext_t* ext, DWORD options); extern BOOL FileIO(enum file_io_type io_type, char* path, char** buffer, DWORD* size); extern unsigned char* GetResource(HMODULE module, char* name, char* type, const char* desc, DWORD* len, BOOL duplicate); extern DWORD GetResourceSize(HMODULE module, char* name, char* type, const char* desc); -extern DWORD RunCommand(const char* cmdline, const char* dir, BOOL log); +extern DWORD RunCommandWithProgress(const char* cmdline, const char* dir, BOOL log, int msg); +#define RunCommand(cmd, dir, log) RunCommandWithProgress(cmd, dir, log, 0) extern BOOL CompareGUID(const GUID *guid1, const GUID *guid2); extern BOOL MountRegistryHive(const HKEY key, const char* pszHiveName, const char* pszHivePath); extern BOOL UnmountRegistryHive(const HKEY key, const char* pszHiveName); diff --git a/src/rufus.rc b/src/rufus.rc index 1c81b27e..4cb53e3c 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.2061" +CAPTION "Rufus 4.2.2062" 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,2061,0 - PRODUCTVERSION 4,2,2061,0 + FILEVERSION 4,2,2062,0 + PRODUCTVERSION 4,2,2062,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.2061" + VALUE "FileVersion", "4.2.2062" 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.2061" + VALUE "ProductVersion", "4.2.2062" END END BLOCK "VarFileInfo" diff --git a/src/stdfn.c b/src/stdfn.c index 04727917..20fd172f 100644 --- a/src/stdfn.c +++ b/src/stdfn.c @@ -26,6 +26,7 @@ #include #include +#include "re.h" #include "rufus.h" #include "missing.h" #include "resource.h" @@ -706,14 +707,19 @@ DWORD GetResourceSize(HMODULE module, char* name, char* type, const char* desc) } // Run a console command, with optional redirection of stdout and stderr to our log -DWORD RunCommand(const char* cmd, const char* dir, BOOL log) +// as well as optional progress reporting if msg is not 0. +DWORD RunCommandWithProgress(const char* cmd, const char* dir, BOOL log, int msg) { - DWORD ret, dwRead, dwAvail, dwPipeSize = 4096; - STARTUPINFOA si = {0}; - PROCESS_INFORMATION pi = {0}; + DWORD i, ret, dwRead, dwAvail, dwPipeSize = 4096; + STARTUPINFOA si = { 0 }; + PROCESS_INFORMATION pi = { 0 }; SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; HANDLE hOutputRead = INVALID_HANDLE_VALUE, hOutputWrite = INVALID_HANDLE_VALUE; + int match_length; static char* output; + // For detecting typical dism.exe commandline progress report of type: + // "\r[==== 8.0% ]\r\n" + re_t pattern = re_compile("\\s*\\[[= ]+[\\d\\.]+%[= ]+\\]\\s*"); si.cb = sizeof(si); if (log) { @@ -737,16 +743,56 @@ DWORD RunCommand(const char* cmd, const char* dir, BOOL log) goto out; } - if (log) { + if (log || msg != 0) { + if (msg != 0) + UpdateProgressWithInfoInit(NULL, FALSE); while (1) { + // Check for user cancel + if (IS_ERROR(FormatStatus) && (SCODE_CODE(FormatStatus) == ERROR_CANCELLED)) { + if (!TerminateProcess(pi.hProcess, ERROR_CANCELLED)) { + uprintf("Could not terminate command: %s", WindowsErrorString()); + } else switch (WaitForSingleObject(pi.hProcess, 5000)) { + case WAIT_TIMEOUT: + uprintf("Command did not terminate within timeout duration"); + break; + case WAIT_OBJECT_0: + uprintf("Command was terminated by user"); + break; + default: + uprintf("Error while waiting for command to be terminated: %s", WindowsErrorString()); + break; + } + ret = ERROR_CANCELLED; + goto out; + } // coverity[string_null] if (PeekNamedPipe(hOutputRead, NULL, dwPipeSize, NULL, &dwAvail, NULL)) { if (dwAvail != 0) { output = malloc(dwAvail + 1); if ((output != NULL) && (ReadFile(hOutputRead, output, dwAvail, &dwRead, NULL)) && (dwRead != 0)) { output[dwAvail] = 0; - // output may contain a '%' so don't feed it as a naked format string - uprintf("%s", output); + // Process a commandline progress bar into a percentage + if ((msg != 0) && (re_matchp(pattern, output, &match_length) != -1)) { + float f = 0.0f; + i = 0; +next_progress_line: + for (; (i < dwAvail) && (output[i] < '0' || output[i] > '9'); i++); + IGNORE_RETVAL(sscanf(&output[i], "%f*", &f)); + UpdateProgressWithInfo(OP_FORMAT, msg, (uint64_t)(f * 100.0f), 100 * 100ULL); + // Go to next line + while ((++i < dwAvail) && (output[i] != '\n') && (output[i] != '\r')); + while ((++i < dwAvail) && ((output[i] == '\n') || (output[i] == '\r'))); + // Print additional lines, if any + if (i < dwAvail) { + // Might have two consecutive progress lines in our buffer + if (re_matchp(pattern, &output[i], &match_length) != -1) + goto next_progress_line; + uprintf("%s", &output[i]); + } + } else if (log) { + // output may contain a '%' so don't feed it as a naked format string + uprintf("%s", output); + } } free(output); } @@ -756,6 +802,7 @@ DWORD RunCommand(const char* cmd, const char* dir, BOOL log) Sleep(100); }; } else { + // TODO: Detect user cancellation here? WaitForSingleObject(pi.hProcess, INFINITE); } diff --git a/src/vhd.c b/src/vhd.c index 9ef94647..844f8e92 100644 --- a/src/vhd.c +++ b/src/vhd.c @@ -896,7 +896,7 @@ PF_TYPE_DECL(WINAPI, DWORD, GetVirtualDiskOperationProgress, (HANDLE, LPOVERLAPP // 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) +static DWORD WINAPI SaveVHDThread(void* param) { IMG_SAVE* img_save = (IMG_SAVE*)param; HANDLE handle = INVALID_HANDLE_VALUE; @@ -906,7 +906,7 @@ static DWORD WINAPI SaveImageThread(void* param) STOPGAP_CREATE_VIRTUAL_DISK_PARAMETERS vparams = { 0 }; VIRTUAL_DISK_PROGRESS vprogress = { 0 }; OVERLAPPED overlapped = { 0 }; - DWORD result, flags; + DWORD r = ERROR_NOT_FOUND, flags; PF_INIT_OR_OUT(CreateVirtualDisk, VirtDisk); PF_INIT_OR_OUT(GetVirtualDiskOperationProgress, VirtDisk); @@ -938,16 +938,16 @@ static DWORD WINAPI SaveImageThread(void* param) // CreateVirtualDisk() does not have an overwrite flag... DeleteFileW(wDst); - result = pfCreateVirtualDisk(&vtype, wDst, VIRTUAL_DISK_ACCESS_NONE, NULL, + r = pfCreateVirtualDisk(&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); + if (r != ERROR_SUCCESS && r != ERROR_IO_PENDING) { + SetLastError(r); uprintf("Could not create virtual disk: %s", WindowsErrorString()); goto out; } - if (result == ERROR_IO_PENDING) { - while ((result = WaitForSingleObject(overlapped.hEvent, 100)) == WAIT_TIMEOUT) { + if (r == ERROR_IO_PENDING) { + while ((r = WaitForSingleObject(overlapped.hEvent, 100)) == WAIT_TIMEOUT) { if (IS_ERROR(FormatStatus) && (SCODE_CODE(FormatStatus) == ERROR_CANCELLED)) { CancelIoEx(handle, &overlapped); goto out; @@ -957,12 +957,13 @@ static DWORD WINAPI SaveImageThread(void* param) UpdateProgressWithInfo(OP_FORMAT, MSG_261, vprogress.CurrentValue, vprogress.CompletionValue); } } - if (result != WAIT_OBJECT_0) { + if (r != WAIT_OBJECT_0) { uprintf("Could not save virtual disk: %s", WindowsErrorString()); goto out; } } + r = 0; UpdateProgressWithInfo(OP_FORMAT, MSG_261, SelectedDrive.DiskSize, SelectedDrive.DiskSize); uprintf("Operation complete."); @@ -974,7 +975,36 @@ out: safe_free(img_save->DevicePath); safe_free(img_save->ImagePath); PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)TRUE, 0); - ExitThread(0); + ExitThread(r); +} + +// FfuProvider.dll has some nice FfuApplyImage()/FfuCaptureImage() calls... which +// Microsoft decided not make public! +// Considering that trying to both figure out how to use these internal function +// calls, as well as how to properly hook into the DLL for every arch/every release +// of Windows, would be a massive timesink, we just take a shortcut by calling dism +// directly, as imperfect as such a solution might be... +static DWORD WINAPI SaveFFUThread(void* param) +{ + DWORD r; + IMG_SAVE* img_save = (IMG_SAVE*)param; + char cmd[MAX_PATH + 128], *letter = "", *label; + + GetDriveLabel(SelectedDrive.DeviceNumber, letter, &label, TRUE); + static_sprintf(cmd, "dism /capture-ffu /capturedrive=%s /imagefile=\"%s\" " + "/name:\"%s\" /description:\"Created by %s (%s)\"", + img_save->DevicePath, img_save->ImagePath, label, APPLICATION_NAME, RUFUS_URL); + uprintf("Running command: '%s", cmd); + r = RunCommandWithProgress(cmd, sysnative_dir, TRUE, MSG_261); + if (r != 0 && !IS_ERROR(FormatStatus)) { + SetLastError(r); + uprintf("Failed to create FFU image: %s", WindowsErrorString()); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_WINDOWS) | SCODE_CODE(r); + } + safe_free(img_save->DevicePath); + safe_free(img_save->ImagePath); + PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)TRUE, 0); + ExitThread(r); } void SaveVHD(void) @@ -983,7 +1013,8 @@ void SaveVHD(void) 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))); + EXT_DECL(img_ext, filename, __VA_GROUP__("*.vhdx", "*.vhd", "*.ffu"), + __VA_GROUP__(lmprintf(MSG_342), lmprintf(MSG_343), lmprintf(MSG_344))); ULARGE_INTEGER free_space; if ((DriveIndex < 0) || (format_thread != NULL)) @@ -992,9 +1023,17 @@ void SaveVHD(void) static_sprintf(filename, "%s.vhdx", rufus_drive[DriveIndex].label); img_save.DeviceNum = (DWORD)ComboBox_GetItemData(hDeviceList, DriveIndex); img_save.DevicePath = GetPhysicalName(img_save.DeviceNum); + // FFU support started with Windows 10 1709 (build 16299) and requires GPT + // TODO: Better check for FFU compatibility + if (WindowsVersion.Major < 10 || WindowsVersion.BuildNumber < 16299 || + SelectedDrive.PartitionStyle != PARTITION_STYLE_GPT) + img_ext.count = 2; 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.Type = VIRTUAL_STORAGE_TYPE_DEVICE_VHD; + if (safe_strstr(img_save.ImagePath, ".vhdx") != NULL) + img_save.Type = VIRTUAL_STORAGE_TYPE_DEVICE_VHD; + else if (safe_strstr(img_save.ImagePath, ".ffu") != NULL) + img_save.Type = VIRTUAL_STORAGE_TYPE_DEVICE_FFU; img_save.BufSize = DD_BUFFER_SIZE; img_save.DeviceSize = SelectedDrive.DiskSize; if (img_save.DevicePath != NULL && img_save.ImagePath != NULL) { @@ -1009,7 +1048,8 @@ void SaveVHD(void) EnableControls(FALSE, FALSE); FormatStatus = 0; InitProgress(TRUE); - format_thread = CreateThread(NULL, 0, SaveImageThread, &img_save, 0, NULL); + format_thread = CreateThread(NULL, 0, img_save.Type == VIRTUAL_STORAGE_TYPE_DEVICE_FFU ? + SaveFFUThread : SaveVHDThread, &img_save, 0, NULL); if (format_thread != NULL) { uprintf("\r\nSave to VHD operation started"); PrintInfo(0, -1); diff --git a/src/vhd.h b/src/vhd.h index 4eee18a8..6609b4aa 100644 --- a/src/vhd.h +++ b/src/vhd.h @@ -72,7 +72,10 @@ #define MBR_SIZE 512 // Might need to review this once we see bootable 4k systems // TODO: Remove this once MinGW has been updated +#ifndef VIRTUAL_STORAGE_TYPE_DEVICE_VHDX #define VIRTUAL_STORAGE_TYPE_DEVICE_VHDX 3 +#endif +#define VIRTUAL_STORAGE_TYPE_DEVICE_FFU 99 #define CREATE_VIRTUAL_DISK_VERSION_2 2 #define CREATE_VIRTUAL_DISK_FLAG_CREATE_BACKING_STORAGE 8