diff --git a/src/drive.c b/src/drive.c index 6a47b09f..db94c7bd 100644 --- a/src/drive.c +++ b/src/drive.c @@ -34,6 +34,7 @@ #include "rufus.h" #include "missing.h" #include "resource.h" +#include "settings.h" #include "msapi_utf8.h" #include "localization.h" @@ -917,7 +918,7 @@ UINT GetDriveTypeFromIndex(DWORD DriveIndex) char GetUnusedDriveLetter(void) { DWORD size; - char drive_letter = 'Z'+1, *drive, drives[26*4 + 1]; /* "D:\", "E:\", etc., plus one NUL */ + char drive_letter, *drive, drives[26*4 + 1]; /* "D:\", "E:\", etc., plus one NUL */ size = GetLogicalDriveStringsA(sizeof(drives), drives); if (size == 0) { @@ -930,7 +931,7 @@ char GetUnusedDriveLetter(void) } for (drive_letter = 'C'; drive_letter <= 'Z'; drive_letter++) { - for (drive = drives ;*drive; drive += safe_strlen(drive)+1) { + for (drive = drives ; *drive; drive += safe_strlen(drive) + 1) { if (!isalpha(*drive)) continue; if (drive_letter == (char)toupper((int)*drive)) @@ -941,7 +942,30 @@ char GetUnusedDriveLetter(void) } out: - return (drive_letter>'Z')?0:drive_letter; + return (drive_letter > 'Z') ? 0 : drive_letter; +} + +BOOL IsDriveLetterInUse(const char drive_letter) +{ + DWORD size; + char *drive, drives[26 * 4 + 1]; + + size = GetLogicalDriveStringsA(sizeof(drives), drives); + if (size == 0) { + uprintf("GetLogicalDriveStrings failed: %s", WindowsErrorString()); + return TRUE; + } + if (size > sizeof(drives)) { + uprintf("GetLogicalDriveStrings: Buffer too small (required %d vs. %d)", size, sizeof(drives)); + return TRUE; + } + + for (drive = drives; *drive; drive += safe_strlen(drive) + 1) { + if (drive_letter == (char)toupper((int)*drive)) + return TRUE; + } + + return FALSE; } /* @@ -1130,6 +1154,142 @@ BOOL AnalyzePBR(HANDLE hLogicalVolume) return TRUE; } +static BOOL StoreEspInfo(GUID* guid) +{ + uint8_t j; + char key_name[2][16], *str; + // Look for an empty slot and use that if available + for (j = 1; j <= MAX_ESP_TOGGLE; j++) { + static_sprintf(key_name[0], "ToggleEsp%02u", j); + str = ReadSettingStr(key_name[0]); + if ((str == NULL) || (str[0] == 0)) + return WriteSettingStr(key_name[0], GuidToString(guid)); + } + // All slots are used => Move every key down and add to last slot + // NB: No, we don't care that the slot we remove may not be the oldest. + for (j = 1; j < MAX_ESP_TOGGLE; j++) { + static_sprintf(key_name[0], "ToggleEsp%02u", j); + static_sprintf(key_name[1], "ToggleEsp%02u", j + 1); + WriteSettingStr(key_name[0], ReadSettingStr(key_name[1])); + } + return WriteSettingStr(key_name[1], GuidToString(guid)); +} + +static GUID* GetEspGuid(uint8_t index) +{ + char key_name[16]; + + static_sprintf(key_name, "ToggleEsp%02u", index); + return StringToGuid(ReadSettingStr(key_name)); +} + +static BOOL ClearEspInfo(uint8_t index) +{ + char key_name[16]; + static_sprintf(key_name, "ToggleEsp%02u", index); + return WriteSettingStr(key_name, ""); +} + +/* + * This calls changes the type of a GPT ESP back and forth to Basic Data. + * Needed because Windows 10 doesn't mount ESPs by default, and also + * doesn't let usermode apps (such as File Explorer) access mounted ESPs. + */ +BOOL ToggleEsp(DWORD DriveIndex) +{ + char *volume_name, mount_point[] = DEFAULT_ESP_MOUNT_POINT; + BOOL r, ret = FALSE, found = FALSE; + HANDLE hPhysical; + DWORD size, i, j, esp_index = 0; + BYTE layout[4096] = { 0 }; + GUID* guid; + PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = (PDRIVE_LAYOUT_INFORMATION_EX)(void*)layout; + + if (nWindowsVersion < WINDOWS_10) { + uprintf("ESP toggling is only available for Windows 10 or later"); + return FALSE; + } + + hPhysical = GetPhysicalHandle(DriveIndex, FALSE, TRUE, TRUE); + if (hPhysical == INVALID_HANDLE_VALUE) + return FALSE; + + r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, + NULL, 0, layout, sizeof(layout), &size, NULL); + if (!r || size <= 0) { + uprintf("Could not get layout for drive 0x%02x: %s", DriveIndex, WindowsErrorString()); + goto out; + } + if (DriveLayout->PartitionStyle != PARTITION_STYLE_GPT) { + uprintf("ESP toggling is only available for GPT drives"); + goto out; + } + + // See if the current drive contains an ESP + for (i = 0, j = 0; i < DriveLayout->PartitionCount; i++) { + if (CompareGUID(&DriveLayout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_GENERIC_ESP)) { + esp_index = i; + j++; + } + } + + if (j > 1) { + uprintf("ESP toggling is not available for drives with more than one ESP"); + goto out; + } + if (j == 1) { + // ESP -> Basic Data + i = esp_index; + uprintf("ESP name: '%S'", DriveLayout->PartitionEntry[i].Gpt.Name); + if (!StoreEspInfo(&DriveLayout->PartitionEntry[i].Gpt.PartitionId)) { + uprintf("ESP toggling data could not be stored"); + goto out; + } + DriveLayout->PartitionEntry[i].Gpt.PartitionType = PARTITION_MICROSOFT_DATA; + } else { + // Basic Data -> ESP + for (j = 1; j <= MAX_ESP_TOGGLE; j++) { + guid = GetEspGuid((uint8_t)j); + if (guid != NULL) { + for (i = 0; i < DriveLayout->PartitionCount; i++) { + if (CompareGUID(guid, &DriveLayout->PartitionEntry[i].Gpt.PartitionId)) { + uprintf("BD name: '%S'", DriveLayout->PartitionEntry[i].Gpt.Name); + found = TRUE; + break; + } + } + if (found) + break; + } + } + if (j > MAX_ESP_TOGGLE) + goto out; + DriveLayout->PartitionEntry[i].Gpt.PartitionType = PARTITION_GENERIC_ESP; + } + + DriveLayout->PartitionEntry[i].RewritePartition = TRUE; // Just in case + r = DeviceIoControl(hPhysical, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, (BYTE*)DriveLayout, size, NULL, 0, &size, NULL); + if (!r) { + uprintf("Could not set drive layout: %s", WindowsErrorString()); + return FALSE; + } + RefreshDriveLayout(hPhysical); + if (CompareGUID(&DriveLayout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_GENERIC_ESP)) { + // We successfully reverted ESP from Basic Data -> Delete stored ESP info + ClearEspInfo((uint8_t)j); + } else if (!IsDriveLetterInUse(*mount_point)) { + // We succesfully switched ESP to Basic Data -> Try to mount it + volume_name = GetLogicalName(DriveIndex, DriveLayout->PartitionEntry[i].StartingOffset.QuadPart, TRUE, FALSE); + MountVolume(mount_point, volume_name); + free(volume_name); + } + ret = TRUE; + +out: + safe_closehandle(hPhysical); + return ret; +} + /* * Fill the drive properties (size, FS, etc) * Returns TRUE if the drive has a partition that can be mounted in Windows, FALSE otherwise @@ -1162,14 +1322,14 @@ BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSys hPhysical = GetPhysicalHandle(DriveIndex, FALSE, FALSE, TRUE); if (hPhysical == INVALID_HANDLE_VALUE) - return 0; + return FALSE; r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, geometry, sizeof(geometry), &size, NULL); if (!r || size <= 0) { suprintf("Could not get geometry for drive 0x%02x: %s", DriveIndex, WindowsErrorString()); safe_closehandle(hPhysical); - return 0; + return FALSE; } SelectedDrive.DiskSize = DiskGeometry->DiskSize.QuadPart; SelectedDrive.SectorSize = DiskGeometry->Geometry.BytesPerSector; @@ -1192,7 +1352,7 @@ BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSys if (!r || size <= 0) { suprintf("Could not get layout for drive 0x%02x: %s", DriveIndex, WindowsErrorString()); safe_closehandle(hPhysical); - return 0; + return FALSE; } #if defined(__GNUC__) @@ -1282,8 +1442,10 @@ BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSys } SelectedDrive.nPartitions++; isUefiNtfs = (wcscmp(DriveLayout->PartitionEntry[i].Gpt.Name, L"UEFI:NTFS") == 0); - suprintf("Partition %d%s:\r\n Type: %s\r\n Name: '%S'", i+1, isUefiNtfs ? " (UEFI:NTFS)" : "", - GetGPTPartitionType(&DriveLayout->PartitionEntry[i].Gpt.PartitionType), DriveLayout->PartitionEntry[i].Gpt.Name); + suprintf("Partition %d%s:\r\n Type: %s", i+1, isUefiNtfs ? " (UEFI:NTFS)" : "", + GetGPTPartitionType(&DriveLayout->PartitionEntry[i].Gpt.PartitionType)); + if (DriveLayout->PartitionEntry[i].Gpt.Name[0] != 0) + suprintf(" Name: '%S'", DriveLayout->PartitionEntry[i].Gpt.Name); suprintf(" ID: %s\r\n Size: %s (%" PRIi64 " bytes)\r\n Start Sector: %" PRIi64 ", Attributes: 0x%016" PRIX64, GuidToString(&DriveLayout->PartitionEntry[i].Gpt.PartitionId), SizeToHumanReadable(DriveLayout->PartitionEntry[i].PartitionLength.QuadPart, TRUE, FALSE), diff --git a/src/drive.h b/src/drive.h index e14a0d85..b4f5e0e2 100644 --- a/src/drive.h +++ b/src/drive.h @@ -1,7 +1,7 @@ /* * Rufus: The Reliable USB Formatting Utility * Drive access function calls - * Copyright © 2011-2019 Pete Batard + * Copyright © 2011-2020 Pete Batard * * 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 @@ -377,6 +377,7 @@ int GetDriveNumber(HANDLE hDrive, char* path); BOOL GetDriveLetters(DWORD DriveIndex, char* drive_letters); UINT GetDriveTypeFromIndex(DWORD DriveIndex); char GetUnusedDriveLetter(void); +BOOL IsDriveLetterInUse(const char drive_letter); BOOL GetDriveLabel(DWORD DriveIndex, char* letter, char** label); uint64_t GetDriveSize(DWORD DriveIndex); BOOL IsMediaPresent(DWORD DriveIndex); @@ -399,3 +400,4 @@ BOOL CyclePort(int index); int CycleDevice(int index); BOOL RefreshLayout(DWORD DriveIndex); BOOL GetOpticalMedia(IMG_SAVE* img_save); +BOOL ToggleEsp(DWORD DriveIndex); diff --git a/src/rufus.c b/src/rufus.c index 9a504265..257cf1b1 100755 --- a/src/rufus.c +++ b/src/rufus.c @@ -3286,7 +3286,7 @@ relaunch: while(GetMessage(&msg, NULL, 0, 0)) { static BOOL ctrl_without_focus = FALSE; BOOL no_focus = (msg.message == WM_SYSKEYDOWN) && !(msg.lParam & 0x20000000); - // ** ****** **** ** ********** + // ** ****** **** ************* // .,ABCDEFGHIJKLMNOPQRSTUVWXYZ // Sigh... The things one need to do to detect standalone use of the 'Alt' key. @@ -3452,6 +3452,14 @@ relaunch: SaveISO(); continue; } + // Alt-P => Toggle GPT ESP to and from Basic Data type (Windows 10 or later) + if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'P')) { + int index = ComboBox_GetCurSel(hDeviceList); + DWORD DeviceNum = (DWORD)ComboBox_GetItemData(hDeviceList, index); + if (ToggleEsp(DeviceNum)) + CyclePort(index); + continue; + } // Alt-Q => Enable file indexing (for file systems that support it) // For multiple reasons, file indexing is disabled by default if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'Q')) { diff --git a/src/rufus.h b/src/rufus.h index b527c796..4cbe2851 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -71,6 +71,7 @@ #define MAX_REFRESH 25 // How long we should wait to refresh UI elements (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 #define MAX_SECTORS_TO_CLEAR 128 // nb sectors to zap when clearing the MBR/GPT (must be >34) #define MAX_WININST 4 // Max number of install[.wim|.esd] we can handle on an image #define MBR_UEFI_MARKER 0x49464555 // 'U', 'E', 'F', 'I', as a 32 bit little endian longword @@ -111,6 +112,7 @@ #define WPPRECORDER_MORE_INFO_URL "https://github.com/pbatard/rufus/wiki/FAQ#BSODs_with_Windows_To_Go_drives_created_from_Windows_10_1809_ISOs" #define SEVENZIP_URL "https://www.7-zip.org" #define FILES_DIR "rufus_files" +#define DEFAULT_ESP_MOUNT_POINT "S:\\" #define IS_POWER_OF_2(x) ((x != 0) && (((x) & ((x) - 1)) == 0)) #define IGNORE_RETVAL(expr) do { (void)(expr); } while(0) #ifndef ARRAYSIZE @@ -485,6 +487,7 @@ extern void UpdateProgressWithInfo(int op, int msg, uint64_t processed, uint64_t #define UpdateProgressWithInfoInit(hProgressDialog, bNoAltMode) UpdateProgressWithInfo(OP_INIT, (int)bNoAltMode, (uint64_t)(uintptr_t)hProgressDialog, 0); extern const char* StrError(DWORD error_code, BOOL use_default_locale); extern char* GuidToString(const GUID* guid); +extern GUID* StringToGuid(const char* str); extern char* SizeToHumanReadable(uint64_t size, BOOL copy_to_log, BOOL fake_units); extern char* TimestampToHumanReadable(uint64_t ts); extern HWND MyCreateDialog(HINSTANCE hInstance, int Dialog_ID, HWND hWndParent, DLGPROC lpDialogFunc); diff --git a/src/rufus.rc b/src/rufus.rc index 7c478047..855d6540 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 3.11.1667" +CAPTION "Rufus 3.11.1668" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -395,8 +395,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,11,1667,0 - PRODUCTVERSION 3,11,1667,0 + FILEVERSION 3,11,1668,0 + PRODUCTVERSION 3,11,1668,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -414,13 +414,13 @@ BEGIN VALUE "Comments", "https://rufus.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "3.11.1667" + VALUE "FileVersion", "3.11.1668" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2020 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" VALUE "OriginalFilename", "rufus-3.11.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "3.11.1667" + VALUE "ProductVersion", "3.11.1668" END END BLOCK "VarFileInfo" diff --git a/src/stdio.c b/src/stdio.c index db935b0a..48cba5e1 100644 --- a/src/stdio.c +++ b/src/stdio.c @@ -674,12 +674,26 @@ char* GuidToString(const GUID* guid) if (guid == NULL) return NULL; sprintf(guid_string, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", - (unsigned int)guid->Data1, guid->Data2, guid->Data3, + (uint32_t)guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); return guid_string; } +GUID* StringToGuid(const char* str) +{ + static GUID guid; + + if (str == NULL) return NULL; + if (sscanf(str, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + (uint32_t*)&guid.Data1, (uint32_t*)&guid.Data2, (uint32_t*)&guid.Data3, + (uint32_t*)&guid.Data4[0], (uint32_t*)&guid.Data4[1], (uint32_t*)&guid.Data4[2], + (uint32_t*)&guid.Data4[3], (uint32_t*)&guid.Data4[4], (uint32_t*)&guid.Data4[5], + (uint32_t*)&guid.Data4[6], (uint32_t*)&guid.Data4[7]) != 11) + return NULL; + return &guid; +} + // Find upper power of 2 static __inline uint16_t upo2(uint16_t v) {