Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntoskrnl.exe/tests/Makefile.in | 4 ++ dlls/ntoskrnl.exe/tests/driver.c | 44 ++++++++++++++ dlls/ntoskrnl.exe/tests/driver.h | 1 + dlls/ntoskrnl.exe/tests/{driver.h => driver2.c} | 37 +++++++----- dlls/ntoskrnl.exe/tests/driver2.spec | 1 + dlls/ntoskrnl.exe/tests/ntoskrnl.c | 76 ++++++++++++++++++------- 6 files changed, 128 insertions(+), 35 deletions(-) copy dlls/ntoskrnl.exe/tests/{driver.h => driver2.c} (55%) create mode 100644 dlls/ntoskrnl.exe/tests/driver2.spec
diff --git a/dlls/ntoskrnl.exe/tests/Makefile.in b/dlls/ntoskrnl.exe/tests/Makefile.in index 6a427ca..fae11bb 100644 --- a/dlls/ntoskrnl.exe/tests/Makefile.in +++ b/dlls/ntoskrnl.exe/tests/Makefile.in @@ -3,8 +3,12 @@ IMPORTS = advapi32
driver_IMPORTS = winecrt0 ntoskrnl driver_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native +driver2_IMPORTS = winecrt0 ntoskrnl +driver2_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native
SOURCES = \ driver.c \ driver.spec \ + driver2.c \ + driver2.spec \ ntoskrnl.c diff --git a/dlls/ntoskrnl.exe/tests/driver.c b/dlls/ntoskrnl.exe/tests/driver.c index cde78e5..d424396 100644 --- a/dlls/ntoskrnl.exe/tests/driver.c +++ b/dlls/ntoskrnl.exe/tests/driver.c @@ -187,6 +187,29 @@ static void test_init_funcs(void) ok(timer2.Header.SignalState == 0, "got: %u\n", timer2.Header.SignalState); }
+static const WCHAR driver2_path[] = { + '\','R','e','g','i','s','t','r','y', + '\','M','a','c','h','i','n','e', + '\','S','y','s','t','e','m', + '\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t', + '\','S','e','r','v','i','c','e','s', + '\','W','i','n','e','T','e','s','t','D','r','i','v','e','r','2',0 +}; + +static void test_load_driver(void) +{ + UNICODE_STRING name; + NTSTATUS ret; + + RtlInitUnicodeString(&name, driver2_path); + + ret = ZwLoadDriver(&name); + ok(!ret, "got %#x\n", ret); + + ret = ZwUnloadDriver(&name); + ok(!ret, "got %#x\n", ret); +} + static NTSTATUS main_test(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info) { ULONG length = stack->Parameters.DeviceIoControl.OutputBufferLength; @@ -213,6 +236,7 @@ static NTSTATUS main_test(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info) test_currentprocess(); test_mdl_map(); test_init_funcs(); + test_load_driver();
/* print process report */ if (test_input->winetest_debug) @@ -245,6 +269,23 @@ static NTSTATUS test_basic_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR * return STATUS_SUCCESS; }
+static NTSTATUS test_load_driver_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info) +{ + BOOL *load = irp->AssociatedIrp.SystemBuffer; + UNICODE_STRING name; + + if (!load) + return STATUS_ACCESS_VIOLATION; + + *info = 0; + + RtlInitUnicodeString(&name, driver2_path); + if (*load) + return ZwLoadDriver(&name); + else + return ZwUnloadDriver(&name); +} + static NTSTATUS WINAPI driver_Create(DEVICE_OBJECT *device, IRP *irp) { irp->IoStatus.Status = STATUS_SUCCESS; @@ -265,6 +306,9 @@ static NTSTATUS WINAPI driver_IoControl(DEVICE_OBJECT *device, IRP *irp) case IOCTL_WINETEST_MAIN_TEST: status = main_test(irp, stack, &irp->IoStatus.Information); break; + case IOCTL_WINETEST_LOAD_DRIVER: + status = test_load_driver_ioctl(irp, stack, &irp->IoStatus.Information); + break; default: break; } diff --git a/dlls/ntoskrnl.exe/tests/driver.h b/dlls/ntoskrnl.exe/tests/driver.h index b254d44..6a4179e 100644 --- a/dlls/ntoskrnl.exe/tests/driver.h +++ b/dlls/ntoskrnl.exe/tests/driver.h @@ -24,6 +24,7 @@ /* All custom IOCTLs need to have a function value >= 0x800. */ #define IOCTL_WINETEST_BASIC_IOCTL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_WINETEST_MAIN_TEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_WINETEST_LOAD_DRIVER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
static const char teststr[] = "Wine is not an emulator";
diff --git a/dlls/ntoskrnl.exe/tests/driver.h b/dlls/ntoskrnl.exe/tests/driver2.c similarity index 55% copy from dlls/ntoskrnl.exe/tests/driver.h copy to dlls/ntoskrnl.exe/tests/driver2.c index b254d44..c648ec6 100644 --- a/dlls/ntoskrnl.exe/tests/driver.h +++ b/dlls/ntoskrnl.exe/tests/driver2.c @@ -1,9 +1,7 @@ /* - * ntoskrnl.exe testing framework + * Second driver loaded by driver.c * - * Copyright 2015 Sebastian Lackner - * Copyright 2015 Michael Müller - * Copyright 2015 Christian Costa + * Copyright 2018 Zebediah Figura * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,17 +18,28 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include <stdarg.h>
-/* All custom IOCTLs need to have a function value >= 0x800. */ -#define IOCTL_WINETEST_BASIC_IOCTL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define IOCTL_WINETEST_MAIN_TEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "winioctl.h" +#include "ddk/wdm.h"
-static const char teststr[] = "Wine is not an emulator"; +#include "driver.h"
-struct test_input +static void WINAPI driver_Unload(DRIVER_OBJECT *driver) { - int running_under_wine; - int winetest_report_success; - int winetest_debug; - WCHAR path[1]; -}; + DbgPrint("unloading driver2\n"); +} + +NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, UNICODE_STRING *registry) +{ + DbgPrint("loading driver2\n"); + + driver->DriverUnload = driver_Unload; + + return STATUS_SUCCESS; +} diff --git a/dlls/ntoskrnl.exe/tests/driver2.spec b/dlls/ntoskrnl.exe/tests/driver2.spec new file mode 100644 index 0000000..ad33444 --- /dev/null +++ b/dlls/ntoskrnl.exe/tests/driver2.spec @@ -0,0 +1 @@ +# nothing here yet diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index 40de760..febf4c6 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -30,9 +30,6 @@
#include "driver.h"
-static const char driver_name[] = "WineTestDriver"; -static const char device_path[] = "\\.\WineTestDriver"; - static HANDLE device;
static BOOL (WINAPI *pRtlDosPathNameToNtPathName_U)( LPCWSTR, PUNICODE_STRING, PWSTR*, CURDIR* ); @@ -80,11 +77,9 @@ static void unload_driver(SC_HANDLE service) CloseServiceHandle(service); }
-static SC_HANDLE load_driver(char *filename) +static SC_HANDLE load_driver(char *filename, const char *resname, const char *driver_name) { SC_HANDLE manager, service; - SERVICE_STATUS status; - BOOL ret;
manager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (!manager && GetLastError() == ERROR_ACCESS_DENIED) @@ -94,12 +89,11 @@ static SC_HANDLE load_driver(char *filename) } ok(!!manager, "OpenSCManager failed\n");
- /* before we start with the actual tests, make sure to terminate - * any old wine test drivers. */ + /* stop any old drivers running under this name */ service = OpenServiceA(manager, driver_name, SERVICE_ALL_ACCESS); if (service) unload_driver(service);
- load_resource("driver.dll", filename); + load_resource(resname, filename); trace("Trying to load driver %s\n", filename);
service = CreateServiceA(manager, driver_name, driver_name, @@ -107,7 +101,15 @@ static SC_HANDLE load_driver(char *filename) SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, filename, NULL, NULL, NULL, NULL, NULL); ok(!!service, "CreateService failed: %u\n", GetLastError()); + CloseServiceHandle(manager); + return service; +} + +static BOOL start_driver(HANDLE service) +{ + SERVICE_STATUS status; + BOOL ret;
SetLastError(0xdeadbeef); ret = StartServiceA(service, 0, NULL); @@ -117,8 +119,7 @@ static SC_HANDLE load_driver(char *filename) skip("Failed to start service; probably your machine doesn't accept unsigned drivers.\n"); DeleteService(service); CloseServiceHandle(service); - DeleteFileA(filename); - return NULL; + return FALSE; } ok(ret, "StartService failed: %u\n", GetLastError());
@@ -134,10 +135,7 @@ static SC_HANDLE load_driver(char *filename) ok(status.dwCurrentState == SERVICE_RUNNING, "expected SERVICE_RUNNING, got %d\n", status.dwCurrentState);
- device = CreateFileA(device_path, 0, 0, NULL, OPEN_EXISTING, 0, NULL); - ok(device != INVALID_HANDLE_VALUE, "failed to open device: %u\n", GetLastError()); - - return service; + return TRUE; }
static void main_test(void) @@ -194,22 +192,58 @@ static void test_basic_ioctl(void) ok(!strcmp(buf, teststr), "got '%s'\n", buf); }
+static void test_load_driver(SC_HANDLE service) +{ + SERVICE_STATUS status; + BOOL load, res; + DWORD sz; + + res = QueryServiceStatus(service, &status); + ok(res, "QueryServiceStatusEx failed: %u\n", GetLastError()); + ok(status.dwCurrentState == SERVICE_STOPPED, "got state %#x\n", status.dwCurrentState); + + load = TRUE; + res = DeviceIoControl(device, IOCTL_WINETEST_LOAD_DRIVER, &load, sizeof(load), NULL, 0, &sz, NULL); + ok(res, "DeviceIoControl failed: %u\n", GetLastError()); + + res = QueryServiceStatus(service, &status); + ok(res, "QueryServiceStatusEx failed: %u\n", GetLastError()); + ok(status.dwCurrentState == SERVICE_RUNNING, "got state %#x\n", status.dwCurrentState); + + load = FALSE; + res = DeviceIoControl(device, IOCTL_WINETEST_LOAD_DRIVER, &load, sizeof(load), NULL, 0, &sz, NULL); + ok(res, "DeviceIoControl failed: %u\n", GetLastError()); + + res = QueryServiceStatus(service, &status); + ok(res, "QueryServiceStatusEx failed: %u\n", GetLastError()); + ok(status.dwCurrentState == SERVICE_STOPPED, "got state %#x\n", status.dwCurrentState); +} + START_TEST(ntoskrnl) { - char filename[MAX_PATH]; - SC_HANDLE service; - BOOL ret; + char filename[MAX_PATH], filename2[MAX_PATH]; + SC_HANDLE service, service2;
HMODULE hntdll = GetModuleHandleA("ntdll.dll"); pRtlDosPathNameToNtPathName_U = (void *)GetProcAddress(hntdll, "RtlDosPathNameToNtPathName_U");
- if (!(service = load_driver(filename))) + service = load_driver(filename, "driver.dll", "WineTestDriver"); + if (!start_driver(service)) + { + DeleteFileA(filename); return; + } + service2 = load_driver(filename2, "driver2.dll", "WineTestDriver2"); + + device = CreateFileA("\\.\WineTestDriver", 0, 0, NULL, OPEN_EXISTING, 0, NULL); + ok(device != INVALID_HANDLE_VALUE, "failed to open device: %u\n", GetLastError());
test_basic_ioctl(); main_test(); + test_load_driver(service2);
+ unload_driver(service2); unload_driver(service); - ret = DeleteFileA(filename); - ok(ret, "DeleteFile failed: %u\n", GetLastError()); + ok(DeleteFileA(filename), "DeleteFile failed: %u\n", GetLastError()); + ok(DeleteFileA(filename2), "DeleteFile failed: %u\n", GetLastError()); }
This essentially reverts 440482d2ef31333d1bc3ce15b0aad4ceec60466c.
440482d was aimed towards making it possible to load multiple drivers asynchronously, as well as to allow reentrancy. Unfortunately, asynchronicity is incorrect, as demonstrated by bug 38836, and some trivial testing shows that the SCM database lock is held for the entirety of the driver entry and exit routines, and that StartService() and ControlService() block until they complete. 5726824 and dd2624a nullified the effects of 440482d, making driver loading all but synchronous (with the exception of the added 30 second timeout, but this is actually incorrect: drivers can block indefinitely).
This patch therefore does not change any behaviour, but rather removes the use of threadpools and "async" functions, essentially reverting back to the implementation prior to 440482d. The incidental change to unload_driver() made by that patch (viz. never to unload a driver without a DriverUnload() routine) is kept.
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- (Please feel free to keep as much or as little of the above as is appropriate, if any.)
programs/winedevice/device.c | 223 ++++++++++--------------------------------- 1 file changed, 51 insertions(+), 172 deletions(-)
diff --git a/programs/winedevice/device.c b/programs/winedevice/device.c index fa8b268..9ee094a 100644 --- a/programs/winedevice/device.c +++ b/programs/winedevice/device.c @@ -45,20 +45,14 @@ extern NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event );
static const WCHAR winedeviceW[] = {'w','i','n','e','d','e','v','i','c','e',0}; static SERVICE_STATUS_HANDLE service_handle; -static PTP_CLEANUP_GROUP cleanup_group; static SC_HANDLE manager_handle; -static BOOL shutdown_in_progress; static HANDLE stop_event;
-#define EVENT_STARTED 0 -#define EVENT_ERROR 1 - struct wine_driver { struct wine_rb_entry entry;
SERVICE_STATUS_HANDLE handle; - HANDLE events[2]; DRIVER_OBJECT *driver_obj; WCHAR name[1]; }; @@ -71,15 +65,6 @@ static int wine_drivers_rb_compare( const void *key, const struct wine_rb_entry
static struct wine_rb_tree wine_drivers = { wine_drivers_rb_compare };
-static CRITICAL_SECTION drivers_cs; -static CRITICAL_SECTION_DEBUG critsect_debug = -{ - 0, 0, &drivers_cs, - { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, - 0, 0, { (DWORD_PTR)(__FILE__ ": drivers_cs") } -}; -static CRITICAL_SECTION drivers_cs = { &critsect_debug, -1, 0, 0, 0, 0 }; - /* find the LDR_MODULE corresponding to the driver module */ static LDR_MODULE *find_ldr_module( HMODULE module ) { @@ -299,11 +284,21 @@ static void set_service_status( SERVICE_STATUS_HANDLE handle, DWORD state, DWORD SetServiceStatus( handle, &status ); }
-static void WINAPI async_unload_driver( PTP_CALLBACK_INSTANCE instance, void *context ) +/* call the driver unload function */ +static void unload_driver( struct wine_rb_entry *entry, void *context ) { - struct wine_driver *driver = context; + struct wine_driver *driver = WINE_RB_ENTRY_VALUE( entry, struct wine_driver, entry ); DRIVER_OBJECT *driver_obj = driver->driver_obj; - LDR_MODULE *ldr; + LDR_MODULE *ldr = driver_obj->DriverSection; + + if (!driver_obj->DriverUnload) + { + TRACE( "driver %s does not support unloading\n", wine_dbgstr_w(driver->name) ); + return; + } + + TRACE( "stopping driver %s\n", wine_dbgstr_w(driver->name) ); + set_service_status( driver->handle, SERVICE_STOP_PENDING, 0 );
TRACE_(relay)( "\1Call driver unload %p (obj=%p)\n", driver_obj->DriverUnload, driver_obj );
@@ -311,7 +306,6 @@ static void WINAPI async_unload_driver( PTP_CALLBACK_INSTANCE instance, void *co
TRACE_(relay)( "\1Ret driver unload %p (obj=%p)\n", driver_obj->DriverUnload, driver_obj );
- ldr = driver_obj->DriverSection; FreeLibrary( ldr->BaseAddress ); IoDeleteDriver( driver_obj ); ObDereferenceObject( driver_obj ); @@ -321,106 +315,15 @@ static void WINAPI async_unload_driver( PTP_CALLBACK_INSTANCE instance, void *co HeapFree( GetProcessHeap(), 0, driver ); }
-/* call the driver unload function */ -static NTSTATUS unload_driver( struct wine_rb_entry *entry, BOOL destroy ) -{ - TP_CALLBACK_ENVIRON environment; - struct wine_driver *driver = WINE_RB_ENTRY_VALUE( entry, struct wine_driver, entry ); - DRIVER_OBJECT *driver_obj = driver->driver_obj; - - if (!driver_obj) - { - TRACE( "driver %s has not finished loading yet\n", wine_dbgstr_w(driver->name) ); - return STATUS_UNSUCCESSFUL; - } - if (!driver_obj->DriverUnload) - { - TRACE( "driver %s does not support unloading\n", wine_dbgstr_w(driver->name) ); - return STATUS_UNSUCCESSFUL; - } - - TRACE( "stopping driver %s\n", wine_dbgstr_w(driver->name) ); - set_service_status( driver->handle, SERVICE_STOP_PENDING, 0 ); - - if (destroy) - { - async_unload_driver( NULL, driver ); - return STATUS_SUCCESS; - } - - wine_rb_remove( &wine_drivers, &driver->entry ); - - memset( &environment, 0, sizeof(environment) ); - environment.Version = 1; - environment.CleanupGroup = cleanup_group; - - /* don't block the service control handler */ - if (!TrySubmitThreadpoolCallback( async_unload_driver, driver, &environment )) - async_unload_driver( NULL, driver ); - - return STATUS_SUCCESS; -} - -static void WINAPI async_create_driver( PTP_CALLBACK_INSTANCE instance, void *context ) -{ - static const WCHAR driverW[] = {'\','D','r','i','v','e','r','\',0}; - struct wine_driver *driver = context; - DRIVER_OBJECT *driver_obj; - UNICODE_STRING drv_name; - NTSTATUS status; - WCHAR *str; - - if (!(str = HeapAlloc( GetProcessHeap(), 0, sizeof(driverW) + strlenW(driver->name)*sizeof(WCHAR) ))) - goto error; - - lstrcpyW( str, driverW); - lstrcatW( str, driver->name ); - RtlInitUnicodeString( &drv_name, str ); - - status = IoCreateDriver( &drv_name, init_driver ); - if (status != STATUS_SUCCESS) - { - ERR( "failed to create driver %s: %08x\n", debugstr_w(driver->name), status ); - RtlFreeUnicodeString( &drv_name ); - goto error; - } - - status = ObReferenceObjectByName( &drv_name, OBJ_CASE_INSENSITIVE, NULL, - 0, NULL, KernelMode, NULL, (void **)&driver_obj ); - RtlFreeUnicodeString( &drv_name ); - if (status != STATUS_SUCCESS) - { - ERR( "failed to locate driver %s: %08x\n", debugstr_w(driver->name), status ); - goto error; - } - - SetEvent(driver->events[EVENT_STARTED]); - - EnterCriticalSection( &drivers_cs ); - driver->driver_obj = driver_obj; - set_service_status( driver->handle, SERVICE_RUNNING, - SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN ); - LeaveCriticalSection( &drivers_cs ); - return; - -error: - SetEvent(driver->events[EVENT_ERROR]); - EnterCriticalSection( &drivers_cs ); - wine_rb_remove( &wine_drivers, &driver->entry ); - LeaveCriticalSection( &drivers_cs ); - - set_service_status( driver->handle, SERVICE_STOPPED, 0 ); - CloseServiceHandle( (void *)driver->handle ); - HeapFree( GetProcessHeap(), 0, driver ); -} - /* load a driver and notify services.exe about the status change */ static NTSTATUS create_driver( const WCHAR *driver_name ) { - TP_CALLBACK_ENVIRON environment; + static const WCHAR driverW[] = {'\','D','r','i','v','e','r','\',0}; struct wine_driver *driver; + UNICODE_STRING drv_name; + NTSTATUS status; DWORD length; - DWORD ret; + WCHAR *str;
length = FIELD_OFFSET( struct wine_driver, name[strlenW(driver_name) + 1] ); if (!(driver = HeapAlloc( GetProcessHeap(), 0, length ))) @@ -442,63 +345,46 @@ static NTSTATUS create_driver( const WCHAR *driver_name ) return STATUS_UNSUCCESSFUL; }
+ if (!(str = HeapAlloc( GetProcessHeap(), 0, sizeof(driverW) + strlenW(driver->name)*sizeof(WCHAR) ))) + { + status = STATUS_NO_MEMORY; + goto error; + } + TRACE( "starting driver %s\n", wine_dbgstr_w(driver_name) ); set_service_status( driver->handle, SERVICE_START_PENDING, 0 );
- memset( &environment, 0, sizeof(environment) ); - environment.Version = 1; - environment.CleanupGroup = cleanup_group; + lstrcpyW( str, driverW); + lstrcatW( str, driver->name ); + RtlInitUnicodeString( &drv_name, str );
- driver->events[EVENT_STARTED] = CreateEventW(NULL, TRUE, FALSE, NULL); - driver->events[EVENT_ERROR] = CreateEventW(NULL, TRUE, FALSE, NULL); - - /* don't block the service control handler */ - if (!TrySubmitThreadpoolCallback( async_create_driver, driver, &environment )) - async_create_driver( NULL, driver ); - - /* Windows wait 30 Seconds */ - ret = WaitForMultipleObjects(2, driver->events, FALSE, 30000); - if(ret == WAIT_OBJECT_0 + EVENT_ERROR) - return STATUS_UNSUCCESSFUL; - else if(ret == WAIT_TIMEOUT) - return ERROR_SERVICE_REQUEST_TIMEOUT; - - return STATUS_SUCCESS; -} - -static void wine_drivers_rb_destroy( struct wine_rb_entry *entry, void *context ) -{ - if (unload_driver( entry, TRUE ) != STATUS_SUCCESS) + status = IoCreateDriver( &drv_name, init_driver ); + if (status != STATUS_SUCCESS) { - struct wine_driver *driver = WINE_RB_ENTRY_VALUE( entry, struct wine_driver, entry ); - CloseHandle(driver->events[EVENT_STARTED]); - CloseHandle(driver->events[EVENT_ERROR]); - ObDereferenceObject( driver->driver_obj ); - CloseServiceHandle( (void *)driver->handle ); - HeapFree( GetProcessHeap(), 0, driver ); + ERR( "failed to create driver %s: %08x\n", debugstr_w(driver->name), status ); + RtlFreeUnicodeString( &drv_name ); + goto error; } -}
-static void WINAPI async_shutdown_drivers( PTP_CALLBACK_INSTANCE instance, void *context ) -{ - CloseThreadpoolCleanupGroupMembers( cleanup_group, FALSE, NULL ); + status = ObReferenceObjectByName( &drv_name, OBJ_CASE_INSENSITIVE, NULL, + 0, NULL, KernelMode, NULL, (void **)&driver->driver_obj ); + RtlFreeUnicodeString( &drv_name ); + if (status != STATUS_SUCCESS) + { + ERR( "failed to locate driver %s: %08x\n", debugstr_w(driver->name), status ); + goto error; + }
- EnterCriticalSection( &drivers_cs ); - wine_rb_destroy( &wine_drivers, wine_drivers_rb_destroy, NULL ); - LeaveCriticalSection( &drivers_cs ); + set_service_status( driver->handle, SERVICE_RUNNING, + SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN ); + return STATUS_SUCCESS;
- SetEvent( stop_event ); -} - -static void shutdown_drivers( void ) -{ - if (shutdown_in_progress) return; - - /* don't block the service control handler */ - if (!TrySubmitThreadpoolCallback( async_shutdown_drivers, NULL, NULL )) - async_shutdown_drivers( NULL, NULL ); - - shutdown_in_progress = TRUE; +error: + set_service_status( driver->handle, SERVICE_STOPPED, 0 ); + CloseServiceHandle( (void *)driver->handle ); + wine_rb_remove( &wine_drivers, &driver->entry ); + HeapFree( GetProcessHeap(), 0, driver ); + return status; }
static DWORD device_handler( DWORD ctrl, const WCHAR *driver_name ) @@ -506,10 +392,6 @@ static DWORD device_handler( DWORD ctrl, const WCHAR *driver_name ) struct wine_rb_entry *entry; DWORD result = NO_ERROR;
- if (shutdown_in_progress) - return ERROR_SERVICE_CANNOT_ACCEPT_CTRL; - - EnterCriticalSection( &drivers_cs ); entry = wine_rb_get( &wine_drivers, driver_name );
switch (ctrl) @@ -521,14 +403,13 @@ static DWORD device_handler( DWORD ctrl, const WCHAR *driver_name )
case SERVICE_CONTROL_STOP: if (!entry) break; - result = RtlNtStatusToDosError(unload_driver( entry, FALSE )); + unload_driver( entry, NULL ); break;
default: FIXME( "got driver ctrl %x for %s\n", ctrl, wine_dbgstr_w(driver_name) ); break; } - LeaveCriticalSection( &drivers_cs ); return result; }
@@ -548,7 +429,7 @@ static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_ case SERVICE_CONTROL_SHUTDOWN: TRACE( "shutting down %s\n", wine_dbgstr_w(service_group) ); set_service_status( service_handle, SERVICE_STOP_PENDING, 0 ); - shutdown_drivers(); + SetEvent( stop_event ); return NO_ERROR; default: FIXME( "got service ctrl %x for %s\n", ctrl, wine_dbgstr_w(service_group) ); @@ -564,8 +445,6 @@ static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
if (!(stop_event = CreateEventW( NULL, TRUE, FALSE, NULL ))) return; - if (!(cleanup_group = CreateThreadpoolCleanupGroup())) - return; if (!(manager_handle = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT ))) return; if (!(service_handle = RegisterServiceCtrlHandlerExW( winedeviceW, service_handler, (void *)service_group ))) @@ -578,9 +457,9 @@ static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv ) wine_ntoskrnl_main_loop( stop_event );
TRACE( "service group %s stopped\n", wine_dbgstr_w(service_group) ); + wine_rb_destroy( &wine_drivers, unload_driver, NULL ); set_service_status( service_handle, SERVICE_STOPPED, 0 ); CloseServiceHandle( manager_handle ); - CloseThreadpoolCleanupGroup( cleanup_group ); CloseHandle( stop_event ); }
We can't implement ZwLoadDriver() on top of StartService(), since the latter takes the service database lock. Instead simply move the entire body of create_driver()/unload_driver() into ZwLoadDriver()/ZwUnloadDriver(). Similarly, clean up the list of loaded drivers in ntoskrnl rather than winedevice.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45084 Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntoskrnl.exe/ntoskrnl.c | 378 +++++++++++++++++++++++++++++++++++-------- programs/winedevice/device.c | 358 +++------------------------------------- 2 files changed, 332 insertions(+), 404 deletions(-)
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c index 280bf27..7b178c4 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/ntoskrnl.c @@ -39,6 +39,7 @@ #include "winbase.h" #include "winuser.h" #include "dbt.h" +#include "winreg.h" #include "ddk/csq.h" #include "ddk/ntddk.h" #include "ddk/ntifs.h" @@ -48,6 +49,7 @@ #include "wine/debug.h" #include "wine/heap.h" #include "wine/rbtree.h" +#include "wine/svcctl.h"
WINE_DEFAULT_DEBUG_CHANNEL(ntoskrnl); WINE_DECLARE_DEBUG_CHANNEL(relay); @@ -96,6 +98,7 @@ struct wine_driver
DRIVER_OBJECT driver_obj; DRIVER_EXTENSION driver_extension; + SERVICE_STATUS_HANDLE service_handle; };
static NTSTATUS get_device_id( DEVICE_OBJECT *device, BUS_QUERY_ID_TYPE type, WCHAR **id ); @@ -583,6 +586,52 @@ static const dispatch_func dispatch_funcs[IRP_MJ_MAXIMUM_FUNCTION + 1] = NULL, /* IRP_MJ_PNP */ };
+/* helper function to update service status */ +static void set_service_status( SERVICE_STATUS_HANDLE handle, DWORD state, DWORD accepted ) +{ + SERVICE_STATUS status; + status.dwServiceType = SERVICE_WIN32; + status.dwCurrentState = state; + status.dwControlsAccepted = accepted; + status.dwWin32ExitCode = 0; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = 0; + status.dwWaitHint = (state == SERVICE_START_PENDING) ? 10000 : 0; + SetServiceStatus( handle, &status ); +} + +static void unload_driver( struct wine_rb_entry *entry, void *context ) +{ + struct wine_driver *driver = WINE_RB_ENTRY_VALUE( entry, struct wine_driver, entry ); + SERVICE_STATUS_HANDLE service_handle = driver->service_handle; + LDR_MODULE *ldr; + + if (!service_handle) return; /* not a service */ + + TRACE("%s\n", debugstr_us(&driver->driver_obj.DriverName)); + + if (!driver->driver_obj.DriverUnload) + { + TRACE( "driver %s does not support unloading\n", debugstr_us(&driver->driver_obj.DriverName) ); + return; + } + + ldr = driver->driver_obj.DriverSection; + + set_service_status( service_handle, SERVICE_STOP_PENDING, 0 ); + + TRACE_(relay)( "\1Call driver unload %p (obj=%p)\n", driver->driver_obj.DriverUnload, &driver->driver_obj ); + + driver->driver_obj.DriverUnload( &driver->driver_obj ); + + TRACE_(relay)( "\1Ret driver unload %p (obj=%p)\n", driver->driver_obj.DriverUnload, &driver->driver_obj ); + + FreeLibrary( ldr->BaseAddress ); + IoDeleteDriver( &driver->driver_obj ); + + set_service_status( service_handle, SERVICE_STOPPED, 0 ); + CloseServiceHandle( (void *)service_handle ); +}
/*********************************************************************** * wine_ntoskrnl_main_loop (Not a Windows API) @@ -607,7 +656,8 @@ NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event ) if (!in_buff && !(in_buff = HeapAlloc( GetProcessHeap(), 0, in_size ))) { ERR( "failed to allocate buffer\n" ); - return STATUS_NO_MEMORY; + status = STATUS_NO_MEMORY; + goto done; }
SERVER_START_REQ( get_next_device_request ) @@ -663,13 +713,18 @@ NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event ) if (ret == WAIT_OBJECT_0) { HeapFree( GetProcessHeap(), 0, in_buff ); - return STATUS_SUCCESS; + status = STATUS_SUCCESS; + goto done; } if (ret != WAIT_IO_COMPLETION) break; } break; } } + +done: + wine_rb_destroy( &wine_drivers, unload_driver, NULL ); + return status; }
@@ -3092,7 +3147,8 @@ static NTSTATUS open_driver( const UNICODE_STRING *service_name, SC_HANDLE *serv return STATUS_NOT_SUPPORTED; }
- *service = OpenServiceW( manager_handle, name + strlenW(servicesW), SERVICE_ALL_ACCESS ); + *service = OpenServiceW( manager_handle, name + strlenW(servicesW), + SERVICE_QUERY_CONFIG | SERVICE_SET_STATUS ); RtlFreeHeap( GetProcessHeap(), 0, name ); CloseServiceHandle( manager_handle );
@@ -3135,106 +3191,296 @@ error: return STATUS_UNSUCCESSFUL; }
+/* find the LDR_MODULE corresponding to the driver module */ +static LDR_MODULE *find_ldr_module( HMODULE module ) +{ + LDR_MODULE *ldr; + ULONG_PTR magic; + + LdrLockLoaderLock( 0, NULL, &magic ); + if (LdrFindEntryForAddress( module, &ldr )) + { + WARN( "module not found for %p\n", module ); + ldr = NULL; + } + LdrUnlockLoaderLock( 0, magic ); + + return ldr; +} + +/* load the driver module file */ +static HMODULE load_driver_module( const WCHAR *name ) +{ + IMAGE_NT_HEADERS *nt; + const IMAGE_IMPORT_DESCRIPTOR *imports; + SYSTEM_BASIC_INFORMATION info; + int i; + INT_PTR delta; + ULONG size; + HMODULE module = LoadLibraryW( name ); + + if (!module) return NULL; + nt = RtlImageNtHeader( module ); + + if (!(delta = (char *)module - (char *)nt->OptionalHeader.ImageBase)) return module; + + /* the loader does not apply relocations to non page-aligned binaries or executables, + * we have to do it ourselves */ + + NtQuerySystemInformation( SystemBasicInformation, &info, sizeof(info), NULL ); + if (nt->OptionalHeader.SectionAlignment < info.PageSize || + !(nt->FileHeader.Characteristics & IMAGE_FILE_DLL)) + { + DWORD old; + IMAGE_BASE_RELOCATION *rel, *end; + + if ((rel = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &size ))) + { + WINE_TRACE( "%s: relocating from %p to %p\n", + wine_dbgstr_w(name), (char *)module - delta, module ); + end = (IMAGE_BASE_RELOCATION *)((char *)rel + size); + while (rel < end && rel->SizeOfBlock) + { + void *page = (char *)module + rel->VirtualAddress; + VirtualProtect( page, info.PageSize, PAGE_EXECUTE_READWRITE, &old ); + rel = LdrProcessRelocationBlock( page, (rel->SizeOfBlock - sizeof(*rel)) / sizeof(USHORT), + (USHORT *)(rel + 1), delta ); + if (old != PAGE_EXECUTE_READWRITE) VirtualProtect( page, info.PageSize, old, &old ); + if (!rel) goto error; + } + /* make sure we don't try again */ + size = FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + nt->FileHeader.SizeOfOptionalHeader; + VirtualProtect( nt, size, PAGE_READWRITE, &old ); + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0; + VirtualProtect( nt, size, old, &old ); + } + } + + /* make sure imports are relocated too */ + + if ((imports = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size ))) + { + for (i = 0; imports[i].Name && imports[i].FirstThunk; i++) + { + char *name = (char *)module + imports[i].Name; + WCHAR buffer[32], *p = buffer; + + while (p < buffer + 32) if (!(*p++ = *name++)) break; + if (p <= buffer + 32) FreeLibrary( load_driver_module( buffer ) ); + } + } + + return module; + +error: + FreeLibrary( module ); + return NULL; +} + +/* load the .sys module for a device driver */ +static HMODULE load_driver( const WCHAR *driver_name, const UNICODE_STRING *keyname ) +{ + static const WCHAR driversW[] = {'\','d','r','i','v','e','r','s','\',0}; + static const WCHAR systemrootW[] = {'\','S','y','s','t','e','m','R','o','o','t','\',0}; + static const WCHAR postfixW[] = {'.','s','y','s',0}; + static const WCHAR ntprefixW[] = {'\','?','?','\',0}; + static const WCHAR ImagePathW[] = {'I','m','a','g','e','P','a','t','h',0}; + HKEY driver_hkey; + HMODULE module; + LPWSTR path = NULL, str; + DWORD type, size; + + if (RegOpenKeyW( HKEY_LOCAL_MACHINE, keyname->Buffer + 18 /* skip \registry\machine */, &driver_hkey )) + { + WINE_ERR( "cannot open key %s, err=%u\n", wine_dbgstr_w(keyname->Buffer), GetLastError() ); + return NULL; + } + + /* read the executable path from memory */ + size = 0; + if (!RegQueryValueExW( driver_hkey, ImagePathW, NULL, &type, NULL, &size )) + { + str = HeapAlloc( GetProcessHeap(), 0, size ); + if (!RegQueryValueExW( driver_hkey, ImagePathW, NULL, &type, (LPBYTE)str, &size )) + { + size = ExpandEnvironmentStringsW(str,NULL,0); + path = HeapAlloc(GetProcessHeap(),0,size*sizeof(WCHAR)); + ExpandEnvironmentStringsW(str,path,size); + } + HeapFree( GetProcessHeap(), 0, str ); + if (!path) + { + RegCloseKey( driver_hkey ); + return NULL; + } + + if (!strncmpiW( path, systemrootW, 12 )) + { + WCHAR buffer[MAX_PATH]; + + GetWindowsDirectoryW(buffer, MAX_PATH); + + str = HeapAlloc(GetProcessHeap(), 0, (size -11 + strlenW(buffer)) + * sizeof(WCHAR)); + lstrcpyW(str, buffer); + lstrcatW(str, path + 11); + HeapFree( GetProcessHeap(), 0, path ); + path = str; + } + else if (!strncmpW( path, ntprefixW, 4 )) + str = path + 4; + else + str = path; + } + else + { + /* default is to use the driver name + ".sys" */ + WCHAR buffer[MAX_PATH]; + GetSystemDirectoryW(buffer, MAX_PATH); + path = HeapAlloc(GetProcessHeap(),0, + (strlenW(buffer) + strlenW(driversW) + strlenW(driver_name) + strlenW(postfixW) + 1) + *sizeof(WCHAR)); + lstrcpyW(path, buffer); + lstrcatW(path, driversW); + lstrcatW(path, driver_name); + lstrcatW(path, postfixW); + str = path; + } + RegCloseKey( driver_hkey ); + + WINE_TRACE( "loading driver %s\n", wine_dbgstr_w(str) ); + + module = load_driver_module( str ); + HeapFree( GetProcessHeap(), 0, path ); + return module; +} + +/* call the driver init entry point */ +static NTSTATUS WINAPI init_driver( DRIVER_OBJECT *driver_object, UNICODE_STRING *keyname ) +{ + unsigned int i; + NTSTATUS status; + const IMAGE_NT_HEADERS *nt; + const WCHAR *driver_name; + HMODULE module; + + /* Retrieve driver name from the keyname */ + driver_name = strrchrW( keyname->Buffer, '\' ); + driver_name++; + + module = load_driver( driver_name, keyname ); + if (!module) + return STATUS_DLL_INIT_FAILED; + + driver_object->DriverSection = find_ldr_module( module ); + + nt = RtlImageNtHeader( module ); + if (!nt->OptionalHeader.AddressOfEntryPoint) return STATUS_SUCCESS; + driver_object->DriverInit = (PDRIVER_INITIALIZE)((char *)module + nt->OptionalHeader.AddressOfEntryPoint); + + TRACE_(relay)( "\1Call driver init %p (obj=%p,str=%s)\n", + driver_object->DriverInit, driver_object, wine_dbgstr_w(keyname->Buffer) ); + + status = driver_object->DriverInit( driver_object, keyname ); + + TRACE_(relay)( "\1Ret driver init %p (obj=%p,str=%s) retval=%08x\n", + driver_object->DriverInit, driver_object, wine_dbgstr_w(keyname->Buffer), status ); + + WINE_TRACE( "init done for %s obj %p\n", wine_dbgstr_w(driver_name), driver_object ); + WINE_TRACE( "- DriverInit = %p\n", driver_object->DriverInit ); + WINE_TRACE( "- DriverStartIo = %p\n", driver_object->DriverStartIo ); + WINE_TRACE( "- DriverUnload = %p\n", driver_object->DriverUnload ); + for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) + WINE_TRACE( "- MajorFunction[%d] = %p\n", i, driver_object->MajorFunction[i] ); + + return status; +} + +static BOOLEAN get_drv_name( UNICODE_STRING *drv_name, const UNICODE_STRING *service_name ) +{ + static const WCHAR driverW[] = {'\','D','r','i','v','e','r','\',0}; + WCHAR *str; + + if (!(str = heap_alloc( sizeof(driverW) + service_name->Length - strlenW(servicesW)*sizeof(WCHAR) ))) + return FALSE; + + lstrcpyW( str, driverW ); + lstrcpynW( str + strlenW(driverW), service_name->Buffer + strlenW(servicesW), + service_name->Length/sizeof(WCHAR) - strlenW(servicesW) + 1 ); + RtlInitUnicodeString( drv_name, str ); + return TRUE; +}
/*********************************************************************** * ZwLoadDriver (NTOSKRNL.EXE.@) */ NTSTATUS WINAPI ZwLoadDriver( const UNICODE_STRING *service_name ) { - SERVICE_STATUS_PROCESS service_status; - SC_HANDLE service_handle; - ULONGLONG start_time; + SERVICE_STATUS_HANDLE service_handle; + struct wine_rb_entry *entry; + struct wine_driver *driver; + UNICODE_STRING drv_name; NTSTATUS status; - DWORD bytes;
TRACE( "(%s)\n", debugstr_us(service_name) );
- if ((status = open_driver( service_name, &service_handle )) != STATUS_SUCCESS) + if ((status = open_driver( service_name, (SC_HANDLE *)&service_handle )) != STATUS_SUCCESS) return status;
- TRACE( "trying to start %s\n", debugstr_us(service_name) ); - - start_time = GetTickCount64(); - for (;;) + if (!get_drv_name( &drv_name, service_name )) { - if (StartServiceW( service_handle, 0, NULL )) break; - if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING) break; - if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) goto error; - if (GetTickCount64() - start_time > 30000) goto error; - Sleep( 100 ); + CloseServiceHandle( (void *)service_handle ); + return STATUS_NO_MEMORY; }
- start_time = GetTickCount64(); - for (;;) + set_service_status( service_handle, SERVICE_START_PENDING, 0 ); + + status = IoCreateDriver( &drv_name, init_driver ); + entry = wine_rb_get( &wine_drivers, &drv_name ); + RtlFreeUnicodeString( &drv_name ); + if (status != STATUS_SUCCESS) { - if (!QueryServiceStatusEx( service_handle, SC_STATUS_PROCESS_INFO, - (BYTE *)&service_status, sizeof(service_status), &bytes )) goto error; - if (service_status.dwCurrentState != SERVICE_START_PENDING) break; - if (GetTickCount64() - start_time > 30000) goto error; - Sleep( 100 ); + ERR( "failed to create driver %s: %08x\n", debugstr_us(service_name), status ); + goto error; }
- if (service_status.dwCurrentState == SERVICE_RUNNING) - { - if (service_status.dwProcessId != GetCurrentProcessId()) - FIXME( "driver %s was loaded into a different process\n", debugstr_us(service_name) ); + driver = WINE_RB_ENTRY_VALUE( entry, struct wine_driver, entry ); + driver->service_handle = service_handle;
- status = STATUS_SUCCESS; - goto done; - } + set_service_status( service_handle, SERVICE_RUNNING, + SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN ); + return STATUS_SUCCESS;
error: - WARN( "failed to start service %s\n", debugstr_us(service_name) ); - status = STATUS_UNSUCCESSFUL; - -done: - TRACE( "returning status %08x\n", status ); - CloseServiceHandle( service_handle ); + set_service_status( service_handle, SERVICE_STOPPED, 0 ); + CloseServiceHandle( (void *)service_handle ); return status; }
- /*********************************************************************** * ZwUnloadDriver (NTOSKRNL.EXE.@) */ NTSTATUS WINAPI ZwUnloadDriver( const UNICODE_STRING *service_name ) { - SERVICE_STATUS service_status; - SC_HANDLE service_handle; - ULONGLONG start_time; - NTSTATUS status; + struct wine_rb_entry *entry; + UNICODE_STRING drv_name;
TRACE( "(%s)\n", debugstr_us(service_name) );
- if ((status = open_driver( service_name, &service_handle )) != STATUS_SUCCESS) - return status; + if (!get_drv_name( &drv_name, service_name )) + return STATUS_NO_MEMORY;
- if (!ControlService( service_handle, SERVICE_CONTROL_STOP, &service_status )) - goto error; - - start_time = GetTickCount64(); - for (;;) + entry = wine_rb_get( &wine_drivers, &drv_name ); + RtlFreeUnicodeString( &drv_name ); + if (!entry) { - if (!QueryServiceStatus( service_handle, &service_status )) goto error; - if (service_status.dwCurrentState != SERVICE_STOP_PENDING) break; - if (GetTickCount64() - start_time > 30000) goto error; - Sleep( 100 ); + ERR( "failed to locate driver %s\n", debugstr_us(service_name) ); + return STATUS_OBJECT_NAME_NOT_FOUND; }
- if (service_status.dwCurrentState == SERVICE_STOPPED) - { - status = STATUS_SUCCESS; - goto done; - } + unload_driver( entry, NULL );
-error: - WARN( "failed to stop service %s\n", debugstr_us(service_name) ); - status = STATUS_UNSUCCESSFUL; - -done: - TRACE( "returning status %08x\n", status ); - CloseServiceHandle( service_handle ); - return status; + return STATUS_SUCCESS; }
diff --git a/programs/winedevice/device.c b/programs/winedevice/device.c index 9ee094a..201a041 100644 --- a/programs/winedevice/device.c +++ b/programs/winedevice/device.c @@ -27,19 +27,21 @@ #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" -#include "winbase.h" #include "winternl.h" -#include "winreg.h" -#include "winnls.h" -#include "winsvc.h" #include "ddk/wdm.h" -#include "wine/rbtree.h" #include "wine/svcctl.h" #include "wine/unicode.h" #include "wine/debug.h" +#include "wine/heap.h"
WINE_DEFAULT_DEBUG_CHANNEL(winedevice); -WINE_DECLARE_DEBUG_CHANNEL(relay); + +static const WCHAR servicesW[] = {'\','R','e','g','i','s','t','r','y', + '\','M','a','c','h','i','n','e', + '\','S','y','s','t','e','m', + '\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t', + '\','S','e','r','v','i','c','e','s', + '\',0};
extern NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event );
@@ -48,228 +50,6 @@ static SERVICE_STATUS_HANDLE service_handle; static SC_HANDLE manager_handle; static HANDLE stop_event;
-struct wine_driver -{ - struct wine_rb_entry entry; - - SERVICE_STATUS_HANDLE handle; - DRIVER_OBJECT *driver_obj; - WCHAR name[1]; -}; - -static int wine_drivers_rb_compare( const void *key, const struct wine_rb_entry *entry ) -{ - const struct wine_driver *driver = WINE_RB_ENTRY_VALUE( entry, const struct wine_driver, entry ); - return strcmpW( (const WCHAR *)key, driver->name ); -} - -static struct wine_rb_tree wine_drivers = { wine_drivers_rb_compare }; - -/* find the LDR_MODULE corresponding to the driver module */ -static LDR_MODULE *find_ldr_module( HMODULE module ) -{ - LDR_MODULE *ldr; - ULONG_PTR magic; - - LdrLockLoaderLock( 0, NULL, &magic ); - if (LdrFindEntryForAddress( module, &ldr )) - { - WARN( "module not found for %p\n", module ); - ldr = NULL; - } - LdrUnlockLoaderLock( 0, magic ); - - return ldr; -} - -/* load the driver module file */ -static HMODULE load_driver_module( const WCHAR *name ) -{ - IMAGE_NT_HEADERS *nt; - const IMAGE_IMPORT_DESCRIPTOR *imports; - SYSTEM_BASIC_INFORMATION info; - int i; - INT_PTR delta; - ULONG size; - HMODULE module = LoadLibraryW( name ); - - if (!module) return NULL; - nt = RtlImageNtHeader( module ); - - if (!(delta = (char *)module - (char *)nt->OptionalHeader.ImageBase)) return module; - - /* the loader does not apply relocations to non page-aligned binaries or executables, - * we have to do it ourselves */ - - NtQuerySystemInformation( SystemBasicInformation, &info, sizeof(info), NULL ); - if (nt->OptionalHeader.SectionAlignment < info.PageSize || - !(nt->FileHeader.Characteristics & IMAGE_FILE_DLL)) - { - DWORD old; - IMAGE_BASE_RELOCATION *rel, *end; - - if ((rel = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &size ))) - { - WINE_TRACE( "%s: relocating from %p to %p\n", - wine_dbgstr_w(name), (char *)module - delta, module ); - end = (IMAGE_BASE_RELOCATION *)((char *)rel + size); - while (rel < end && rel->SizeOfBlock) - { - void *page = (char *)module + rel->VirtualAddress; - VirtualProtect( page, info.PageSize, PAGE_EXECUTE_READWRITE, &old ); - rel = LdrProcessRelocationBlock( page, (rel->SizeOfBlock - sizeof(*rel)) / sizeof(USHORT), - (USHORT *)(rel + 1), delta ); - if (old != PAGE_EXECUTE_READWRITE) VirtualProtect( page, info.PageSize, old, &old ); - if (!rel) goto error; - } - /* make sure we don't try again */ - size = FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + nt->FileHeader.SizeOfOptionalHeader; - VirtualProtect( nt, size, PAGE_READWRITE, &old ); - nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0; - VirtualProtect( nt, size, old, &old ); - } - } - - /* make sure imports are relocated too */ - - if ((imports = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size ))) - { - for (i = 0; imports[i].Name && imports[i].FirstThunk; i++) - { - char *name = (char *)module + imports[i].Name; - WCHAR buffer[32], *p = buffer; - - while (p < buffer + 32) if (!(*p++ = *name++)) break; - if (p <= buffer + 32) FreeLibrary( load_driver_module( buffer ) ); - } - } - - return module; - -error: - FreeLibrary( module ); - return NULL; -} - -/* load the .sys module for a device driver */ -static HMODULE load_driver( const WCHAR *driver_name, const UNICODE_STRING *keyname ) -{ - static const WCHAR driversW[] = {'\','d','r','i','v','e','r','s','\',0}; - static const WCHAR systemrootW[] = {'\','S','y','s','t','e','m','R','o','o','t','\',0}; - static const WCHAR postfixW[] = {'.','s','y','s',0}; - static const WCHAR ntprefixW[] = {'\','?','?','\',0}; - static const WCHAR ImagePathW[] = {'I','m','a','g','e','P','a','t','h',0}; - HKEY driver_hkey; - HMODULE module; - LPWSTR path = NULL, str; - DWORD type, size; - - if (RegOpenKeyW( HKEY_LOCAL_MACHINE, keyname->Buffer + 18 /* skip \registry\machine */, &driver_hkey )) - { - WINE_ERR( "cannot open key %s, err=%u\n", wine_dbgstr_w(keyname->Buffer), GetLastError() ); - return NULL; - } - - /* read the executable path from memory */ - size = 0; - if (!RegQueryValueExW( driver_hkey, ImagePathW, NULL, &type, NULL, &size )) - { - str = HeapAlloc( GetProcessHeap(), 0, size ); - if (!RegQueryValueExW( driver_hkey, ImagePathW, NULL, &type, (LPBYTE)str, &size )) - { - size = ExpandEnvironmentStringsW(str,NULL,0); - path = HeapAlloc(GetProcessHeap(),0,size*sizeof(WCHAR)); - ExpandEnvironmentStringsW(str,path,size); - } - HeapFree( GetProcessHeap(), 0, str ); - if (!path) - { - RegCloseKey( driver_hkey ); - return NULL; - } - - if (!strncmpiW( path, systemrootW, 12 )) - { - WCHAR buffer[MAX_PATH]; - - GetWindowsDirectoryW(buffer, MAX_PATH); - - str = HeapAlloc(GetProcessHeap(), 0, (size -11 + strlenW(buffer)) - * sizeof(WCHAR)); - lstrcpyW(str, buffer); - lstrcatW(str, path + 11); - HeapFree( GetProcessHeap(), 0, path ); - path = str; - } - else if (!strncmpW( path, ntprefixW, 4 )) - str = path + 4; - else - str = path; - } - else - { - /* default is to use the driver name + ".sys" */ - WCHAR buffer[MAX_PATH]; - GetSystemDirectoryW(buffer, MAX_PATH); - path = HeapAlloc(GetProcessHeap(),0, - (strlenW(buffer) + strlenW(driversW) + strlenW(driver_name) + strlenW(postfixW) + 1) - *sizeof(WCHAR)); - lstrcpyW(path, buffer); - lstrcatW(path, driversW); - lstrcatW(path, driver_name); - lstrcatW(path, postfixW); - str = path; - } - RegCloseKey( driver_hkey ); - - WINE_TRACE( "loading driver %s\n", wine_dbgstr_w(str) ); - - module = load_driver_module( str ); - HeapFree( GetProcessHeap(), 0, path ); - return module; -} - -/* call the driver init entry point */ -static NTSTATUS WINAPI init_driver( DRIVER_OBJECT *driver_object, UNICODE_STRING *keyname ) -{ - unsigned int i; - NTSTATUS status; - const IMAGE_NT_HEADERS *nt; - const WCHAR *driver_name; - HMODULE module; - - /* Retrieve driver name from the keyname */ - driver_name = strrchrW( keyname->Buffer, '\' ); - driver_name++; - - module = load_driver( driver_name, keyname ); - if (!module) - return STATUS_DLL_INIT_FAILED; - - driver_object->DriverSection = find_ldr_module( module ); - - nt = RtlImageNtHeader( module ); - if (!nt->OptionalHeader.AddressOfEntryPoint) return STATUS_SUCCESS; - driver_object->DriverInit = (PDRIVER_INITIALIZE)((char *)module + nt->OptionalHeader.AddressOfEntryPoint); - - TRACE_(relay)( "\1Call driver init %p (obj=%p,str=%s)\n", - driver_object->DriverInit, driver_object, wine_dbgstr_w(keyname->Buffer) ); - - status = driver_object->DriverInit( driver_object, keyname ); - - TRACE_(relay)( "\1Ret driver init %p (obj=%p,str=%s) retval=%08x\n", - driver_object->DriverInit, driver_object, wine_dbgstr_w(keyname->Buffer), status ); - - WINE_TRACE( "init done for %s obj %p\n", wine_dbgstr_w(driver_name), driver_object ); - WINE_TRACE( "- DriverInit = %p\n", driver_object->DriverInit ); - WINE_TRACE( "- DriverStartIo = %p\n", driver_object->DriverStartIo ); - WINE_TRACE( "- DriverUnload = %p\n", driver_object->DriverUnload ); - for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) - WINE_TRACE( "- MajorFunction[%d] = %p\n", i, driver_object->MajorFunction[i] ); - - return status; -} - /* helper function to update service status */ static void set_service_status( SERVICE_STATUS_HANDLE handle, DWORD state, DWORD accepted ) { @@ -284,132 +64,35 @@ static void set_service_status( SERVICE_STATUS_HANDLE handle, DWORD state, DWORD SetServiceStatus( handle, &status ); }
-/* call the driver unload function */ -static void unload_driver( struct wine_rb_entry *entry, void *context ) -{ - struct wine_driver *driver = WINE_RB_ENTRY_VALUE( entry, struct wine_driver, entry ); - DRIVER_OBJECT *driver_obj = driver->driver_obj; - LDR_MODULE *ldr = driver_obj->DriverSection; - - if (!driver_obj->DriverUnload) - { - TRACE( "driver %s does not support unloading\n", wine_dbgstr_w(driver->name) ); - return; - } - - TRACE( "stopping driver %s\n", wine_dbgstr_w(driver->name) ); - set_service_status( driver->handle, SERVICE_STOP_PENDING, 0 ); - - TRACE_(relay)( "\1Call driver unload %p (obj=%p)\n", driver_obj->DriverUnload, driver_obj ); - - driver_obj->DriverUnload( driver_obj ); - - TRACE_(relay)( "\1Ret driver unload %p (obj=%p)\n", driver_obj->DriverUnload, driver_obj ); - - FreeLibrary( ldr->BaseAddress ); - IoDeleteDriver( driver_obj ); - ObDereferenceObject( driver_obj ); - - set_service_status( driver->handle, SERVICE_STOPPED, 0 ); - CloseServiceHandle( (void *)driver->handle ); - HeapFree( GetProcessHeap(), 0, driver ); -} - -/* load a driver and notify services.exe about the status change */ -static NTSTATUS create_driver( const WCHAR *driver_name ) -{ - static const WCHAR driverW[] = {'\','D','r','i','v','e','r','\',0}; - struct wine_driver *driver; - UNICODE_STRING drv_name; - NTSTATUS status; - DWORD length; - WCHAR *str; - - length = FIELD_OFFSET( struct wine_driver, name[strlenW(driver_name) + 1] ); - if (!(driver = HeapAlloc( GetProcessHeap(), 0, length ))) - return STATUS_NO_MEMORY; - - strcpyW( driver->name, driver_name ); - driver->driver_obj = NULL; - - if (!(driver->handle = (void *)OpenServiceW( manager_handle, driver_name, SERVICE_SET_STATUS ))) - { - HeapFree( GetProcessHeap(), 0, driver ); - return STATUS_UNSUCCESSFUL; - } - - if (wine_rb_put( &wine_drivers, driver_name, &driver->entry )) - { - CloseServiceHandle( (void *)driver->handle ); - HeapFree( GetProcessHeap(), 0, driver ); - return STATUS_UNSUCCESSFUL; - } - - if (!(str = HeapAlloc( GetProcessHeap(), 0, sizeof(driverW) + strlenW(driver->name)*sizeof(WCHAR) ))) - { - status = STATUS_NO_MEMORY; - goto error; - } - - TRACE( "starting driver %s\n", wine_dbgstr_w(driver_name) ); - set_service_status( driver->handle, SERVICE_START_PENDING, 0 ); - - lstrcpyW( str, driverW); - lstrcatW( str, driver->name ); - RtlInitUnicodeString( &drv_name, str ); - - status = IoCreateDriver( &drv_name, init_driver ); - if (status != STATUS_SUCCESS) - { - ERR( "failed to create driver %s: %08x\n", debugstr_w(driver->name), status ); - RtlFreeUnicodeString( &drv_name ); - goto error; - } - - status = ObReferenceObjectByName( &drv_name, OBJ_CASE_INSENSITIVE, NULL, - 0, NULL, KernelMode, NULL, (void **)&driver->driver_obj ); - RtlFreeUnicodeString( &drv_name ); - if (status != STATUS_SUCCESS) - { - ERR( "failed to locate driver %s: %08x\n", debugstr_w(driver->name), status ); - goto error; - } - - set_service_status( driver->handle, SERVICE_RUNNING, - SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN ); - return STATUS_SUCCESS; - -error: - set_service_status( driver->handle, SERVICE_STOPPED, 0 ); - CloseServiceHandle( (void *)driver->handle ); - wine_rb_remove( &wine_drivers, &driver->entry ); - HeapFree( GetProcessHeap(), 0, driver ); - return status; -} - static DWORD device_handler( DWORD ctrl, const WCHAR *driver_name ) { - struct wine_rb_entry *entry; + UNICODE_STRING service_name; DWORD result = NO_ERROR; + WCHAR *str;
- entry = wine_rb_get( &wine_drivers, driver_name ); + if (!(str = heap_alloc( sizeof(servicesW) + strlenW(driver_name)*sizeof(WCHAR) ))) + return STATUS_NO_MEMORY; + + lstrcpyW( str, servicesW ); + lstrcatW( str, driver_name ); + RtlInitUnicodeString( &service_name, str );
switch (ctrl) { case SERVICE_CONTROL_START: - if (entry) break; - result = RtlNtStatusToDosError(create_driver( driver_name )); + result = RtlNtStatusToDosError(ZwLoadDriver( &service_name )); break;
case SERVICE_CONTROL_STOP: - if (!entry) break; - unload_driver( entry, NULL ); + result = RtlNtStatusToDosError(ZwUnloadDriver( &service_name )); break;
default: FIXME( "got driver ctrl %x for %s\n", ctrl, wine_dbgstr_w(driver_name) ); break; } + + RtlFreeUnicodeString( &service_name ); return result; }
@@ -457,7 +140,6 @@ static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv ) wine_ntoskrnl_main_loop( stop_event );
TRACE( "service group %s stopped\n", wine_dbgstr_w(service_group) ); - wine_rb_destroy( &wine_drivers, unload_driver, NULL ); set_service_status( service_handle, SERVICE_STOPPED, 0 ); CloseServiceHandle( manager_handle ); CloseHandle( stop_event );
Hi,
While running your changed tests on Windows, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=41153
Your paranoid android.
=== w8adm (32 bit ntoskrnl) === ntoskrnl.c:124: Test failed: StartService failed: 6 ntoskrnl.c:128: Test failed: QueryServiceStatus failed: 6 ntoskrnl.c:135: Test failed: expected SERVICE_RUNNING, got 4208704 ntoskrnl.c:239: Test failed: failed to open device: 2 ntoskrnl.c:190: Test failed: DeviceIoControl failed: 6 ntoskrnl.c:192: Test failed: got ' |.' ntoskrnl.c:165: Test failed: DeviceIoControl failed: 6 ntoskrnl.c:166: Test failed: got size fffffd3f ntoskrnl.c:202: Test failed: QueryServiceStatusEx failed: 6 ntoskrnl.c:203: Test failed: got state 0x55005c ntoskrnl.c:207: Test failed: DeviceIoControl failed: 6 ntoskrnl.c:210: Test failed: QueryServiceStatusEx failed: 6 ntoskrnl.c:211: Test failed: got state 0x55005c ntoskrnl.c:215: Test failed: DeviceIoControl failed: 6 ntoskrnl.c:218: Test failed: QueryServiceStatusEx failed: 6 ntoskrnl.c:219: Test failed: got state 0x55005c ntoskrnl.c:73: Test failed: expected SERVICE_STOPPED, got 219 ntoskrnl.c:73: Test failed: expected SERVICE_STOPPED, got 219 ntoskrnl.c:247: Test failed: DeleteFile failed: 6 ntoskrnl.c:248: Test failed: DeleteFile failed: 123