mirror of
https://github.com/pbatard/rufus.git
synced 2024-08-14 23:57:05 +00:00
[process] move the search for conflicting process to a background thread
* Removes the annoyance of having to wait for the process search to complete before media creation can start. * Also update the "Process Hacker" references to its new "System Informer" name.
This commit is contained in:
parent
8859c59548
commit
45a5f22d43
8 changed files with 568 additions and 357 deletions
|
@ -172,8 +172,7 @@ static HANDLE GetHandle(char* Path, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWr
|
||||||
uprintf("Warning: Could not obtain exclusive rights. Retrying with write sharing enabled...");
|
uprintf("Warning: Could not obtain exclusive rights. Retrying with write sharing enabled...");
|
||||||
bWriteShare = TRUE;
|
bWriteShare = TRUE;
|
||||||
// Try to report the process that is locking the drive
|
// Try to report the process that is locking the drive
|
||||||
// We also use bit 6 as a flag to indicate that SearchProcess was called.
|
access_mask = GetProcessSearch(SEARCH_PROCESS_TIMEOUT, 0x07, FALSE);
|
||||||
access_mask = SearchProcess(DevPath, SEARCH_PROCESS_TIMEOUT, TRUE, TRUE, FALSE) | 0x40;
|
|
||||||
}
|
}
|
||||||
Sleep(DRIVE_ACCESS_TIMEOUT / DRIVE_ACCESS_RETRIES);
|
Sleep(DRIVE_ACCESS_TIMEOUT / DRIVE_ACCESS_RETRIES);
|
||||||
}
|
}
|
||||||
|
@ -203,9 +202,7 @@ static HANDLE GetHandle(char* Path, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWr
|
||||||
uprintf("Could not lock access to %s: %s", Path, WindowsErrorString());
|
uprintf("Could not lock access to %s: %s", Path, WindowsErrorString());
|
||||||
// See if we can report the processes are accessing the drive
|
// See if we can report the processes are accessing the drive
|
||||||
if (!IS_ERROR(FormatStatus) && (access_mask == 0))
|
if (!IS_ERROR(FormatStatus) && (access_mask == 0))
|
||||||
// Double the search process timeout here, as Windows is so bloated with processes
|
access_mask = GetProcessSearch(SEARCH_PROCESS_TIMEOUT, 0x07, FALSE);
|
||||||
// that 10 seconds has become way too small to get much of any results these days...
|
|
||||||
access_mask = SearchProcess(DevPath, 2 * SEARCH_PROCESS_TIMEOUT, TRUE, TRUE, FALSE);
|
|
||||||
// Try to continue if the only access rights we saw were for read-only
|
// Try to continue if the only access rights we saw were for read-only
|
||||||
if ((access_mask & 0x07) != 0x01)
|
if ((access_mask & 0x07) != 0x01)
|
||||||
safe_closehandle(hDrive);
|
safe_closehandle(hDrive);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Rufus: The Reliable USB Formatting Utility
|
* Rufus: The Reliable USB Formatting Utility
|
||||||
* Licensing Data
|
* Licensing Data
|
||||||
* Copyright © 2011-2015 Pete Batard <pete@akeo.ie>
|
* Copyright © 2011-2023 Pete Batard <pete@akeo.ie>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -89,8 +89,8 @@ const char* additional_copyrights =
|
||||||
"https://www.codeguru.com/forum/showthread.php?p=1951973\\line\n"
|
"https://www.codeguru.com/forum/showthread.php?p=1951973\\line\n"
|
||||||
"Public Domain\\line\n"
|
"Public Domain\\line\n"
|
||||||
"\\line\n"
|
"\\line\n"
|
||||||
"Handle search & process enumeration from Process Hacker by wj32 & dmex:\\line\n"
|
"Handle search & process enumeration from System Informer by wj32 & dmex:\\line\n"
|
||||||
"https://processhacker.sourceforge.io/\\line\n"
|
"https://systeminformer.sourceforge.io/\\line\n"
|
||||||
"GNU General Public License (GPL) v3 or later\\line\n"
|
"GNU General Public License (GPL) v3 or later\\line\n"
|
||||||
"\\line\n"
|
"\\line\n"
|
||||||
"Decompression support from BusyBox/Bled:\\line\n"
|
"Decompression support from BusyBox/Bled:\\line\n"
|
||||||
|
|
733
src/process.c
733
src/process.c
|
@ -2,8 +2,8 @@
|
||||||
* Rufus: The Reliable USB Formatting Utility
|
* Rufus: The Reliable USB Formatting Utility
|
||||||
* Process search functionality
|
* Process search functionality
|
||||||
*
|
*
|
||||||
* Modified from Process Hacker:
|
* Modified from System Informer (a.k.a. Process Hacker):
|
||||||
* https://github.com/processhacker2/processhacker2/
|
* https://github.com/winsiderss/systeminformer
|
||||||
* Copyright © 2017-2023 Pete Batard <pete@akeo.ie>
|
* Copyright © 2017-2023 Pete Batard <pete@akeo.ie>
|
||||||
* Copyright © 2017 dmex
|
* Copyright © 2017 dmex
|
||||||
* Copyright © 2009-2016 wj32
|
* Copyright © 2009-2016 wj32
|
||||||
|
@ -31,6 +31,7 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include "rufus.h"
|
#include "rufus.h"
|
||||||
|
#include "drive.h"
|
||||||
#include "process.h"
|
#include "process.h"
|
||||||
#include "missing.h"
|
#include "missing.h"
|
||||||
#include "msapi_utf8.h"
|
#include "msapi_utf8.h"
|
||||||
|
@ -53,10 +54,10 @@ PF_TYPE_DECL(NTAPI, NTSTATUS, NtAdjustPrivilegesToken, (HANDLE, BOOLEAN, PTOKEN_
|
||||||
PF_TYPE_DECL(NTAPI, NTSTATUS, NtClose, (HANDLE));
|
PF_TYPE_DECL(NTAPI, NTSTATUS, NtClose, (HANDLE));
|
||||||
|
|
||||||
static PVOID PhHeapHandle = NULL;
|
static PVOID PhHeapHandle = NULL;
|
||||||
static wchar_t* _wHandleName;
|
static HANDLE hSearchProcessThread = NULL;
|
||||||
static BOOL _bPartialMatch, _bIgnoreSelf, _bQuiet;
|
static BlockingProcess blocking_process = { 0 };
|
||||||
static BYTE access_mask;
|
|
||||||
extern StrArray BlockingProcess;
|
extern StrArray BlockingProcessList;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert an NT Status to an error message
|
* Convert an NT Status to an error message
|
||||||
|
@ -110,7 +111,6 @@ char* NtStatusError(NTSTATUS Status) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static NTSTATUS PhCreateHeap(VOID)
|
static NTSTATUS PhCreateHeap(VOID)
|
||||||
{
|
{
|
||||||
NTSTATUS status = STATUS_SUCCESS;
|
NTSTATUS status = STATUS_SUCCESS;
|
||||||
|
@ -155,7 +155,6 @@ static NTSTATUS PhDestroyHeap(VOID)
|
||||||
* \param Size The number of bytes to allocate.
|
* \param Size The number of bytes to allocate.
|
||||||
*
|
*
|
||||||
* \return A pointer to the allocated block of memory.
|
* \return A pointer to the allocated block of memory.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
static PVOID PhAllocate(SIZE_T Size)
|
static PVOID PhAllocate(SIZE_T Size)
|
||||||
{
|
{
|
||||||
|
@ -173,7 +172,6 @@ static PVOID PhAllocate(SIZE_T Size)
|
||||||
* Frees a block of memory allocated with PhAllocate().
|
* Frees a block of memory allocated with PhAllocate().
|
||||||
*
|
*
|
||||||
* \param Memory A pointer to a block of memory.
|
* \param Memory A pointer to a block of memory.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
static VOID PhFree(PVOID Memory)
|
static VOID PhFree(PVOID Memory)
|
||||||
{
|
{
|
||||||
|
@ -423,10 +421,18 @@ out:
|
||||||
return wcmdline;
|
return wcmdline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The search process thread.
|
||||||
|
* Note: Avoid using uprintf statements here, as it may lock the thread.
|
||||||
|
*
|
||||||
|
* \param param The thread parameters.
|
||||||
|
*
|
||||||
|
* \return A thread exit code.
|
||||||
|
*/
|
||||||
static DWORD WINAPI SearchProcessThread(LPVOID param)
|
static DWORD WINAPI SearchProcessThread(LPVOID param)
|
||||||
{
|
{
|
||||||
const char *access_rights_str[8] = { "n", "r", "w", "rw", "x", "rx", "wx", "rwx" };
|
BOOL bInitSuccess = FALSE;
|
||||||
char tmp[MAX_PATH];
|
|
||||||
NTSTATUS status = STATUS_SUCCESS;
|
NTSTATUS status = STATUS_SUCCESS;
|
||||||
PSYSTEM_HANDLE_INFORMATION_EX handles = NULL;
|
PSYSTEM_HANDLE_INFORMATION_EX handles = NULL;
|
||||||
POBJECT_NAME_INFORMATION buffer = NULL;
|
POBJECT_NAME_INFORMATION buffer = NULL;
|
||||||
|
@ -434,276 +440,533 @@ static DWORD WINAPI SearchProcessThread(LPVOID param)
|
||||||
ULONG_PTR pid[2];
|
ULONG_PTR pid[2];
|
||||||
ULONG_PTR last_access_denied_pid = 0;
|
ULONG_PTR last_access_denied_pid = 0;
|
||||||
ULONG bufferSize;
|
ULONG bufferSize;
|
||||||
USHORT wHandleNameLen;
|
wchar_t** wHandleName = NULL;
|
||||||
|
USHORT* wHandleNameLen = NULL;
|
||||||
HANDLE dupHandle = NULL;
|
HANDLE dupHandle = NULL;
|
||||||
HANDLE processHandle = NULL;
|
HANDLE processHandle = NULL;
|
||||||
BOOLEAN bFound = FALSE, bGotCmdLine, verbose = !_bQuiet;
|
HANDLE hLock = NULL;
|
||||||
|
BOOLEAN bFound = FALSE, bGotCmdLine;
|
||||||
ULONG access_rights = 0;
|
ULONG access_rights = 0;
|
||||||
DWORD size;
|
DWORD size;
|
||||||
char cmdline[MAX_PATH] = { 0 };
|
|
||||||
wchar_t wexe_path[MAX_PATH], *wcmdline;
|
wchar_t wexe_path[MAX_PATH], *wcmdline;
|
||||||
int cur_pid;
|
uint64_t start_time;
|
||||||
|
char cmdline[MAX_PATH] = { 0 }, tmp[64];
|
||||||
|
int cur_pid, j, nHandles = 0;
|
||||||
|
|
||||||
PF_INIT_OR_SET_STATUS(NtQueryObject, Ntdll);
|
PF_INIT_OR_OUT(NtQueryObject, Ntdll);
|
||||||
PF_INIT_OR_SET_STATUS(NtDuplicateObject, NtDll);
|
PF_INIT_OR_OUT(NtDuplicateObject, NtDll);
|
||||||
PF_INIT_OR_SET_STATUS(NtClose, NtDll);
|
PF_INIT_OR_OUT(NtClose, NtDll);
|
||||||
|
|
||||||
StrArrayClear(&BlockingProcess);
|
// Initialize the blocking process struct
|
||||||
|
memset(&blocking_process, 0, sizeof(blocking_process));
|
||||||
|
hLock = CreateMutexA(NULL, TRUE, NULL);
|
||||||
|
if (hLock == NULL)
|
||||||
|
goto out;
|
||||||
|
blocking_process.hStart = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||||
|
if (blocking_process.hStart == NULL)
|
||||||
|
goto out;
|
||||||
|
if (!ReleaseMutex(hLock))
|
||||||
|
goto out;
|
||||||
|
// Only assign the mutex handle once our init is complete
|
||||||
|
blocking_process.hLock = hLock;
|
||||||
|
|
||||||
if (NT_SUCCESS(status))
|
if (!NT_SUCCESS(PhCreateHeap()))
|
||||||
status = PhCreateHeap();
|
goto out;
|
||||||
|
|
||||||
if (NT_SUCCESS(status))
|
// Wait until we are signaled active one way or another
|
||||||
status = PhEnumHandlesEx(&handles);
|
if (!blocking_process.bActive &&
|
||||||
|
(WaitForSingleObject(blocking_process.hStart, INFINITE) != WAIT_OBJECT_0)) {
|
||||||
if (!NT_SUCCESS(status)) {
|
|
||||||
uprintf("Warning: Could not enumerate process handles: %s", NtStatusError(status));
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
pid[0] = (ULONG_PTR)0;
|
bInitSuccess = TRUE;
|
||||||
cur_pid = 1;
|
while (blocking_process.bActive) {
|
||||||
|
// Get a lock to our data
|
||||||
wHandleNameLen = (USHORT)wcslen(_wHandleName);
|
if (WaitForSingleObject(hLock, SEARCH_PROCESS_LOCK_TIMEOUT) != WAIT_OBJECT_0)
|
||||||
|
goto out;
|
||||||
bufferSize = 0x200;
|
// No handles to check => just sleep for a while
|
||||||
buffer = PhAllocate(bufferSize);
|
if (blocking_process.nHandles == 0) {
|
||||||
if (buffer == NULL)
|
ReleaseMutex(hLock);
|
||||||
goto out;
|
Sleep(500);
|
||||||
|
continue;
|
||||||
for (i = 0; ; i++) {
|
|
||||||
ULONG attempts = 8;
|
|
||||||
PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handleInfo = NULL;
|
|
||||||
|
|
||||||
// We are seeing reports of application crashes due to access
|
|
||||||
// violation exceptions here, so, since this is not critical code,
|
|
||||||
// we add an exception handler to ignore them.
|
|
||||||
TRY_AND_HANDLE(
|
|
||||||
EXCEPTION_ACCESS_VIOLATION,
|
|
||||||
{ handleInfo = (i < handles->NumberOfHandles) ? &handles->Handles[i] : NULL; },
|
|
||||||
{ continue; }
|
|
||||||
);
|
|
||||||
|
|
||||||
if ((dupHandle != NULL) && (processHandle != NtCurrentProcess())) {
|
|
||||||
pfNtClose(dupHandle);
|
|
||||||
dupHandle = NULL;
|
|
||||||
}
|
}
|
||||||
|
// Work on our own copy of the handle names so we don't have to hold the
|
||||||
// Update the current handle's process PID and compare against last
|
// mutex for string comparison. Update only if the version has changed.
|
||||||
// Note: Be careful about not trying to overflow our list!
|
if (blocking_process.nVersion[0] != blocking_process.nVersion[1]) {
|
||||||
TRY_AND_HANDLE(
|
assert(blocking_process.wHandleName != NULL && blocking_process.nHandles != 0);
|
||||||
EXCEPTION_ACCESS_VIOLATION,
|
if (blocking_process.wHandleName == NULL || blocking_process.nHandles == 0) {
|
||||||
{ pid[cur_pid] = (handleInfo != NULL) ? handleInfo->UniqueProcessId : -1; },
|
ReleaseMutex(hLock);
|
||||||
{ continue; }
|
goto out;
|
||||||
);
|
|
||||||
|
|
||||||
if (pid[0] != pid[1]) {
|
|
||||||
cur_pid = (cur_pid + 1) % 2;
|
|
||||||
|
|
||||||
// If we're switching process and found a match, print it
|
|
||||||
if (bFound) {
|
|
||||||
static_sprintf (tmp, "● [%06u] %s (%s)", (uint32_t)pid[cur_pid], cmdline, access_rights_str[access_rights & 0x7]);
|
|
||||||
// tmp may contain a '%' so don't feed it as a naked format string
|
|
||||||
vuprintf("%s", tmp);
|
|
||||||
StrArrayAdd(&BlockingProcess, tmp, TRUE);
|
|
||||||
bFound = FALSE;
|
|
||||||
access_rights = 0;
|
|
||||||
}
|
}
|
||||||
|
if (wHandleName != NULL) {
|
||||||
// Close the previous handle
|
for (i = 0; i < nHandles; i++)
|
||||||
if (processHandle != NULL) {
|
free(wHandleName[i]);
|
||||||
if (processHandle != NtCurrentProcess())
|
free(wHandleName);
|
||||||
pfNtClose(processHandle);
|
|
||||||
processHandle = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
safe_free(wHandleNameLen);
|
||||||
|
nHandles = blocking_process.nHandles;
|
||||||
CHECK_FOR_USER_CANCEL;
|
wHandleName = calloc(nHandles, sizeof(wchar_t*));
|
||||||
|
if (wHandleName == NULL) {
|
||||||
// Exit loop condition
|
ReleaseMutex(hLock);
|
||||||
if (i >= handles->NumberOfHandles)
|
goto out;
|
||||||
break;
|
}
|
||||||
|
wHandleNameLen = calloc(nHandles, sizeof(USHORT));
|
||||||
if (handleInfo == NULL)
|
if (wHandleNameLen == NULL) {
|
||||||
continue;
|
ReleaseMutex(hLock);
|
||||||
|
goto out;
|
||||||
// Don't bother with processes we can't access
|
}
|
||||||
if (handleInfo->UniqueProcessId == last_access_denied_pid)
|
for (i = 0; i < nHandles; i++) {
|
||||||
continue;
|
wHandleName[i] = wcsdup(blocking_process.wHandleName[i]);
|
||||||
|
wHandleNameLen[i] = (USHORT)wcslen(blocking_process.wHandleName[i]);
|
||||||
// Filter out handles that aren't opened with Read (bit 0), Write (bit 1) or Execute (bit 5) access
|
if (wHandleName[i] == NULL) {
|
||||||
if ((handleInfo->GrantedAccess & 0x23) == 0)
|
ReleaseMutex(hLock);
|
||||||
continue;
|
goto out;
|
||||||
|
|
||||||
// Open the process to which the handle we are after belongs, if not already opened
|
|
||||||
if (pid[0] != pid[1]) {
|
|
||||||
status = PhOpenProcess(&processHandle, PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
|
|
||||||
(HANDLE)handleInfo->UniqueProcessId);
|
|
||||||
// There exists some processes we can't access
|
|
||||||
if (!NT_SUCCESS(status)) {
|
|
||||||
uuprintf("SearchProcess: Could not open process %ld: %s",
|
|
||||||
handleInfo->UniqueProcessId, NtStatusError(status));
|
|
||||||
processHandle = NULL;
|
|
||||||
if (status == STATUS_ACCESS_DENIED) {
|
|
||||||
last_access_denied_pid = handleInfo->UniqueProcessId;
|
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
blocking_process.nVersion[1] = blocking_process.nVersion[0];
|
||||||
|
blocking_process.nPass = 0;
|
||||||
|
}
|
||||||
|
ReleaseMutex(hLock);
|
||||||
|
|
||||||
|
start_time = GetTickCount64();
|
||||||
|
// Get a list of all opened handles
|
||||||
|
if (!NT_SUCCESS(PhEnumHandlesEx(&handles))) {
|
||||||
|
Sleep(1000);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now duplicate this handle onto our own process, so that we can access its properties
|
pid[0] = (ULONG_PTR)0;
|
||||||
if (processHandle == NtCurrentProcess()) {
|
cur_pid = 1;
|
||||||
if (_bIgnoreSelf)
|
bufferSize = 0x200;
|
||||||
|
buffer = PhAllocate(bufferSize);
|
||||||
|
if (buffer == NULL)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
for (i = 0; blocking_process.bActive; i++) {
|
||||||
|
ULONG attempts = 8;
|
||||||
|
PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handleInfo = NULL;
|
||||||
|
|
||||||
|
// We are seeing reports of application crashes due to access
|
||||||
|
// violation exceptions here, so, since this is not critical code,
|
||||||
|
// we add an exception handler to ignore them.
|
||||||
|
TRY_AND_HANDLE(
|
||||||
|
EXCEPTION_ACCESS_VIOLATION,
|
||||||
|
{ handleInfo = (i < handles->NumberOfHandles) ? &handles->Handles[i] : NULL; },
|
||||||
|
{ continue; }
|
||||||
|
);
|
||||||
|
|
||||||
|
if ((dupHandle != NULL) && (processHandle != NtCurrentProcess())) {
|
||||||
|
pfNtClose(dupHandle);
|
||||||
|
dupHandle = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the current handle's process PID and compare against last
|
||||||
|
// Note: Be careful about not trying to overflow our list!
|
||||||
|
TRY_AND_HANDLE(
|
||||||
|
EXCEPTION_ACCESS_VIOLATION,
|
||||||
|
{ pid[cur_pid] = (handleInfo != NULL) ? handleInfo->UniqueProcessId : -1; },
|
||||||
|
{ continue; }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pid[0] != pid[1]) {
|
||||||
|
cur_pid = (cur_pid + 1) % 2;
|
||||||
|
|
||||||
|
// If we're switching process and found a match, store it
|
||||||
|
if (bFound) {
|
||||||
|
if (WaitForSingleObject(hLock, SEARCH_PROCESS_LOCK_TIMEOUT) == WAIT_OBJECT_0) {
|
||||||
|
ProcessEntry* pe = blocking_process.Process;
|
||||||
|
// Prune entries that have not been detected for a few passes
|
||||||
|
for (j = 0; j < MAX_BLOCKING_PROCESSES; j++)
|
||||||
|
if (pe[j].pid != 0 && pe[j].seen_on_pass < blocking_process.nPass - 1)
|
||||||
|
pe[j].pid = 0;
|
||||||
|
// Try to reuse an existing entry for the current pid
|
||||||
|
for (j = 0; (j < MAX_BLOCKING_PROCESSES) && (pe[j].pid != pid[cur_pid]); j++);
|
||||||
|
if (j == MAX_BLOCKING_PROCESSES)
|
||||||
|
for (j = 0; (j < MAX_BLOCKING_PROCESSES) && (pe[j].pid != 0); j++);
|
||||||
|
if (j != MAX_BLOCKING_PROCESSES) {
|
||||||
|
pe[j].pid = pid[cur_pid];
|
||||||
|
pe[j].access_rights = access_rights & 0x7;
|
||||||
|
pe[j].seen_on_pass = blocking_process.nPass;
|
||||||
|
static_strcpy(pe[j].cmdline, cmdline);
|
||||||
|
} else if (usb_debug) {
|
||||||
|
OutputDebugStringA("SearchProcessThread: No empty slot!\n");
|
||||||
|
}
|
||||||
|
ReleaseMutex(hLock);
|
||||||
|
}
|
||||||
|
bFound = FALSE;
|
||||||
|
access_rights = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the previous handle
|
||||||
|
if (processHandle != NULL) {
|
||||||
|
if (processHandle != NtCurrentProcess())
|
||||||
|
pfNtClose(processHandle);
|
||||||
|
processHandle = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit thread condition
|
||||||
|
if (!blocking_process.bActive)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
// Exit loop condition
|
||||||
|
if (i >= handles->NumberOfHandles)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (handleInfo == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Don't bother with processes we can't access
|
||||||
|
if (handleInfo->UniqueProcessId == last_access_denied_pid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Filter out handles that aren't opened with Read (bit 0), Write (bit 1) or Execute (bit 5) access
|
||||||
|
if ((handleInfo->GrantedAccess & 0x23) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Open the process to which the handle we are after belongs, if not already opened
|
||||||
|
if (pid[0] != pid[1]) {
|
||||||
|
status = PhOpenProcess(&processHandle, PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
|
||||||
|
(HANDLE)handleInfo->UniqueProcessId);
|
||||||
|
// There exists some processes we can't access
|
||||||
|
if (!NT_SUCCESS(status)) {
|
||||||
|
processHandle = NULL;
|
||||||
|
if (status == STATUS_ACCESS_DENIED) {
|
||||||
|
last_access_denied_pid = handleInfo->UniqueProcessId;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now duplicate this handle onto our own process, so that we can access its properties
|
||||||
|
if (processHandle == NtCurrentProcess())
|
||||||
continue;
|
continue;
|
||||||
dupHandle = (HANDLE)handleInfo->HandleValue;
|
|
||||||
} else {
|
|
||||||
status = pfNtDuplicateObject(processHandle, (HANDLE)handleInfo->HandleValue,
|
status = pfNtDuplicateObject(processHandle, (HANDLE)handleInfo->HandleValue,
|
||||||
NtCurrentProcess(), &dupHandle, 0, 0, 0);
|
NtCurrentProcess(), &dupHandle, 0, 0, 0);
|
||||||
if (!NT_SUCCESS(status))
|
if (!NT_SUCCESS(status))
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
// Filter non-storage handles. We're not interested in them and they make NtQueryObject() freeze
|
// Filter non-storage handles. We're not interested in them and they make NtQueryObject() freeze
|
||||||
if (GetFileType(dupHandle) != FILE_TYPE_DISK)
|
if (GetFileType(dupHandle) != FILE_TYPE_DISK)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// A loop is needed because the I/O subsystem likes to give us the wrong return lengths...
|
// A loop is needed because the I/O subsystem likes to give us the wrong return lengths...
|
||||||
do {
|
do {
|
||||||
ULONG returnSize;
|
ULONG returnSize;
|
||||||
// TODO: We might potentially still need a timeout on ObjectName queries, as PH does...
|
// TODO: We might potentially still need a timeout on ObjectName queries, as PH does...
|
||||||
status = pfNtQueryObject(dupHandle, ObjectNameInformation, buffer, bufferSize, &returnSize);
|
status = pfNtQueryObject(dupHandle, ObjectNameInformation, buffer, bufferSize, &returnSize);
|
||||||
if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_INFO_LENGTH_MISMATCH ||
|
if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_INFO_LENGTH_MISMATCH ||
|
||||||
status == STATUS_BUFFER_TOO_SMALL) {
|
status == STATUS_BUFFER_TOO_SMALL) {
|
||||||
uuprintf("SearchProcess: Realloc from %d to %d", bufferSize, returnSize);
|
bufferSize = returnSize;
|
||||||
bufferSize = returnSize;
|
PhFree(buffer);
|
||||||
PhFree(buffer);
|
buffer = PhAllocate(bufferSize);
|
||||||
buffer = PhAllocate(bufferSize);
|
} else {
|
||||||
} else {
|
break;
|
||||||
break;
|
}
|
||||||
|
} while (--attempts);
|
||||||
|
if (!NT_SUCCESS(status))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (j = 0; j < nHandles; j++) {
|
||||||
|
// Don't bother comparing if length of our handle string is larger than the current data
|
||||||
|
if (wHandleNameLen[j] > buffer->Name.Length)
|
||||||
|
continue;
|
||||||
|
// Match against our target string(s)
|
||||||
|
if (wcsncmp(wHandleName[j], buffer->Name.Buffer, wHandleNameLen[j]) == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (j == nHandles)
|
||||||
|
continue;
|
||||||
|
bFound = TRUE;
|
||||||
|
|
||||||
|
// Keep a mask of all the access rights being used
|
||||||
|
access_rights |= handleInfo->GrantedAccess;
|
||||||
|
// The Executable bit is in a place we don't like => reposition it
|
||||||
|
if (access_rights & 0x20)
|
||||||
|
access_rights = (access_rights & 0x03) | 0x04;
|
||||||
|
access_rights &= 0x07;
|
||||||
|
|
||||||
|
// Where possible, try to get the full command line
|
||||||
|
bGotCmdLine = FALSE;
|
||||||
|
size = MAX_PATH;
|
||||||
|
wcmdline = GetProcessCommandLine(processHandle);
|
||||||
|
if (wcmdline != NULL) {
|
||||||
|
bGotCmdLine = TRUE;
|
||||||
|
wchar_to_utf8_no_alloc(wcmdline, cmdline, sizeof(cmdline));
|
||||||
|
free(wcmdline);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we couldn't get the full commandline, try to get the executable path
|
||||||
|
if (!bGotCmdLine)
|
||||||
|
bGotCmdLine = (GetModuleFileNameExU(processHandle, 0, cmdline, MAX_PATH - 1) != 0);
|
||||||
|
|
||||||
|
// The above may not work on all Windows version, so fall back to QueryFullProcessImageName
|
||||||
|
if (!bGotCmdLine) {
|
||||||
|
bGotCmdLine = (QueryFullProcessImageNameW(processHandle, 0, wexe_path, &size) != FALSE);
|
||||||
|
if (bGotCmdLine)
|
||||||
|
wchar_to_utf8_no_alloc(wexe_path, cmdline, sizeof(cmdline));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Still nothing? Try GetProcessImageFileName. Note that GetProcessImageFileName uses
|
||||||
|
// '\Device\Harddisk#\Partition#\' instead drive letters
|
||||||
|
if (!bGotCmdLine) {
|
||||||
|
bGotCmdLine = (GetProcessImageFileNameW(processHandle, wexe_path, MAX_PATH) != 0);
|
||||||
|
if (bGotCmdLine)
|
||||||
|
wchar_to_utf8_no_alloc(wexe_path, cmdline, sizeof(cmdline));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complete failure => Just craft a default process name that includes the PID
|
||||||
|
if (!bGotCmdLine) {
|
||||||
|
static_sprintf(cmdline, "Unknown_Process_%" PRIu64,
|
||||||
|
(ULONGLONG)handleInfo->UniqueProcessId);
|
||||||
}
|
}
|
||||||
} while (--attempts);
|
|
||||||
if (!NT_SUCCESS(status)) {
|
|
||||||
uuprintf("SearchProcess: NtQueryObject failed for handle %X of process %ld: %s",
|
|
||||||
handleInfo->HandleValue, handleInfo->UniqueProcessId, NtStatusError(status));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't bother comparing if we are looking for full match and the length is different
|
|
||||||
if ((!_bPartialMatch) && (wHandleNameLen != buffer->Name.Length))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Likewise, if we are looking for a partial match and the current length is smaller
|
|
||||||
if ((_bPartialMatch) && (wHandleNameLen > buffer->Name.Length))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Match against our target string
|
|
||||||
if (wcsncmp(_wHandleName, buffer->Name.Buffer, wHandleNameLen) != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// If we are here, we have a process accessing our target!
|
|
||||||
bFound = TRUE;
|
|
||||||
|
|
||||||
// Keep a mask of all the access rights being used
|
|
||||||
access_rights |= handleInfo->GrantedAccess;
|
|
||||||
// The Executable bit is in a place we don't like => reposition it
|
|
||||||
if (access_rights & 0x20)
|
|
||||||
access_rights = (access_rights & 0x03) | 0x04;
|
|
||||||
access_mask |= (BYTE) (access_rights & 0x7) + 0x80; // Bit 7 is always set if a process was found
|
|
||||||
|
|
||||||
// If this is the very first process we find, print a header
|
|
||||||
if (cmdline[0] == 0)
|
|
||||||
vuprintf("WARNING: The following process(es) or service(s) are accessing %S:", _wHandleName);
|
|
||||||
|
|
||||||
// Where possible, try to get the full command line
|
|
||||||
bGotCmdLine = FALSE;
|
|
||||||
size = MAX_PATH;
|
|
||||||
wcmdline = GetProcessCommandLine(processHandle);
|
|
||||||
if (wcmdline != NULL) {
|
|
||||||
bGotCmdLine = TRUE;
|
|
||||||
wchar_to_utf8_no_alloc(wcmdline, cmdline, sizeof(cmdline));
|
|
||||||
free(wcmdline);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we couldn't get the full commandline, try to get the executable path
|
|
||||||
if (!bGotCmdLine)
|
|
||||||
bGotCmdLine = (GetModuleFileNameExU(processHandle, 0, cmdline, MAX_PATH - 1) != 0);
|
|
||||||
|
|
||||||
// The above may not work on all Windows version, so fall back to QueryFullProcessImageName
|
|
||||||
if (!bGotCmdLine) {
|
|
||||||
bGotCmdLine = (QueryFullProcessImageNameW(processHandle, 0, wexe_path, &size) != FALSE);
|
|
||||||
if (bGotCmdLine)
|
|
||||||
wchar_to_utf8_no_alloc(wexe_path, cmdline, sizeof(cmdline));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Still nothing? Try GetProcessImageFileName. Note that GetProcessImageFileName uses
|
|
||||||
// '\Device\Harddisk#\Partition#\' instead drive letters
|
|
||||||
if (!bGotCmdLine) {
|
|
||||||
bGotCmdLine = (GetProcessImageFileNameW(processHandle, wexe_path, MAX_PATH) != 0);
|
|
||||||
if (bGotCmdLine)
|
|
||||||
wchar_to_utf8_no_alloc(wexe_path, cmdline, sizeof(cmdline));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Complete failure => Just craft a default process name that includes the PID
|
|
||||||
if (!bGotCmdLine) {
|
|
||||||
static_sprintf(cmdline, "Unknown_Process_%" PRIu64,
|
|
||||||
(ULONGLONG)handleInfo->UniqueProcessId);
|
|
||||||
}
|
}
|
||||||
|
PhFree(buffer);
|
||||||
|
PhFree(handles);
|
||||||
|
// We are the only ones updating the counter so no need for lock
|
||||||
|
blocking_process.nPass++;
|
||||||
|
// In extended debug mode, notify how much time our search took to the debug facility
|
||||||
|
static_sprintf(tmp, "Process search run #%d completed in %llu ms\n",
|
||||||
|
blocking_process.nPass, GetTickCount64() - start_time);
|
||||||
|
if (usb_debug)
|
||||||
|
OutputDebugStringA(tmp);
|
||||||
|
Sleep(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (cmdline[0] != 0)
|
if (!bInitSuccess)
|
||||||
vuprintf("You should close these applications before attempting to reformat the drive.");
|
uprintf("Warning: Could not start process handle enumerator!");
|
||||||
else
|
|
||||||
vuprintf("NOTE: Could not identify the process(es) or service(s) accessing %S", _wHandleName);
|
if (wHandleName != NULL) {
|
||||||
|
for (i = 0; i < nHandles; i++)
|
||||||
|
free(wHandleName[i]);
|
||||||
|
free(wHandleName);
|
||||||
|
}
|
||||||
|
safe_free(wHandleNameLen);
|
||||||
|
|
||||||
PhFree(buffer);
|
|
||||||
PhFree(handles);
|
|
||||||
PhDestroyHeap();
|
PhDestroyHeap();
|
||||||
|
if ((hLock != NULL) && (hLock != INVALID_HANDLE_VALUE) &&
|
||||||
|
(WaitForSingleObject(hLock, 1000) == WAIT_OBJECT_0)) {
|
||||||
|
blocking_process.hLock = NULL;
|
||||||
|
blocking_process.bActive = FALSE;
|
||||||
|
for (i = 0; i < blocking_process.nHandles; i++)
|
||||||
|
free(blocking_process.wHandleName[i]);
|
||||||
|
safe_free(blocking_process.wHandleName);
|
||||||
|
safe_closehandle(blocking_process.hStart);
|
||||||
|
ReleaseMutex(hLock);
|
||||||
|
}
|
||||||
|
safe_closehandle(hLock);
|
||||||
|
|
||||||
ExitThread(0);
|
ExitThread(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search all the processes and list the ones that have a specific handle open.
|
* Start the process search thread.
|
||||||
*
|
*
|
||||||
* \param HandleName The name of the handle to look for.
|
* \return TRUE on success, FALSE otherwise.
|
||||||
* \param dwTimeOut The maximum amounf of time (ms) that may be spent searching
|
|
||||||
* \param bPartialMatch Whether partial matches should be allowed.
|
|
||||||
* \param bIgnoreSelf Whether the current process should be listed.
|
|
||||||
* \param bQuiet Prints minimal output.
|
|
||||||
*
|
*
|
||||||
* \return a byte containing the cumulated access rights (f----xwr) from all the handles found
|
|
||||||
* with bit 7 ('f') also set if at least one process was found.
|
|
||||||
*/
|
*/
|
||||||
BYTE SearchProcess(char* HandleName, DWORD dwTimeOut, BOOL bPartialMatch, BOOL bIgnoreSelf, BOOL bQuiet)
|
BOOL StartProcessSearch(void)
|
||||||
{
|
{
|
||||||
HANDLE handle;
|
int i;
|
||||||
DWORD res = 0;
|
|
||||||
|
|
||||||
_wHandleName = utf8_to_wchar(HandleName);
|
if (hSearchProcessThread != NULL)
|
||||||
_bPartialMatch = bPartialMatch;
|
return TRUE;
|
||||||
_bIgnoreSelf = bIgnoreSelf;
|
|
||||||
_bQuiet = bQuiet;
|
|
||||||
access_mask = 0x00;
|
|
||||||
|
|
||||||
assert(_wHandleName != NULL);
|
hSearchProcessThread = CreateThread(NULL, 0, SearchProcessThread, NULL, 0, NULL);
|
||||||
|
if (hSearchProcessThread == NULL) {
|
||||||
handle = CreateThread(NULL, 0, SearchProcessThread, NULL, 0, NULL);
|
uprintf("Failed to start process search thread: %s", WindowsErrorString());
|
||||||
if (handle == NULL) {
|
return FALSE;
|
||||||
uprintf("Warning: Unable to create conflicting process search thread");
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
res = WaitForSingleObjectWithMessages(handle, dwTimeOut);
|
SetThreadPriority(SearchProcessThread, THREAD_PRIORITY_LOWEST);
|
||||||
if (res == WAIT_TIMEOUT) {
|
|
||||||
// Timeout - kill the thread
|
// Wait until we have hLock
|
||||||
TerminateThread(handle, 0);
|
for (i = 0; (i < 50) && (blocking_process.hLock == NULL); i++)
|
||||||
uprintf("Search for conflicting processes was interrupted due to timeout");
|
Sleep(100);
|
||||||
} else if (res != WAIT_OBJECT_0) {
|
if (i >= 50) {
|
||||||
TerminateThread(handle, 0);
|
uprintf("Failed to start process search thread: hLock init failure!");
|
||||||
uprintf("Warning: Failed to wait for conflicting process search thread %s", WindowsErrorString());
|
TerminateThread(hSearchProcessThread, 0);
|
||||||
|
CloseHandle(hSearchProcessThread);
|
||||||
|
hSearchProcessThread = NULL;
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the process search thread..
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void StopProcessSearch(void)
|
||||||
|
{
|
||||||
|
if (hSearchProcessThread == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// No need for a lock on this one
|
||||||
|
blocking_process.bActive = FALSE;
|
||||||
|
if (WaitForSingleObject(hSearchProcessThread, SEARCH_PROCESS_LOCK_TIMEOUT) != WAIT_OBJECT_0) {
|
||||||
|
uprintf("Process search thread did not exit within timeout - forcefully terminating it!");
|
||||||
|
TerminateThread(hSearchProcessThread, 0);
|
||||||
|
CloseHandle(hSearchProcessThread);
|
||||||
|
}
|
||||||
|
hSearchProcessThread = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up the handles that the process search will run against.
|
||||||
|
*
|
||||||
|
* \param DeviceNum The device number for the currently selected drive.
|
||||||
|
*
|
||||||
|
* \return TRUE on success, FALSE otherwise.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
BOOL SetProcessSearch(DWORD DeviceNum)
|
||||||
|
{
|
||||||
|
char* PhysicalPath = NULL, DevPath[MAX_PATH];
|
||||||
|
char drive_letter[27], drive_name[] = "?:";
|
||||||
|
uint32_t i, nHandles = 0;
|
||||||
|
wchar_t** wHandleName = NULL;
|
||||||
|
|
||||||
|
if (hSearchProcessThread == NULL) {
|
||||||
|
uprintf("Process search thread is not started!");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(blocking_process.hLock != NULL);
|
||||||
|
|
||||||
|
// Populate the handle names
|
||||||
|
wHandleName = calloc(MAX_NUM_HANDLES, sizeof(wchar_t*));
|
||||||
|
if (wHandleName == NULL)
|
||||||
|
return FALSE;
|
||||||
|
// Physical drive handle name
|
||||||
|
PhysicalPath = GetPhysicalName(DeviceNum);
|
||||||
|
if (QueryDosDeviceA(&PhysicalPath[4], DevPath, sizeof(DevPath)) != 0)
|
||||||
|
wHandleName[nHandles++] = utf8_to_wchar(DevPath);
|
||||||
|
free(PhysicalPath);
|
||||||
|
// Logical drive(s) handle name(s)
|
||||||
|
GetDriveLetters(DeviceNum, drive_letter);
|
||||||
|
for (i = 0; nHandles < MAX_NUM_HANDLES && drive_letter[i]; i++) {
|
||||||
|
drive_name[0] = drive_letter[i];
|
||||||
|
if (QueryDosDeviceA(drive_name, DevPath, sizeof(DevPath)) != 0)
|
||||||
|
wHandleName[nHandles++] = utf8_to_wchar(DevPath);
|
||||||
|
}
|
||||||
|
if (WaitForSingleObject(blocking_process.hLock, SEARCH_PROCESS_LOCK_TIMEOUT) != WAIT_OBJECT_0) {
|
||||||
|
uprintf("Could not obtain process search lock");
|
||||||
|
free(wHandleName);
|
||||||
|
nHandles = 0;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blocking_process.wHandleName != NULL)
|
||||||
|
for (i = 0; i < blocking_process.nHandles; i++)
|
||||||
|
free(blocking_process.wHandleName[i]);
|
||||||
|
free(blocking_process.wHandleName);
|
||||||
|
blocking_process.wHandleName = wHandleName;
|
||||||
|
blocking_process.nHandles = nHandles;
|
||||||
|
blocking_process.nVersion[0]++;
|
||||||
|
blocking_process.bActive = TRUE;
|
||||||
|
if (!SetEvent(blocking_process.hStart))
|
||||||
|
uprintf("Could not send start event to process search: %s", WindowsErrorString);
|
||||||
|
return ReleaseMutex(blocking_process.hLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the corresponding PID is that of a running process.
|
||||||
|
*
|
||||||
|
* \param pid The PID of the process to check.
|
||||||
|
*
|
||||||
|
* \return TRUE if the process is detected as currently running, FALSE otherwise.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static BOOL IsProcessRunning(uint64_t pid)
|
||||||
|
{
|
||||||
|
HANDLE hProcess = NULL;
|
||||||
|
DWORD dwExitCode;
|
||||||
|
BOOL ret = FALSE;
|
||||||
|
NTSTATUS status;
|
||||||
|
|
||||||
|
PF_INIT_OR_OUT(NtClose, NtDll);
|
||||||
|
|
||||||
|
status = PhOpenProcess(&hProcess, PROCESS_QUERY_LIMITED_INFORMATION, (HANDLE)pid);
|
||||||
|
if (!NT_SUCCESS(status) || (hProcess == NULL))
|
||||||
|
return FALSE;
|
||||||
|
if (GetExitCodeProcess(hProcess, &dwExitCode))
|
||||||
|
ret = (dwExitCode == STILL_ACTIVE);
|
||||||
|
pfNtClose(hProcess);
|
||||||
out:
|
out:
|
||||||
free(_wHandleName);
|
return ret;
|
||||||
return access_mask;
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report the result of the process search.
|
||||||
|
*
|
||||||
|
* \param timeout Maximum time that should be spend in this function before aborting (in ms).
|
||||||
|
* \param access_mask Desired access mask (x = 0x4, w = 0x2, r = 0x1).
|
||||||
|
* \param bIgnoreStaleProcesses Whether to ignore processes that are no longer active.
|
||||||
|
*
|
||||||
|
* \return The combined access mask of all the matching processes found.
|
||||||
|
* The BlockingProcessList string array is also updated with the results.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
BYTE GetProcessSearch(uint32_t timeout, uint8_t access_mask, BOOL bIgnoreStaleProcesses)
|
||||||
|
{
|
||||||
|
const char* access_rights_str[8] = { "n", "r", "w", "rw", "x", "rx", "wx", "rwx" };
|
||||||
|
char tmp[MAX_PATH];
|
||||||
|
int i, j;
|
||||||
|
uint32_t elapsed = 0;
|
||||||
|
BYTE returned_mask = 0;
|
||||||
|
|
||||||
|
StrArrayClear(&BlockingProcessList);
|
||||||
|
if (hSearchProcessThread == NULL) {
|
||||||
|
uprintf("Process search thread is not started!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(blocking_process.hLock != NULL);
|
||||||
|
if (blocking_process.hLock == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
retry:
|
||||||
|
if (WaitForSingleObject(blocking_process.hLock, SEARCH_PROCESS_LOCK_TIMEOUT) != WAIT_OBJECT_0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Make sure we have at least one pass with the current handles in order to report them.
|
||||||
|
// If we have a timeout, wait until timeout has elapsed to give up.
|
||||||
|
if ((blocking_process.nVersion[0] != blocking_process.nVersion[1]) ||
|
||||||
|
(blocking_process.nPass < 1)) {
|
||||||
|
ReleaseMutex(blocking_process.hLock);
|
||||||
|
if (elapsed < timeout) {
|
||||||
|
Sleep(100);
|
||||||
|
elapsed += 100;
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
if (timeout != 0)
|
||||||
|
uprintf("Timeout while retrieving conflicting process list");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0, j = 0; i < MAX_BLOCKING_PROCESSES; i++) {
|
||||||
|
if (blocking_process.Process[i].pid == 0)
|
||||||
|
continue;
|
||||||
|
if ((blocking_process.Process[i].access_rights & access_mask) == 0)
|
||||||
|
continue;
|
||||||
|
if (bIgnoreStaleProcesses && !IsProcessRunning(blocking_process.Process[i].pid))
|
||||||
|
continue;
|
||||||
|
returned_mask |= blocking_process.Process[i].access_rights;
|
||||||
|
static_sprintf(tmp, "● [%llu] %s (%s)", blocking_process.Process[i].pid, blocking_process.Process[i].cmdline,
|
||||||
|
access_rights_str[blocking_process.Process[i].access_rights & 0x7]);
|
||||||
|
StrArrayAdd(&BlockingProcessList, tmp, TRUE);
|
||||||
|
if (j++ == 0)
|
||||||
|
uprintf("WARNING: The following application(s) or service(s) are accessing the drive:");
|
||||||
|
// tmp may contain a '%' so don't feed it as a naked format string
|
||||||
|
uprintf("%s", tmp);
|
||||||
|
}
|
||||||
|
if (j != 0)
|
||||||
|
uprintf("You should close these applications before retrying the operation.");
|
||||||
|
ReleaseMutex(blocking_process.hLock);
|
||||||
|
|
||||||
|
return returned_mask & access_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
* Rufus: The Reliable USB Formatting Utility
|
* Rufus: The Reliable USB Formatting Utility
|
||||||
* Process search functionality
|
* Process search functionality
|
||||||
*
|
*
|
||||||
* Modified from Process Hacker:
|
* Modified from System Informer (a.k.a. Process Hacker):
|
||||||
* https://github.com/processhacker2/processhacker2/
|
* https://github.com/winsiderss/systeminformer
|
||||||
* Copyright © 2017-2019 Pete Batard <pete@akeo.ie>
|
* Copyright © 2017-2023 Pete Batard <pete@akeo.ie>
|
||||||
* Copyright © 2017 dmex
|
* Copyright © 2017 dmex
|
||||||
* Copyright © 2009-2016 wj32
|
* Copyright © 2009-2016 wj32
|
||||||
*
|
*
|
||||||
|
@ -25,6 +25,7 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <winnt.h>
|
#include <winnt.h>
|
||||||
#include <winternl.h>
|
#include <winternl.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
@ -294,3 +295,25 @@ typedef struct _RTL_HEAP_PARAMETERS
|
||||||
#define SE_TIME_ZONE_PRIVILEGE (34L)
|
#define SE_TIME_ZONE_PRIVILEGE (34L)
|
||||||
#define SE_CREATE_SYMBOLIC_LINK_PRIVILEGE (35L)
|
#define SE_CREATE_SYMBOLIC_LINK_PRIVILEGE (35L)
|
||||||
#define SE_MAX_WELL_KNOWN_PRIVILEGE SE_CREATE_SYMBOLIC_LINK_PRIVILEGE
|
#define SE_MAX_WELL_KNOWN_PRIVILEGE SE_CREATE_SYMBOLIC_LINK_PRIVILEGE
|
||||||
|
|
||||||
|
#define MAX_NUM_HANDLES 16
|
||||||
|
#define MAX_BLOCKING_PROCESSES 16
|
||||||
|
#define SEARCH_PROCESS_LOCK_TIMEOUT 2000
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t pid; // PID of the process
|
||||||
|
uint8_t access_rights; // rwx access rights
|
||||||
|
uint32_t seen_on_pass; // nPass value of when this process was last detected
|
||||||
|
char cmdline[MAX_PATH]; // Command line for the process
|
||||||
|
} ProcessEntry;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
BOOL bActive; // Indicates whether the search for processes is currently active
|
||||||
|
uint32_t nVersion[2]; // Version indicator for the handle name list.
|
||||||
|
uint32_t nHandles; // Number of handle names in the list below
|
||||||
|
wchar_t** wHandleName; // Handle names we search against
|
||||||
|
HANDLE hLock; // Global lock to request for modifying this structure
|
||||||
|
HANDLE hStart; // Event indicating that the search for processes can be started
|
||||||
|
ProcessEntry Process[MAX_BLOCKING_PROCESSES]; // Fixed size process list
|
||||||
|
uint32_t nPass; // Incremental counter of how many passes we ran
|
||||||
|
} BlockingProcess;
|
||||||
|
|
126
src/rufus.c
126
src/rufus.c
|
@ -141,7 +141,7 @@ char embedded_sl_version_ext[2][32];
|
||||||
char ClusterSizeLabel[MAX_CLUSTER_SIZES][64];
|
char ClusterSizeLabel[MAX_CLUSTER_SIZES][64];
|
||||||
char msgbox[1024], msgbox_title[32], *ini_file = NULL, *image_path = NULL, *short_image_path;
|
char msgbox[1024], msgbox_title[32], *ini_file = NULL, *image_path = NULL, *short_image_path;
|
||||||
char *archive_path = NULL, image_option_txt[128], *fido_url = NULL, *save_image_type = NULL;
|
char *archive_path = NULL, image_option_txt[128], *fido_url = NULL, *save_image_type = NULL;
|
||||||
StrArray BlockingProcess, ImageList;
|
StrArray BlockingProcessList, ImageList;
|
||||||
// Number of steps for each FS for FCC_STRUCTURE_PROGRESS
|
// Number of steps for each FS for FCC_STRUCTURE_PROGRESS
|
||||||
const int nb_steps[FS_MAX] = { 5, 5, 12, 1, 10, 1, 1, 1, 1 };
|
const int nb_steps[FS_MAX] = { 5, 5, 12, 1, 10, 1, 1, 1, 1 };
|
||||||
const char* flash_type[BADLOCKS_PATTERN_TYPES] = { "SLC", "MLC", "TLC" };
|
const char* flash_type[BADLOCKS_PATTERN_TYPES] = { "SLC", "MLC", "TLC" };
|
||||||
|
@ -2105,7 +2105,7 @@ static void InitDialog(HWND hDlg)
|
||||||
IGNORE_RETVAL(ComboBox_SetCurSel(hDiskID, 0));
|
IGNORE_RETVAL(ComboBox_SetCurSel(hDiskID, 0));
|
||||||
|
|
||||||
// Create the string arrays
|
// Create the string arrays
|
||||||
StrArrayCreate(&BlockingProcess, 16);
|
StrArrayCreate(&BlockingProcessList, 16);
|
||||||
StrArrayCreate(&ImageList, 16);
|
StrArrayCreate(&ImageList, 16);
|
||||||
// Set various checkboxes
|
// Set various checkboxes
|
||||||
CheckDlgButton(hDlg, IDC_QUICK_FORMAT, BST_CHECKED);
|
CheckDlgButton(hDlg, IDC_QUICK_FORMAT, BST_CHECKED);
|
||||||
|
@ -2169,85 +2169,6 @@ static void PrintStatusTimeout(const char* str, BOOL val)
|
||||||
PrintStatus(STATUS_MSG_TIMEOUT, (val)?MSG_250:MSG_251, str);
|
PrintStatus(STATUS_MSG_TIMEOUT, (val)?MSG_250:MSG_251, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for conflicting processes accessing the drive.
|
|
||||||
// If bPrompt is true, ask the user whether they want to proceed.
|
|
||||||
// dwTimeOut is the maximum amount of time we allow for this call to execute (in ms)
|
|
||||||
// If bPrompt is false, the return value is the amount of time remaining before
|
|
||||||
// dwTimeOut would expire (or zero if we spent more than dwTimeout in this procedure).
|
|
||||||
// If bPrompt is true, the return value is 0 on error, dwTimeOut on success.
|
|
||||||
DWORD CheckDriveAccess(DWORD dwTimeOut, BOOL bPrompt)
|
|
||||||
{
|
|
||||||
uint32_t i, j;
|
|
||||||
DWORD ret = 0, proceed = TRUE;
|
|
||||||
BYTE access_mask;
|
|
||||||
char *PhysicalPath = NULL, DevPath[MAX_PATH];
|
|
||||||
char drive_letter[27], drive_name[] = "?:";
|
|
||||||
char title[128];
|
|
||||||
uint64_t start_time = GetTickCount64(), cur_time, end_time = start_time + dwTimeOut;
|
|
||||||
|
|
||||||
// Get the current selected device
|
|
||||||
DWORD DeviceNum = (DWORD)ComboBox_GetCurItemData(hDeviceList);
|
|
||||||
if ((DeviceNum < 0x80) || (DeviceNum == (DWORD)-1))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
// "Checking for conflicting processes..."
|
|
||||||
if (bPrompt)
|
|
||||||
PrintInfo(0, MSG_278);
|
|
||||||
|
|
||||||
// Search for any blocking processes against the physical drive
|
|
||||||
PhysicalPath = GetPhysicalName(DeviceNum);
|
|
||||||
if (QueryDosDeviceA(&PhysicalPath[4], DevPath, sizeof(DevPath)) != 0) {
|
|
||||||
access_mask = SearchProcess(DevPath, dwTimeOut, TRUE, TRUE, TRUE);
|
|
||||||
CHECK_FOR_USER_CANCEL;
|
|
||||||
if (access_mask != 0) {
|
|
||||||
proceed = FALSE;
|
|
||||||
uprintf("Found potentially blocking process(es) against %s:", &PhysicalPath[4]);
|
|
||||||
for (j = 0; j < BlockingProcess.Index; j++)
|
|
||||||
// BlockingProcess.String[j] may contain a '%' so don't feed it as a naked format string
|
|
||||||
uprintf("%s", BlockingProcess.String[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
cur_time = GetTickCount64();
|
|
||||||
if (cur_time >= end_time)
|
|
||||||
break;
|
|
||||||
access_mask = SearchProcess(DevPath, (DWORD)(end_time - cur_time), TRUE, TRUE, TRUE);
|
|
||||||
CHECK_FOR_USER_CANCEL;
|
|
||||||
// Ignore if all we have is read-only
|
|
||||||
if ((access_mask & 0x06) || (access_mask == 0x80)) {
|
|
||||||
proceed = FALSE;
|
|
||||||
uprintf("Found potentially blocking process(es) against %s", drive_name);
|
|
||||||
for (j = 0; j < BlockingProcess.Index; j++)
|
|
||||||
// BlockingProcess.String[j] may contain a '%' so don't feed it as a naked format string
|
|
||||||
uprintf("%s", BlockingProcess.String[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prompt the user if we detected blocking processes
|
|
||||||
if (bPrompt && !proceed) {
|
|
||||||
ComboBox_GetTextU(hDeviceList, title, sizeof(title));
|
|
||||||
proceed = Notification(MSG_WARNING_QUESTION, NULL, NULL, title, lmprintf(MSG_132));
|
|
||||||
}
|
|
||||||
if (bPrompt) {
|
|
||||||
ret = proceed ? dwTimeOut : 0;
|
|
||||||
} else {
|
|
||||||
ret = (DWORD)(GetTickCount64() - start_time);
|
|
||||||
ret = (dwTimeOut > ret) ? (dwTimeOut - ret) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
PrintInfo(0, MSG_210);
|
|
||||||
free(PhysicalPath);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Main dialog callback
|
* Main dialog callback
|
||||||
*/
|
*/
|
||||||
|
@ -2358,7 +2279,8 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
|
||||||
SHChangeNotifyDeregister(ulRegister);
|
SHChangeNotifyDeregister(ulRegister);
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
ClearDrives();
|
ClearDrives();
|
||||||
StrArrayDestroy(&BlockingProcess);
|
StopProcessSearch();
|
||||||
|
StrArrayDestroy(&BlockingProcessList);
|
||||||
StrArrayDestroy(&ImageList);
|
StrArrayDestroy(&ImageList);
|
||||||
DestroyAllTooltips();
|
DestroyAllTooltips();
|
||||||
DestroyWindow(hLogDialog);
|
DestroyWindow(hLogDialog);
|
||||||
|
@ -2447,12 +2369,19 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
|
||||||
if (HIWORD(wParam) != CBN_SELCHANGE)
|
if (HIWORD(wParam) != CBN_SELCHANGE)
|
||||||
break;
|
break;
|
||||||
nb_devices = ComboBox_GetCount(hDeviceList);
|
nb_devices = ComboBox_GetCount(hDeviceList);
|
||||||
PrintStatusDebug(0, (nb_devices==1)?MSG_208:MSG_209, nb_devices);
|
PrintStatusDebug(0, (nb_devices == 1) ? MSG_208 : MSG_209, nb_devices);
|
||||||
PopulateProperties();
|
PopulateProperties();
|
||||||
nDeviceIndex = ComboBox_GetCurSel(hDeviceList);
|
nDeviceIndex = ComboBox_GetCurSel(hDeviceList);
|
||||||
DeviceNum = (nDeviceIndex == CB_ERR) ? 0 : (DWORD)ComboBox_GetItemData(hDeviceList, nDeviceIndex);
|
DeviceNum = (nDeviceIndex == CB_ERR) ? 0 : (DWORD)ComboBox_GetItemData(hDeviceList, nDeviceIndex);
|
||||||
SendMessage(hMainDialog, WM_COMMAND, (CBN_SELCHANGE_INTERNAL << 16) | IDC_FILE_SYSTEM,
|
SendMessage(hMainDialog, WM_COMMAND, (CBN_SELCHANGE_INTERNAL << 16) | IDC_FILE_SYSTEM,
|
||||||
ComboBox_GetCurSel(hFileSystem));
|
ComboBox_GetCurSel(hFileSystem));
|
||||||
|
if (nb_devices == 0) {
|
||||||
|
// No need to run the process search if no device is selected
|
||||||
|
StopProcessSearch();
|
||||||
|
} else if (!StartProcessSearch() || !SetProcessSearch(DeviceNum)) {
|
||||||
|
uprintf("Failed to start conflicting process search");
|
||||||
|
StopProcessSearch();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case IDC_IMAGE_OPTION:
|
case IDC_IMAGE_OPTION:
|
||||||
if (HIWORD(wParam) != CBN_SELCHANGE)
|
if (HIWORD(wParam) != CBN_SELCHANGE)
|
||||||
|
@ -2654,7 +2583,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
|
||||||
EnableControls(FALSE, FALSE);
|
EnableControls(FALSE, FALSE);
|
||||||
FormatStatus = 0;
|
FormatStatus = 0;
|
||||||
LastWriteError = 0;
|
LastWriteError = 0;
|
||||||
StrArrayClear(&BlockingProcess);
|
StrArrayClear(&BlockingProcessList);
|
||||||
no_confirmation_on_cancel = FALSE;
|
no_confirmation_on_cancel = FALSE;
|
||||||
SendMessage(hMainDialog, UM_PROGRESS_INIT, 0, 0);
|
SendMessage(hMainDialog, UM_PROGRESS_INIT, 0, 0);
|
||||||
selection_default = (int)ComboBox_GetCurItemData(hBootType);
|
selection_default = (int)ComboBox_GetCurItemData(hBootType);
|
||||||
|
@ -3049,8 +2978,15 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CheckDriveAccess(SEARCH_PROCESS_TIMEOUT, TRUE))
|
// Detect processes that have write (0x2) or exec (0x4) permissions against our drive.
|
||||||
goto aborted_start;
|
// Ideally, exec should be no big deal, but Windows complains on USB ejection if a
|
||||||
|
// process such as cmd.exe holds exec rights, so we follow suit.
|
||||||
|
if (GetProcessSearch(SEARCH_PROCESS_TIMEOUT, 0x06, TRUE)) {
|
||||||
|
char title[128];
|
||||||
|
ComboBox_GetTextU(hDeviceList, title, sizeof(title));
|
||||||
|
if (!Notification(MSG_WARNING_QUESTION, NULL, NULL, title, lmprintf(MSG_132)))
|
||||||
|
goto aborted_start;
|
||||||
|
}
|
||||||
|
|
||||||
GetWindowTextU(hDeviceList, tmp, ARRAYSIZE(tmp));
|
GetWindowTextU(hDeviceList, tmp, ARRAYSIZE(tmp));
|
||||||
if (MessageBoxExU(hMainDialog, lmprintf(MSG_003, tmp),
|
if (MessageBoxExU(hMainDialog, lmprintf(MSG_003, tmp),
|
||||||
|
@ -3132,8 +3068,9 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
|
||||||
PrintInfo(0, MSG_212);
|
PrintInfo(0, MSG_212);
|
||||||
MessageBeep(MB_ICONERROR);
|
MessageBeep(MB_ICONERROR);
|
||||||
FlashTaskbar(dialog_handle);
|
FlashTaskbar(dialog_handle);
|
||||||
if (BlockingProcess.Index > 0) {
|
GetProcessSearch(0, 0x07, FALSE);
|
||||||
ListDialog(lmprintf(MSG_042), lmprintf(MSG_055), BlockingProcess.String, BlockingProcess.Index);
|
if (BlockingProcessList.Index > 0) {
|
||||||
|
ListDialog(lmprintf(MSG_042), lmprintf(MSG_055), BlockingProcessList.String, BlockingProcessList.Index);
|
||||||
} else {
|
} else {
|
||||||
if (WindowsVersion.Version >= WINDOWS_10) {
|
if (WindowsVersion.Version >= WINDOWS_10) {
|
||||||
// Try to detect if 'Controlled Folder Access' is enabled on Windows 10 or later. See also:
|
// Try to detect if 'Controlled Folder Access' is enabled on Windows 10 or later. See also:
|
||||||
|
@ -3831,18 +3768,7 @@ extern int TestHashes(void);
|
||||||
// Ctrl-T => Alternate Test mode that doesn't require a full rebuild
|
// Ctrl-T => Alternate Test mode that doesn't require a full rebuild
|
||||||
if ((ctrl_without_focus || ((GetKeyState(VK_CONTROL) & 0x8000) && (msg.message == WM_KEYDOWN)))
|
if ((ctrl_without_focus || ((GetKeyState(VK_CONTROL) & 0x8000) && (msg.message == WM_KEYDOWN)))
|
||||||
&& (msg.wParam == 'T')) {
|
&& (msg.wParam == 'T')) {
|
||||||
// TestHashes();
|
TestHashes();
|
||||||
dll_resolver_t resolver = { 0 };
|
|
||||||
resolver.path = "C:\\Windows\\System32\\Dism\\FfuProvider.dll";
|
|
||||||
resolver.count = 3;
|
|
||||||
resolver.name = calloc(resolver.count, sizeof(char*));
|
|
||||||
resolver.address = calloc(resolver.count, sizeof(uint32_t));
|
|
||||||
resolver.name[0] = "FfuCaptureImage";
|
|
||||||
resolver.name[1] = "FfuApplyImage";
|
|
||||||
resolver.name[2] = "FfuMountImage";
|
|
||||||
uprintf("Got %d resolved addresses", ResolveDllAddress(&resolver));
|
|
||||||
free(resolver.name);
|
|
||||||
free(resolver.address);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
10
src/rufus.h
10
src/rufus.h
|
@ -87,8 +87,8 @@
|
||||||
#define STATUS_MSG_TIMEOUT 3500 // How long should cheat mode messages appear for on the status bar
|
#define STATUS_MSG_TIMEOUT 3500 // How long should cheat mode messages appear for on the status bar
|
||||||
#define WRITE_RETRIES 4
|
#define WRITE_RETRIES 4
|
||||||
#define WRITE_TIMEOUT 5000 // How long we should wait between write retries (in ms)
|
#define WRITE_TIMEOUT 5000 // How long we should wait between write retries (in ms)
|
||||||
#define SEARCH_PROCESS_TIMEOUT 10000 // How long we should search for conflicting processes before giving up (in ms)
|
#define SEARCH_PROCESS_TIMEOUT 5000 // How long we should wait to get the conflicting process data (in ms)
|
||||||
#define NET_SESSION_TIMEOUT 3500 // How long we should wait to connect, send or receive internet data
|
#define NET_SESSION_TIMEOUT 3500 // How long we should wait to connect, send or receive internet data (in ms)
|
||||||
#define FS_DEFAULT FS_FAT32
|
#define FS_DEFAULT FS_FAT32
|
||||||
#define SINGLE_CLUSTERSIZE_DEFAULT 0x00000100
|
#define SINGLE_CLUSTERSIZE_DEFAULT 0x00000100
|
||||||
#define BADLOCKS_PATTERN_TYPES 5
|
#define BADLOCKS_PATTERN_TYPES 5
|
||||||
|
@ -729,8 +729,10 @@ extern char* ToLocaleName(DWORD lang_id);
|
||||||
extern void SetAlertPromptMessages(void);
|
extern void SetAlertPromptMessages(void);
|
||||||
extern BOOL SetAlertPromptHook(void);
|
extern BOOL SetAlertPromptHook(void);
|
||||||
extern void ClrAlertPromptHook(void);
|
extern void ClrAlertPromptHook(void);
|
||||||
extern DWORD CheckDriveAccess(DWORD dwTimeOut, BOOL bPrompt);
|
extern BOOL StartProcessSearch(void);
|
||||||
extern BYTE SearchProcess(char* HandleName, DWORD dwTimeout, BOOL bPartialMatch, BOOL bIgnoreSelf, BOOL bQuiet);
|
extern void StopProcessSearch(void);
|
||||||
|
extern BOOL SetProcessSearch(DWORD DeviceNum);
|
||||||
|
extern BYTE GetProcessSearch(uint32_t timeout, uint8_t access_mask, BOOL bIgnoreStaleProcesses);
|
||||||
extern BOOL EnablePrivileges(void);
|
extern BOOL EnablePrivileges(void);
|
||||||
extern void FlashTaskbar(HANDLE handle);
|
extern void FlashTaskbar(HANDLE handle);
|
||||||
extern DWORD WaitForSingleObjectWithMessages(HANDLE hHandle, DWORD dwMilliseconds);
|
extern DWORD WaitForSingleObjectWithMessages(HANDLE hHandle, DWORD dwMilliseconds);
|
||||||
|
|
10
src/rufus.rc
10
src/rufus.rc
|
@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||||
IDD_DIALOG DIALOGEX 12, 12, 232, 326
|
IDD_DIALOG DIALOGEX 12, 12, 232, 326
|
||||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||||
EXSTYLE WS_EX_ACCEPTFILES
|
EXSTYLE WS_EX_ACCEPTFILES
|
||||||
CAPTION "Rufus 4.3.2084"
|
CAPTION "Rufus 4.3.2085"
|
||||||
FONT 9, "Segoe UI Symbol", 400, 0, 0x0
|
FONT 9, "Segoe UI Symbol", 400, 0, 0x0
|
||||||
BEGIN
|
BEGIN
|
||||||
LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP
|
LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP
|
||||||
|
@ -392,8 +392,8 @@ END
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 4,3,2084,0
|
FILEVERSION 4,3,2085,0
|
||||||
PRODUCTVERSION 4,3,2084,0
|
PRODUCTVERSION 4,3,2085,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -411,13 +411,13 @@ BEGIN
|
||||||
VALUE "Comments", "https://rufus.ie"
|
VALUE "Comments", "https://rufus.ie"
|
||||||
VALUE "CompanyName", "Akeo Consulting"
|
VALUE "CompanyName", "Akeo Consulting"
|
||||||
VALUE "FileDescription", "Rufus"
|
VALUE "FileDescription", "Rufus"
|
||||||
VALUE "FileVersion", "4.3.2084"
|
VALUE "FileVersion", "4.3.2085"
|
||||||
VALUE "InternalName", "Rufus"
|
VALUE "InternalName", "Rufus"
|
||||||
VALUE "LegalCopyright", "© 2011-2023 Pete Batard (GPL v3)"
|
VALUE "LegalCopyright", "© 2011-2023 Pete Batard (GPL v3)"
|
||||||
VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html"
|
VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html"
|
||||||
VALUE "OriginalFilename", "rufus-4.3.exe"
|
VALUE "OriginalFilename", "rufus-4.3.exe"
|
||||||
VALUE "ProductName", "Rufus"
|
VALUE "ProductName", "Rufus"
|
||||||
VALUE "ProductVersion", "4.3.2084"
|
VALUE "ProductVersion", "4.3.2085"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
|
@ -562,8 +562,8 @@ BOOL WriteFileWithRetry(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWr
|
||||||
break;
|
break;
|
||||||
if (nTry < nNumRetries) {
|
if (nTry < nNumRetries) {
|
||||||
uprintf("Retrying in %d seconds...", WRITE_TIMEOUT / 1000);
|
uprintf("Retrying in %d seconds...", WRITE_TIMEOUT / 1000);
|
||||||
// Don't sit idly but use the downtime to check for conflicting processes...
|
// TODO: Call GetProcessSearch() here?
|
||||||
Sleep(CheckDriveAccess(WRITE_TIMEOUT, FALSE));
|
Sleep(WRITE_TIMEOUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (SCODE_CODE(GetLastError()) == ERROR_SUCCESS)
|
if (SCODE_CODE(GetLastError()) == ERROR_SUCCESS)
|
||||||
|
|
Loading…
Reference in a new issue