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" | ||||||
|  |  | ||||||
							
								
								
									
										475
									
								
								src/process.c
									
										
									
									
									
								
							
							
						
						
									
										475
									
								
								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,44 +440,109 @@ 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)); | ||||||
| 	if (NT_SUCCESS(status)) | 	hLock = CreateMutexA(NULL, TRUE, NULL); | ||||||
| 		status = PhCreateHeap(); | 	if (hLock == NULL) | ||||||
| 
 |  | ||||||
| 	if (NT_SUCCESS(status)) |  | ||||||
| 		status = PhEnumHandlesEx(&handles); |  | ||||||
| 
 |  | ||||||
| 	if (!NT_SUCCESS(status)) { |  | ||||||
| 		uprintf("Warning: Could not enumerate process handles: %s", NtStatusError(status)); |  | ||||||
| 		goto out; | 		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(PhCreateHeap())) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	// Wait until we are signaled active one way or another
 | ||||||
|  | 	if (!blocking_process.bActive && | ||||||
|  | 		(WaitForSingleObject(blocking_process.hStart, INFINITE) != WAIT_OBJECT_0)) { | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	bInitSuccess = TRUE; | ||||||
|  | 	while (blocking_process.bActive) { | ||||||
|  | 		// Get a lock to our data
 | ||||||
|  | 		if (WaitForSingleObject(hLock, SEARCH_PROCESS_LOCK_TIMEOUT) != WAIT_OBJECT_0) | ||||||
|  | 			goto out; | ||||||
|  | 		// No handles to check => just sleep for a while
 | ||||||
|  | 		if (blocking_process.nHandles == 0) { | ||||||
|  | 			ReleaseMutex(hLock); | ||||||
|  | 			Sleep(500); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 		// Work on our own copy of the handle names so we don't have to hold the
 | ||||||
|  | 		// mutex for string comparison. Update only if the version has changed.
 | ||||||
|  | 		if (blocking_process.nVersion[0] != blocking_process.nVersion[1]) { | ||||||
|  | 			assert(blocking_process.wHandleName != NULL && blocking_process.nHandles != 0); | ||||||
|  | 			if (blocking_process.wHandleName == NULL || blocking_process.nHandles == 0) { | ||||||
|  | 				ReleaseMutex(hLock); | ||||||
|  | 				goto out; | ||||||
|  | 			} | ||||||
|  | 			if (wHandleName != NULL) { | ||||||
|  | 				for (i = 0; i < nHandles; i++) | ||||||
|  | 					free(wHandleName[i]); | ||||||
|  | 				free(wHandleName); | ||||||
|  | 			} | ||||||
|  | 			safe_free(wHandleNameLen); | ||||||
|  | 			nHandles = blocking_process.nHandles; | ||||||
|  | 			wHandleName = calloc(nHandles, sizeof(wchar_t*)); | ||||||
|  | 			if (wHandleName == NULL) { | ||||||
|  | 				ReleaseMutex(hLock); | ||||||
|  | 				goto out; | ||||||
|  | 			} | ||||||
|  | 			wHandleNameLen = calloc(nHandles, sizeof(USHORT)); | ||||||
|  | 			if (wHandleNameLen == NULL) { | ||||||
|  | 				ReleaseMutex(hLock); | ||||||
|  | 				goto out; | ||||||
|  | 			} | ||||||
|  | 			for (i = 0; i < nHandles; i++) { | ||||||
|  | 				wHandleName[i] = wcsdup(blocking_process.wHandleName[i]); | ||||||
|  | 				wHandleNameLen[i] = (USHORT)wcslen(blocking_process.wHandleName[i]); | ||||||
|  | 				if (wHandleName[i] == NULL) { | ||||||
|  | 					ReleaseMutex(hLock); | ||||||
|  | 					goto out; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			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; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		pid[0] = (ULONG_PTR)0; | 		pid[0] = (ULONG_PTR)0; | ||||||
| 		cur_pid = 1; | 		cur_pid = 1; | ||||||
| 
 |  | ||||||
| 	wHandleNameLen = (USHORT)wcslen(_wHandleName); |  | ||||||
| 
 |  | ||||||
| 		bufferSize = 0x200; | 		bufferSize = 0x200; | ||||||
| 		buffer = PhAllocate(bufferSize); | 		buffer = PhAllocate(bufferSize); | ||||||
| 		if (buffer == NULL) | 		if (buffer == NULL) | ||||||
| 			goto out; | 			goto out; | ||||||
| 
 | 
 | ||||||
| 	for (i = 0; ; i++) { | 		for (i = 0; blocking_process.bActive; i++) { | ||||||
| 			ULONG attempts = 8; | 			ULONG attempts = 8; | ||||||
| 			PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handleInfo = NULL; | 			PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handleInfo = NULL; | ||||||
| 
 | 
 | ||||||
|  | @ -500,12 +571,28 @@ static DWORD WINAPI SearchProcessThread(LPVOID param) | ||||||
| 			if (pid[0] != pid[1]) { | 			if (pid[0] != pid[1]) { | ||||||
| 				cur_pid = (cur_pid + 1) % 2; | 				cur_pid = (cur_pid + 1) % 2; | ||||||
| 
 | 
 | ||||||
| 			// If we're switching process and found a match, print it
 | 				// If we're switching process and found a match, store it
 | ||||||
| 				if (bFound) { | 				if (bFound) { | ||||||
| 				static_sprintf (tmp, "● [%06u] %s (%s)", (uint32_t)pid[cur_pid], cmdline, access_rights_str[access_rights & 0x7]); | 					if (WaitForSingleObject(hLock, SEARCH_PROCESS_LOCK_TIMEOUT) == WAIT_OBJECT_0) { | ||||||
| 				// tmp may contain a '%' so don't feed it as a naked format string
 | 						ProcessEntry* pe = blocking_process.Process; | ||||||
| 				vuprintf("%s", tmp); | 						// Prune entries that have not been detected for a few passes
 | ||||||
| 				StrArrayAdd(&BlockingProcess, tmp, TRUE); | 						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; | 					bFound = FALSE; | ||||||
| 					access_rights = 0; | 					access_rights = 0; | ||||||
| 				} | 				} | ||||||
|  | @ -518,7 +605,9 @@ static DWORD WINAPI SearchProcessThread(LPVOID param) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 		CHECK_FOR_USER_CANCEL; | 			// Exit thread condition
 | ||||||
|  | 			if (!blocking_process.bActive) | ||||||
|  | 				goto out; | ||||||
| 
 | 
 | ||||||
| 			// Exit loop condition
 | 			// Exit loop condition
 | ||||||
| 			if (i >= handles->NumberOfHandles) | 			if (i >= handles->NumberOfHandles) | ||||||
|  | @ -541,8 +630,6 @@ static DWORD WINAPI SearchProcessThread(LPVOID param) | ||||||
| 					(HANDLE)handleInfo->UniqueProcessId); | 					(HANDLE)handleInfo->UniqueProcessId); | ||||||
| 				// There exists some processes we can't access
 | 				// There exists some processes we can't access
 | ||||||
| 				if (!NT_SUCCESS(status)) { | 				if (!NT_SUCCESS(status)) { | ||||||
| 				uuprintf("SearchProcess: Could not open process %ld: %s", |  | ||||||
| 					handleInfo->UniqueProcessId, NtStatusError(status)); |  | ||||||
| 					processHandle = NULL; | 					processHandle = NULL; | ||||||
| 					if (status == STATUS_ACCESS_DENIED) { | 					if (status == STATUS_ACCESS_DENIED) { | ||||||
| 						last_access_denied_pid = handleInfo->UniqueProcessId; | 						last_access_denied_pid = handleInfo->UniqueProcessId; | ||||||
|  | @ -552,16 +639,12 @@ static DWORD WINAPI SearchProcessThread(LPVOID param) | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			// Now duplicate this handle onto our own process, so that we can access its properties
 | 			// Now duplicate this handle onto our own process, so that we can access its properties
 | ||||||
| 		if (processHandle == NtCurrentProcess()) { | 			if (processHandle == NtCurrentProcess()) | ||||||
| 			if (_bIgnoreSelf) |  | ||||||
| 				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) | ||||||
|  | @ -574,7 +657,6 @@ static DWORD WINAPI SearchProcessThread(LPVOID param) | ||||||
| 				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); | ||||||
|  | @ -582,25 +664,19 @@ static DWORD WINAPI SearchProcessThread(LPVOID param) | ||||||
| 					break; | 					break; | ||||||
| 				} | 				} | ||||||
| 			} while (--attempts); | 			} while (--attempts); | ||||||
| 		if (!NT_SUCCESS(status)) { | 			if (!NT_SUCCESS(status)) | ||||||
| 			uuprintf("SearchProcess: NtQueryObject failed for handle %X of process %ld: %s", |  | ||||||
| 				handleInfo->HandleValue, handleInfo->UniqueProcessId, NtStatusError(status)); |  | ||||||
| 				continue; | 				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) | ||||||
| 		// Don't bother comparing if we are looking for full match and the length is different
 |  | ||||||
| 		if ((!_bPartialMatch) && (wHandleNameLen != buffer->Name.Length)) |  | ||||||
| 				continue; | 				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; | 			bFound = TRUE; | ||||||
| 
 | 
 | ||||||
| 			// Keep a mask of all the access rights being used
 | 			// Keep a mask of all the access rights being used
 | ||||||
|  | @ -608,11 +684,7 @@ static DWORD WINAPI SearchProcessThread(LPVOID param) | ||||||
| 			// The Executable bit is in a place we don't like => reposition it
 | 			// The Executable bit is in a place we don't like => reposition it
 | ||||||
| 			if (access_rights & 0x20) | 			if (access_rights & 0x20) | ||||||
| 				access_rights = (access_rights & 0x03) | 0x04; | 				access_rights = (access_rights & 0x03) | 0x04; | ||||||
| 		access_mask |= (BYTE) (access_rights & 0x7) + 0x80;	// Bit 7 is always set if a process was found
 | 			access_rights &= 0x07; | ||||||
| 
 |  | ||||||
| 		// 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
 | 			// Where possible, try to get the full command line
 | ||||||
| 			bGotCmdLine = FALSE; | 			bGotCmdLine = FALSE; | ||||||
|  | @ -649,61 +721,252 @@ static DWORD WINAPI SearchProcessThread(LPVOID param) | ||||||
| 					(ULONGLONG)handleInfo->UniqueProcessId); | 					(ULONGLONG)handleInfo->UniqueProcessId); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 |  | ||||||
| out: |  | ||||||
| 	if (cmdline[0] != 0) |  | ||||||
| 		vuprintf("You should close these applications before attempting to reformat the drive."); |  | ||||||
| 	else |  | ||||||
| 		vuprintf("NOTE: Could not identify the process(es) or service(s) accessing %S", _wHandleName); |  | ||||||
| 
 |  | ||||||
| 		PhFree(buffer); | 		PhFree(buffer); | ||||||
| 		PhFree(handles); | 		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: | ||||||
|  | 	if (!bInitSuccess) | ||||||
|  | 		uprintf("Warning: Could not start process handle enumerator!"); | ||||||
|  | 
 | ||||||
|  | 	if (wHandleName != NULL) { | ||||||
|  | 		for (i = 0; i < nHandles; i++) | ||||||
|  | 			free(wHandleName[i]); | ||||||
|  | 		free(wHandleName); | ||||||
|  | 	} | ||||||
|  | 	safe_free(wHandleNameLen); | ||||||
|  | 
 | ||||||
| 	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; | ||||||
|  |  | ||||||
							
								
								
									
										124
									
								
								src/rufus.c
									
										
									
									
									
								
							
							
						
						
									
										124
									
								
								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.
 | ||||||
|  | 		// 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; | 				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…
	
	Add table
		Add a link
		
	
		Reference in a new issue