diff --git a/src/dos_locale.c b/src/dos_locale.c index fbb25403..b042b10b 100644 --- a/src/dos_locale.c +++ b/src/dos_locale.c @@ -984,7 +984,8 @@ BOOL SetDOSLocale(const char* path, BOOL bFreeDOS) } if (bFreeDOS) { fprintf(fd, "!MENUCOLOR=7,0\nMENU\nMENU FreeDOS Language Selection Menu\n"); - fprintf(fd, "MENU ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\nMENU\n"); + fprintf(fd, "MENU \xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\nMENU\n"); } else { fprintf(fd, "[MENU]\n"); } diff --git a/src/drive.c b/src/drive.c index 53b2a020..98a97fed 100644 --- a/src/drive.c +++ b/src/drive.c @@ -60,6 +60,7 @@ HANDLE GetDriveHandle(DWORD DriveIndex, char* DriveLetter, BOOL bWriteAccess, BO char logical_drive[] = "\\\\.\\#:"; char physical_drive[24]; + DriveIndex &= DRIVE_INDEX_MASK; if ((DriveIndex < DRIVE_INDEX_MIN) || (DriveIndex > DRIVE_INDEX_MAX)) { uprintf("WARNING: Bad index value. Please check the code!\n"); } @@ -160,8 +161,10 @@ BOOL GetDriveLabel(DWORD DriveIndex, char* letter, char** label) *label = STR_NO_LABEL; hDrive = GetDriveHandle(DriveIndex, letter, FALSE, FALSE); - if (hDrive == INVALID_HANDLE_VALUE) - return FALSE; + if (hDrive == INVALID_HANDLE_VALUE) { + // Assume we have a raw drive without volume assigned if enable_fixed_disk is true + return enable_fixed_disks; + } safe_closehandle(hDrive); AutorunPath[0] = *letter; wDrivePath[0] = *letter; @@ -206,7 +209,7 @@ BOOL GetDrivePartitionData(DWORD DeviceNumber, char* FileSystemName, DWORD FileS char DrivePath[] = "#:\\", tmp[256]; DWORD i, nb_partitions = 0; - hDrive = GetDriveHandle(DeviceNumber, DrivePath, FALSE, FALSE); + hDrive = GetDriveHandle(DeviceNumber, NULL, FALSE, FALSE); if (hDrive == INVALID_HANDLE_VALUE) return FALSE; diff --git a/src/format.c b/src/format.c index ca33ca68..437515c5 100644 --- a/src/format.c +++ b/src/format.c @@ -1093,13 +1093,56 @@ static BOOL RemountVolume(char drive_letter) return TRUE; } +/* + * Detect if a Windows Format prompt is active, by enumerating the + * whole Windows tree and looking for the relevant popup + */ +static BOOL CALLBACK FormatPromptCallback(HWND hWnd, LPARAM lParam) +{ + char str_buf[MAX_PATH]; + HWND *hFound = (HWND*)lParam; + static const char* security_string = "Microsoft Windows"; + + // The format prompt has the popup window style + if (GetWindowLong(hWnd, GWL_STYLE) & WS_POPUPWINDOW) { + str_buf[0] = 0; + GetWindowTextA(hWnd, str_buf, MAX_PATH); + str_buf[MAX_PATH-1] = 0; + if (safe_strcmp(str_buf, security_string) == 0) { + *hFound = hWnd; + return TRUE; + } + } + return TRUE; +} + +/* + * When we format a drive that doesn't have any existing partitions, we can't lock it + * prior to partitioning, which means that Windows will display a "You need to format the + * disk in drive X: before you can use it'. We have to close that popup manually. + */ +DWORD WINAPI CloseFormatPromptThread(LPVOID param) { + HWND hFormatPrompt; + + while(format_op_in_progress) { + hFormatPrompt = NULL; + EnumChildWindows(GetDesktopWindow(), FormatPromptCallback, (LPARAM)&hFormatPrompt); + if (hFormatPrompt != NULL) { + SendMessage(hFormatPrompt, WM_COMMAND, (WPARAM)IDCANCEL, (LPARAM)0); + uprintf("Closed Windows format prompt\n"); + } + Sleep(50); + } + ExitThread(0); +} + /* * Standalone thread for the formatting operation */ DWORD WINAPI FormatThread(LPVOID param) { int r, pt, bt, fs, dt; - BOOL ret; + BOOL ret, no_volume = FALSE; DWORD num = (DWORD)(uintptr_t)param; HANDLE hPhysicalDrive = INVALID_HANDLE_VALUE; HANDLE hLogicalVolume = INVALID_HANDLE_VALUE; @@ -1116,6 +1159,11 @@ DWORD WINAPI FormatThread(LPVOID param) pt = GETPARTTYPE((int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme))); bt = GETBIOSTYPE((int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme))); + if (num & DRIVE_INDEX_RAW_DRIVE) { + no_volume = TRUE; + uprintf("Using raw drive mode\n"); + } + hPhysicalDrive = GetDriveHandle(num, NULL, TRUE, TRUE); if (hPhysicalDrive == INVALID_HANDLE_VALUE) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; @@ -1126,16 +1174,19 @@ DWORD WINAPI FormatThread(LPVOID param) // ... but we can't write sectors that are part of a volume, even if we have // access to physical, unless we have a lock (which doesn't have to be write) // Also, having a volume handle allows us to unmount the volume - hLogicalVolume = GetDriveHandle(num, drive_name, FALSE, TRUE); - if (hLogicalVolume == INVALID_HANDLE_VALUE) { - uprintf("Could not lock volume\n"); - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; - goto out; + if (!no_volume) { + hLogicalVolume = GetDriveHandle(num, drive_name, FALSE, TRUE); + if (hLogicalVolume == INVALID_HANDLE_VALUE) { + uprintf("Could not lock volume\n"); + FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; + goto out; + } + UnmountDrive(hLogicalVolume); } - UnmountDrive(hLogicalVolume); AnalyzeMBR(hPhysicalDrive); - AnalyzePBR(hLogicalVolume); + if (!no_volume) + AnalyzePBR(hLogicalVolume); if (IsChecked(IDC_BADBLOCKS)) { do { @@ -1198,7 +1249,8 @@ DWORD WINAPI FormatThread(LPVOID param) } } // Close the (unmounted) volume before formatting, but keep the lock - safe_closehandle(hLogicalVolume); + if (!no_volume) + safe_closehandle(hLogicalVolume); // Especially after destructive badblocks test, you must zero the MBR/GPT completely // before repartitioning. Else, all kind of bad things can happen. @@ -1210,6 +1262,7 @@ DWORD WINAPI FormatThread(LPVOID param) } UpdateProgress(OP_ZERO_MBR, -1.0f); + CreateThread(NULL, 0, CloseFormatPromptThread, NULL, 0, NULL); if (!CreatePartition(hPhysicalDrive, pt, fs, (pt==PARTITION_STYLE_MBR)&&(bt==BT_UEFI))) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_PARTITION_FAILURE; goto out; @@ -1219,6 +1272,18 @@ DWORD WINAPI FormatThread(LPVOID param) // Add a small delay after partitioning to be safe Sleep(200); + if (no_volume) { + hLogicalVolume = GetDriveHandle(num, drive_name, FALSE, TRUE); + if (hLogicalVolume == INVALID_HANDLE_VALUE) { + uprintf("Could not lock volume\n"); + FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; + goto out; + } + UnmountDrive(hLogicalVolume); + safe_closehandle(hLogicalVolume); + } + + // If FAT32 is requested and we have a large drive (>32 GB) use // large FAT32 format, else use MS's FormatEx. ret = ((fs == FS_FAT32) && (SelectedDrive.DiskSize > LARGE_FAT32_SIZE))? diff --git a/src/rufus.c b/src/rufus.c index 836cf5d0..c2f018ad 100644 --- a/src/rufus.c +++ b/src/rufus.c @@ -94,7 +94,6 @@ static BOOL iso_size_check = TRUE; static BOOL log_displayed = FALSE; static BOOL iso_provided = FALSE; static int selection_default; -BOOL enable_fixed_disks = FALSE, advanced_mode = TRUE; /* * Globals @@ -109,6 +108,7 @@ HWND hDeviceList, hPartitionScheme, hFileSystem, hClusterSize, hLabel, hBootType HWND hISOProgressDlg = NULL, hLogDlg = NULL, hISOProgressBar, hISOFileName, hDiskID; BOOL use_own_c32[NB_OLD_C32] = {FALSE, FALSE}, detect_fakes = TRUE, mbr_selected_by_user = FALSE; BOOL iso_op_in_progress = FALSE, format_op_in_progress = FALSE; +BOOL enable_fixed_disks = FALSE, advanced_mode = TRUE; int dialog_showing = 0; uint16_t rufus_version[4]; RUFUS_UPDATE update = { {0,0,0,0}, {0,0}, NULL, NULL}; @@ -657,11 +657,22 @@ static BOOL GetUSBDevices(DWORD devnum) continue; } + if (device_number.DeviceNumber >= MAX_DRIVES) { + uprintf("Device Number %d is too big - ignoring device\n"); + continue; + } + if (GetDriveLabel(device_number.DeviceNumber + DRIVE_INDEX_MIN, &drive_letter, &label)) { // Must ensure that the combo box is UNSORTED for indexes to be the same StrArrayAdd(&DriveID, buffer); StrArrayAdd(&DriveLabel, label); - safe_sprintf(entry, sizeof(entry), "%s (%c:)", label, drive_letter); + // Drive letter ' ' is returned for drives that don't have a volume assigned yet + if (drive_letter == ' ') { + safe_sprintf(entry, sizeof(entry), "%s (Disk %d)", label, device_number.DeviceNumber); + device_number.DeviceNumber |= DRIVE_INDEX_RAW_DRIVE; + } else { + safe_sprintf(entry, sizeof(entry), "%s (%c:)", label, drive_letter); + } IGNORE_RETVAL(ComboBox_SetItemData(hDeviceList, ComboBox_AddStringU(hDeviceList, entry), device_number.DeviceNumber + DRIVE_INDEX_MIN)); maxwidth = max(maxwidth, GetEntryWidth(hDeviceList, entry)); @@ -906,6 +917,14 @@ static void CALLBACK ClockTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dw SendMessageA(GetDlgItem(hWnd, IDC_STATUS), SB_SETTEXTA, SBT_OWNERDRAW | 1, (LPARAM)szTimer); } +/* + * Device Refresh Timer + */ +static void CALLBACK RefreshTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + SendMessage(hWnd, WM_DEVICECHANGE, DBT_CUSTOMEVENT, 0); +} + /* * Detect and notify about a blocking operation during ISO extraction cancellation */ @@ -1346,7 +1365,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA POINT Point; RECT DialogRect, DesktopRect; int nDeviceIndex, fs, bt, i, nWidth, nHeight; - static DWORD DeviceNum = 0; + static DWORD DeviceNum = 0, LastRefresh = 0; wchar_t wtmp[128], wstr[MAX_PATH]; static UINT uDOSChecked = BST_CHECKED, uQFChecked; static BOOL first_log_display = TRUE, user_changed_label = FALSE; @@ -1354,11 +1373,33 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA switch (message) { case WM_DEVICECHANGE: - if ( (format_thid == NULL) && - ((wParam == DBT_DEVICEARRIVAL) || (wParam == DBT_DEVICEREMOVECOMPLETE)) ) { - GetUSBDevices((DWORD)ComboBox_GetItemData(hDeviceList, ComboBox_GetCurSel(hDeviceList))); - user_changed_label = FALSE; - return (INT_PTR)TRUE; + // The Windows hotplug subsystem sucks. Among other things, if you insert a GPT partitioned + // USB drive with zero partitions, the only device messages you will get are a stream of + // DBT_DEVNODES_CHANGED and that's it. But those messages are also issued when you get a + // DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE, and there's a whole slew of them so we + // can't really issue a refresh for each one we receive + // What we do then is arm a timer on DBT_DEVNODES_CHANGED, if it's been more than 1 second + // since last refresh/arm timer, and have that timer send DBT_CUSTOMEVENT when it expires. + if (format_thid == NULL) { + switch (wParam) { + case DBT_DEVICEARRIVAL: + case DBT_DEVICEREMOVECOMPLETE: + case DBT_CUSTOMEVENT: // This last event is sent by our timer refresh function + LastRefresh = GetTickCount(); // Don't care about 49.7 days rollback of GetTickCount() + KillTimer(hMainDialog, TID_REFRESH_TIMER); + GetUSBDevices((DWORD)ComboBox_GetItemData(hDeviceList, ComboBox_GetCurSel(hDeviceList))); + user_changed_label = FALSE; + return (INT_PTR)TRUE; + case DBT_DEVNODES_CHANGED: + // If it's been more than a second since last device refresh, arm a refresh timer + if (GetTickCount() > LastRefresh + 1000) { + LastRefresh = GetTickCount(); + SetTimer(hMainDialog, TID_REFRESH_TIMER, 1000, RefreshTimer); + } + break; + default: + break; + } } break; diff --git a/src/rufus.h b/src/rufus.h index 0dd4a24e..9005a69a 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -36,9 +36,11 @@ #define STR_NO_LABEL "NO_LABEL" #define RUFUS_CANCELBOX_TITLE APPLICATION_NAME " - Cancellation" #define RUFUS_BLOCKING_IO_TITLE APPLICATION_NAME " - Flushing buffers" -#define DRIVE_INDEX_MIN 0x80 -#define DRIVE_INDEX_MAX 0xC0 -#define MAX_DRIVES 16 +#define DRIVE_INDEX_MIN 0x00000080 +#define DRIVE_INDEX_MAX 0x000000C0 +#define DRIVE_INDEX_MASK 0x0000FFFF +#define DRIVE_INDEX_RAW_DRIVE 0x00010000 // Additional drive properties stored in the drive index +#define MAX_DRIVES (DRIVE_INDEX_MAX - DRIVE_INDEX_MIN) #define MAX_TOOLTIPS 32 #define MAX_PROGRESS (0xFFFF-1) // leave room for 1 more for insta-progress workaround #define MAX_LOG_SIZE 0x7FFFFFFE @@ -127,7 +129,8 @@ enum timer_type { TID_MESSAGE = 0x1000, TID_BADBLOCKS_UPDATE, TID_APP_TIMER, - TID_BLOCKING_TIMER + TID_BLOCKING_TIMER, + TID_REFRESH_TIMER }; /* Action type, for progress bar breakdown */ diff --git a/src/rufus.rc b/src/rufus.rc index 1a26b202..59ab4bd3 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -30,7 +30,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 206, 316 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_APPWINDOW -CAPTION "Rufus v1.3.3.237" +CAPTION "Rufus v1.3.3.238" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "Start",IDC_START,94,278,50,14 @@ -274,8 +274,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,3,3,237 - PRODUCTVERSION 1,3,3,237 + FILEVERSION 1,3,3,238 + PRODUCTVERSION 1,3,3,238 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -292,13 +292,13 @@ BEGIN BEGIN VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "1.3.3.237" + VALUE "FileVersion", "1.3.3.238" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "(c) 2011-2013 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html" VALUE "OriginalFilename", "rufus.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "1.3.3.237" + VALUE "ProductVersion", "1.3.3.238" END END BLOCK "VarFileInfo"