From b4a2c06a2ef36de6245e69ad6162a2698940154c Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Sun, 16 Jul 2017 22:42:19 +0100 Subject: [PATCH] [ui] ask user if they want proceed when conflicting processes are found * Rufus now checks for processes with handles opened on the drives/volumes before starting the format operation and asks the user if they want to continue. * This mimics Windows' behaviour when formatting drives, and actually uses the same message as the one from shell32.dll.mui. * Closes #773 --- src/iso.c | 6 ++--- src/msapi_utf8.h | 14 ++++++++++ src/process.c | 2 +- src/rufus.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++- src/rufus.h | 2 ++ src/rufus.rc | 10 +++---- src/stdfn.c | 32 +++++++++++++++++++++++ src/stdlg.c | 3 +++ 8 files changed, 127 insertions(+), 10 deletions(-) diff --git a/src/iso.c b/src/iso.c index f436cb57..7791a036 100644 --- a/src/iso.c +++ b/src/iso.c @@ -277,7 +277,7 @@ static void fix_config(const char* psz_fullpath, const char* psz_path, const cha if ((iso_label != NULL) && (usb_label != NULL)) { if (replace_in_token_data(src, (props->is_grub_cfg) ? "linuxefi" : "append", iso_label, usb_label, TRUE) != NULL) - uprintf(" Patched %s: '%s' ⇨ '%s'\n", src, iso_label, usb_label); + uprintf(" Patched %s: '%s' ➔ '%s'\n", src, iso_label, usb_label); } safe_free(iso_label); safe_free(usb_label); @@ -301,7 +301,7 @@ static void fix_config(const char* psz_fullpath, const char* psz_path, const cha safe_sprintf(iso_label, MAX_PATH, "cd9660:/dev/iso9660/%s", img_report.label); safe_sprintf(usb_label, MAX_PATH, "msdosfs:/dev/msdosfs/%s", img_report.usb_label); if (replace_in_token_data(src, "set", iso_label, usb_label, TRUE) != NULL) - uprintf(" Patched %s: '%s' ⇨ '%s'\n", src, iso_label, usb_label); + uprintf(" Patched %s: '%s' ➔ '%s'\n", src, iso_label, usb_label); } safe_free(iso_label); safe_free(usb_label); @@ -896,7 +896,7 @@ out: fclose(fd); fd = NULL; safe_sprintf(path2, sizeof(path2), "%s\\syslinux.org", dest_dir); - uprintf("Renaming: %s ⇨ %s", path, path2); + uprintf("Renaming: %s ➔ %s", path, path2); IGNORE_RETVAL(rename(path, path2)); } if (fd == NULL) { diff --git a/src/msapi_utf8.h b/src/msapi_utf8.h index f8557b30..a0b2c0e5 100644 --- a/src/msapi_utf8.h +++ b/src/msapi_utf8.h @@ -276,6 +276,11 @@ static __inline int LoadStringU(HINSTANCE hInstance, UINT uID, LPSTR lpBuffer, i { int ret; DWORD err = ERROR_INVALID_DATA; + if (nBufferMax == 0) { + // read-only pointer to resource mode is not supported + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } // coverity[returned_null] walloc(lpBuffer, nBufferMax); ret = LoadStringW(hInstance, uID, wlpBuffer, nBufferMax); @@ -461,6 +466,15 @@ static __inline BOOL DeleteFileU(const char* lpFileName) return ret; } +static __inline BOOL PathFileExistsU(char* szPath) +{ + BOOL ret; + wconvert(szPath); + ret = PathFileExistsW(wszPath); + wfree(szPath); + return ret; +} + static __inline int PathGetDriveNumberU(char* lpPath) { int ret = 0; diff --git a/src/process.c b/src/process.c index 8e6f2ab1..066e8efb 100644 --- a/src/process.c +++ b/src/process.c @@ -384,7 +384,7 @@ BYTE SearchProcess(char* HandleName, BOOL bPartialMatch, BOOL bIgnoreSelf, BOOL // If we're switching process and found a match, print it if (bFound) { vuprintf("● '%s' (pid: %ld, access: %s)", exe_path, pid[cur_pid], access_rights_str[access_rights & 0x7]); - static_sprintf(tmp, "● %s (pid %ld)", exe_path, pid[cur_pid]); + static_sprintf(tmp, "● %s (%s)", exe_path, access_rights_str[access_rights & 0x7]); StrArrayAdd(&BlockingProcess, tmp, TRUE); bFound = FALSE; access_rights = 0; diff --git a/src/rufus.c b/src/rufus.c index 5e9bf2fc..bac5b2c0 100644 --- a/src/rufus.c +++ b/src/rufus.c @@ -2153,6 +2153,65 @@ static void SaveISO(void) } } +// Check for conflicting processes accessing the drive, and if any, +// ask the user whether they want to proceed. +static BOOL CheckDriveAccess(void) +{ + uint32_t i, j; + BOOL bProceed = TRUE; + BYTE access_mask; + char *PhysicalPath, DevPath[MAX_PATH]; + char drive_letter[27], drive_name[] = "?:"; + char *message, title[128]; + + // Get the current selected device + DWORD DeviceNum = (DWORD)ComboBox_GetItemData(hDeviceList, ComboBox_GetCurSel(hDeviceList)); + if ((DeviceNum < 0x80) || (DeviceNum == (DWORD)-1)) + return FALSE; + + // Search for any blocking processes against the physical drive + PhysicalPath = GetPhysicalName(DeviceNum); + QueryDosDeviceA(&PhysicalPath[4], DevPath, sizeof(DevPath)); + access_mask = SearchProcess(DevPath, TRUE, TRUE, TRUE); + if (access_mask != 0) { + bProceed = FALSE; + uprintf("Found potentially blocking process(es) against %s:", &PhysicalPath[4]); + for (j = 0; j < BlockingProcess.Index; j++) + uprintf(BlockingProcess.String[j]); + } + free(PhysicalPath); + + // Search for any blocking processes against the logical volume(s) + GetDriveLetters(DeviceNum, drive_letter); + for (i = 0; drive_letter[i]; i++) { + drive_name[0] = drive_letter[i]; + if (QueryDosDeviceA(drive_name, DevPath, sizeof(DevPath)) != 0) { + StrArrayClear(&BlockingProcess); + access_mask = SearchProcess(DevPath, TRUE, TRUE, TRUE); + // Ignore if all we have is read-only + if ((access_mask & 0x06) || (access_mask == 0x80)) { + bProceed = FALSE; + uprintf("Found potentially blocking process(es) against %s", drive_name); + for (j = 0; j < BlockingProcess.Index; j++) + uprintf(BlockingProcess.String[j]); + } + } + } + + // Prompt the user if we detected blocking processes + if (!bProceed) { + // We'll use a system translated string instead of one from rufus.loc + message = GetMuiString("shell32.dll", 28701); // "This drive is in use (...) Do you want to format it anyway?" + if (message != NULL) { + ComboBox_GetTextU(hDeviceList, title, sizeof(title)); + bProceed = Notification(MSG_WARNING_QUESTION, NULL, title, message); + free(message); + } + } + + return bProceed; +} + #ifdef RUFUS_TEST extern int SelectionDyn(char* title, char* message, char** szChoice, int nChoices); #endif @@ -2189,7 +2248,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA case WM_COMMAND: #ifdef RUFUS_TEST if (LOWORD(wParam) == IDC_TEST) { - SearchProcess("\\Device\\Harddisk5\\DR5", TRUE, TRUE); + uprintf("Proceed = %s", CheckDriveAccess()?"True":"False"); // char* choices[] = { "Choice 1", "Choice 2", "Choice 3" }; // SelectionDyn("Test Choice", "Unused", choices, ARRAYSIZE(choices)); break; @@ -2509,6 +2568,13 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA } } + if (!CheckDriveAccess()) { + format_op_in_progress = FALSE; + zero_drive = FALSE; + PROCESS_QUEUED_EVENTS; + break; + } + GetWindowTextU(hDeviceList, tmp, ARRAYSIZE(tmp)); if (MessageBoxExU(hMainDialog, lmprintf(MSG_003, tmp), APPLICATION_NAME, MB_OKCANCEL|MB_ICONWARNING|MB_IS_RTL, selected_langid) == IDCANCEL) { diff --git a/src/rufus.h b/src/rufus.h index 1e97e2df..b5ed9509 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -160,6 +160,7 @@ enum notification_type { MSG_WARNING, MSG_ERROR, MSG_QUESTION, + MSG_WARNING_QUESTION }; typedef INT_PTR (CALLBACK *Callback_t)(HWND, UINT, WPARAM, LPARAM); typedef struct { @@ -493,6 +494,7 @@ extern BOOL IsBufferInDB(const unsigned char* buf, const size_t len); extern char* _printbits(size_t const size, void const * const ptr, int leading_zeroes); extern BOOL IsCurrentProcessElevated(void); extern char* GetCurrentMUI(void); +extern char* GetMuiString(char* szModuleName, UINT uID); extern BOOL SetFormatPromptHook(void); extern void ClrFormatPromptHook(void); extern BYTE SearchProcess(char* HandleName, BOOL bPartialMatch, BOOL bIgnoreSelf, BOOL bQuiet); diff --git a/src/rufus.rc b/src/rufus.rc index 7a25aa59..831b452d 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 242, 376 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_ACCEPTFILES -CAPTION "Rufus 2.16.1126" +CAPTION "Rufus 2.16.1127" FONT 8, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Device",IDS_DEVICE_TXT,9,6,200,8 @@ -366,8 +366,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,16,1126,0 - PRODUCTVERSION 2,16,1126,0 + FILEVERSION 2,16,1127,0 + PRODUCTVERSION 2,16,1127,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -384,13 +384,13 @@ BEGIN BEGIN VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "2.16.1126" + VALUE "FileVersion", "2.16.1127" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", " 2011-2017 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html" VALUE "OriginalFilename", "rufus.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "2.16.1126" + VALUE "ProductVersion", "2.16.1127" END END BLOCK "VarFileInfo" diff --git a/src/stdfn.c b/src/stdfn.c index 9ce754d2..1874574a 100644 --- a/src/stdfn.c +++ b/src/stdfn.c @@ -909,3 +909,35 @@ char* GetCurrentMUI(void) } return mui_str; } + +char* GetMuiString(char* szModuleName, UINT uID) +{ + HMODULE hModule; + char path[MAX_PATH], *str; + wchar_t* wstr; + void* ptr; + int len; + static_sprintf(path, "%s\\%s\\%s.mui", system_dir, GetCurrentMUI(), szModuleName); + // If the file doesn't exist, fall back to en-US + if (!PathFileExistsU(path)) + static_sprintf(path, "%s\\en-US\\%s.mui", system_dir, szModuleName); + hModule = LoadLibraryExA(path, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE); + if (hModule == NULL) { + uprintf("Could not load '%s': %s", path, WindowsErrorString()); + return NULL; + } + // Calling LoadStringW with last parameter 0 returns the length of the string (without NUL terminator) + len = LoadStringW(hModule, uID, (LPWSTR)(&ptr), 0); + if (len <= 0) { + if (GetLastError() == ERROR_SUCCESS) + SetLastError(ERROR_RESOURCE_NAME_NOT_FOUND); + uprintf("Could not find string ID %d in '%s': %s", uID, path, WindowsErrorString()); + return NULL; + } + len += 1; + wstr = calloc(len, sizeof(wchar_t)); + len = LoadStringW(hModule, uID, wstr, len); + str = wchar_to_utf8(wstr); + free(wstr); + return str; +} diff --git a/src/stdlg.c b/src/stdlg.c index 310e2ef9..a96f87f4 100644 --- a/src/stdlg.c +++ b/src/stdlg.c @@ -823,6 +823,9 @@ BOOL Notification(int type, const notification_info* more_info, char* title, cha notification_is_question = FALSE; switch(type) { + case MSG_WARNING_QUESTION: + notification_is_question = TRUE; + // Fall through case MSG_WARNING: hMessageIcon = LoadIcon(NULL, IDI_WARNING); break;