[Wine-patches] [5/23] winedevice: Load drivers into one process.

Alexander Morozov =?iso-8859-1?q?amorozov_=CE=C1_etersoft=2Eru?=
Пн Янв 19 18:23:08 MSK 2009


For eterhack branch
----------- следующая часть -----------
From 86c901427b3c75e57bf60523bcd3a2b253279772 Mon Sep 17 00:00:00 2001
From: Alexander Morozov <amorozov на etersoft.ru>
Date: Mon, 12 Jan 2009 14:23:09 +0300
Subject: [PATCH] winedevice: Load drivers into one process.

---
 programs/winedevice/device.c |  318 +++++++++++++++++++++++++++++++++++-------
 1 files changed, 268 insertions(+), 50 deletions(-)

diff --git a/programs/winedevice/device.c b/programs/winedevice/device.c
index ac988e1..adf61a4 100644
--- a/programs/winedevice/device.c
+++ b/programs/winedevice/device.c
@@ -46,13 +46,19 @@ WINE_DECLARE_DEBUG_CHANNEL(relay);
 
 extern DEVICE_OBJECT * CDECL __wine_usbhub_get_pdo( UCHAR *pdo_info );
 extern NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event );
+extern HANDLE CDECL __wine_make_process_system(void);
 
+#define EVENT_NAME_LEN (30 * sizeof(WCHAR))
+
+static const WCHAR pipe_nameW[] = {'\\','\\','.','\\','p','i','p','e',
+                                   '\\','w','i','n','e','d','e','v','i','c','e',0};
+static const WCHAR winedevice_mutexW[] = {'_','_','w','i','n','e','_',
+                                          'W','i','n','e','d','e','v','i','c','e',0};
+
+/* these variables are used only by "winedevice driver_name" */
 static WCHAR *driver_name;
 static SERVICE_STATUS_HANDLE service_handle;
-static HKEY driver_hkey;
 static HANDLE stop_event;
-static DRIVER_OBJECT driver_obj;
-static DRIVER_EXTENSION driver_extension;
 
 /* find the LDR_MODULE corresponding to the driver module */
 static LDR_MODULE *find_ldr_module( HMODULE module )
@@ -133,7 +139,9 @@ error:
 }
 
 /* call the driver init entry point */
-static NTSTATUS init_driver( HMODULE module, UNICODE_STRING *keyname )
+static NTSTATUS init_driver( HMODULE module, UNICODE_STRING *keyname,
+                             const WCHAR *drv_name, PDRIVER_OBJECT driver_obj,
+                             PDRIVER_EXTENSION driver_extension )
 {
     unsigned int i;
     NTSTATUS status;
@@ -141,37 +149,38 @@ static NTSTATUS init_driver( HMODULE module, UNICODE_STRING *keyname )
 
     if (!nt->OptionalHeader.AddressOfEntryPoint) return STATUS_SUCCESS;
 
-    driver_obj.Size            = sizeof(driver_obj);
-    driver_obj.DriverSection   = find_ldr_module( module );
-    driver_obj.DriverInit      = (PDRIVER_INITIALIZE)((char *)module + nt->OptionalHeader.AddressOfEntryPoint);
-    driver_obj.DriverExtension = &driver_extension;
+    driver_obj->Size            = sizeof(DRIVER_OBJECT);
+    driver_obj->DriverSection   = find_ldr_module( module );
+    driver_obj->DriverInit      = (PDRIVER_INITIALIZE)((char *)module + nt->OptionalHeader.AddressOfEntryPoint);
+    driver_obj->DriverExtension = driver_extension;
 
-    driver_extension.DriverObject   = &driver_obj;
-    driver_extension.ServiceKeyName = *keyname;
+    driver_extension->DriverObject   = driver_obj;
+    driver_extension->ServiceKeyName = *keyname;
 
     if (WINE_TRACE_ON(relay))
         WINE_DPRINTF( "%04x:Call driver init %p (obj=%p,str=%s)\n", GetCurrentThreadId(),
-                      driver_obj.DriverInit, &driver_obj, wine_dbgstr_w(keyname->Buffer) );
+                      driver_obj->DriverInit, driver_obj, wine_dbgstr_w(keyname->Buffer) );
 
-    status = driver_obj.DriverInit( &driver_obj, keyname );
+    status = driver_obj->DriverInit( driver_obj, keyname );
 
     if (WINE_TRACE_ON(relay))
         WINE_DPRINTF( "%04x:Ret  driver init %p (obj=%p,str=%s) retval=%08x\n", GetCurrentThreadId(),
-                      driver_obj.DriverInit, &driver_obj, wine_dbgstr_w(keyname->Buffer), status );
+                      driver_obj->DriverInit, driver_obj, wine_dbgstr_w(keyname->Buffer), status );
 
-    WINE_TRACE( "init done for %s obj %p\n", wine_dbgstr_w(driver_name), &driver_obj );
-    WINE_TRACE( "- DriverInit = %p\n", driver_obj.DriverInit );
-    WINE_TRACE( "- DriverStartIo = %p\n", driver_obj.DriverStartIo );
-    WINE_TRACE( "- DriverUnload = %p\n", driver_obj.DriverUnload );
-    WINE_TRACE( "- AddDevice = %p\n", driver_extension.AddDevice );
+    WINE_TRACE( "init done for %s obj %p\n", wine_dbgstr_w(drv_name), driver_obj );
+    WINE_TRACE( "- DriverInit = %p\n", driver_obj->DriverInit );
+    WINE_TRACE( "- DriverStartIo = %p\n", driver_obj->DriverStartIo );
+    WINE_TRACE( "- DriverUnload = %p\n", driver_obj->DriverUnload );
+    WINE_TRACE( "- AddDevice = %p\n", driver_extension->AddDevice );
     for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
-        WINE_TRACE( "- MajorFunction[%d] = %p\n", i, driver_obj.MajorFunction[i] );
+        WINE_TRACE( "- MajorFunction[%d] = %p\n", i, driver_obj->MajorFunction[i] );
 
     return status;
 }
 
 /* load the .sys module for a device driver */
-static BOOL load_driver(void)
+static HMODULE load_driver( const WCHAR *drv_name, PDRIVER_OBJECT driver_obj,
+                            PDRIVER_EXTENSION driver_extension )
 {
     static const WCHAR driversW[] = {'\\','d','r','i','v','e','r','s','\\',0};
     static const WCHAR postfixW[] = {'.','s','y','s',0};
@@ -183,20 +192,22 @@ static BOOL load_driver(void)
                                       '\\','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};
 
+    HKEY driver_hkey;
     UNICODE_STRING keypath;
     HMODULE module;
     LPWSTR path = NULL, str;
     DWORD type, size;
+    NTSTATUS status;
 
-    str = HeapAlloc( GetProcessHeap(), 0, sizeof(servicesW) + strlenW(driver_name)*sizeof(WCHAR) );
+    str = HeapAlloc( GetProcessHeap(), 0, sizeof(servicesW) + strlenW(drv_name)*sizeof(WCHAR) );
     lstrcpyW( str, servicesW );
-    lstrcatW( str, driver_name );
+    lstrcatW( str, drv_name );
 
     if (RegOpenKeyW( HKEY_LOCAL_MACHINE, str + 18 /* skip \registry\machine */, &driver_hkey ))
     {
         WINE_ERR( "cannot open key %s, err=%u\n", wine_dbgstr_w(str), GetLastError() );
         HeapFree( GetProcessHeap(), 0, str);
-        return FALSE;
+        return NULL;
     }
     RtlInitUnicodeString( &keypath, str );
 
@@ -212,7 +223,7 @@ static BOOL load_driver(void)
             ExpandEnvironmentStringsW(str,path,size);
         }
         HeapFree( GetProcessHeap(), 0, str );
-        if (!path) return FALSE;
+        if (!path) return NULL;
     }
     else
     {
@@ -220,11 +231,11 @@ static BOOL load_driver(void)
         WCHAR buffer[MAX_PATH];
         GetSystemDirectoryW(buffer, MAX_PATH);
         path = HeapAlloc(GetProcessHeap(),0,
-          (strlenW(buffer) + strlenW(driversW) + strlenW(driver_name) + strlenW(postfixW) + 1)
+          (strlenW(buffer) + strlenW(driversW) + strlenW(drv_name) + strlenW(postfixW) + 1)
           *sizeof(WCHAR));
         lstrcpyW(path, buffer);
         lstrcatW(path, driversW);
-        lstrcatW(path, driver_name);
+        lstrcatW(path, drv_name);
         lstrcatW(path, postfixW);
     }
 
@@ -236,10 +247,32 @@ static BOOL load_driver(void)
 
     module = load_driver_module( str );
     HeapFree( GetProcessHeap(), 0, path );
-    if (!module) return FALSE;
+    if (!module) return NULL;
 
-    init_driver( module, &keypath );
-    return TRUE;
+    status = init_driver( module, &keypath, drv_name, driver_obj, driver_extension );
+    if (status != STATUS_SUCCESS)
+    {
+        FreeLibrary( module );
+        return NULL;
+    }
+    return module;
+}
+
+static void unload_driver( HMODULE module, DRIVER_OBJECT *driver_obj )
+{
+    if (driver_obj->DriverUnload)
+    {
+        if (WINE_TRACE_ON(relay))
+            WINE_DPRINTF( "%04x:Call driver unload %p (obj=%p)\n",
+                          GetCurrentThreadId(), driver_obj->DriverUnload, driver_obj );
+
+        driver_obj->DriverUnload( driver_obj );
+
+        if (WINE_TRACE_ON(relay))
+            WINE_DPRINTF( "%04x:Ret  driver unload %p (obj=%p)\n",
+                          GetCurrentThreadId(), driver_obj->DriverUnload, driver_obj );
+    }
+    FreeLibrary( module );
 }
 
 static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context )
@@ -264,24 +297,144 @@ static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_
         SetEvent( stop_event );
         return NO_ERROR;
     default:
-        WINE_FIXME( "got service ctrl %x for %s\n", ctrl, wine_dbgstr_w(driver_name) );
+        WINE_FIXME( "got service ctrl %x for %s\n", ctrl,
+                    wine_dbgstr_w(driver_name) );
         status.dwCurrentState = SERVICE_RUNNING;
         SetServiceStatus( service_handle, &status );
         return NO_ERROR;
     }
 }
 
+static int loading_request( WCHAR *event_name )
+{
+    static WCHAR winedeviceW[] = {'\\','w','i','n','e','d','e','v','i','c','e','.','e','x','e',0};
+
+    WCHAR *driver_process_cmd;
+    PROCESS_INFORMATION pi;
+    STARTUPINFOW si;
+    HANDLE pipe;
+    DWORD count, len;
+    BOOL ret, loaded;
+
+    /* create winedevice.exe process which will load drivers */
+
+    len = GetSystemDirectoryW( NULL, 0 );
+    driver_process_cmd = HeapAlloc( GetProcessHeap(), 0, sizeof(winedeviceW)
+            + sizeof(WCHAR) * len );
+    if (!driver_process_cmd) return 1;
+    GetSystemDirectoryW( driver_process_cmd, len );
+    strcpyW( driver_process_cmd + len - 1, winedeviceW );
+
+    RtlZeroMemory( &si, sizeof(STARTUPINFOW) );
+    si.cb = sizeof(STARTUPINFOW);
+    ret = CreateProcessW( NULL, driver_process_cmd, NULL, NULL, FALSE, 0,
+            NULL, NULL, &si, &pi );
+    HeapFree( GetProcessHeap(), 0, driver_process_cmd );
+    if (!ret) return 1;
+    CloseHandle( pi.hThread );
+    CloseHandle( pi.hProcess );
+
+    /* send driver and event names and receive loading result */
+
+    do {
+        WaitNamedPipeW( pipe_nameW, NMPWAIT_WAIT_FOREVER );
+        pipe = CreateFileW( pipe_nameW, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
+    } while (pipe == INVALID_HANDLE_VALUE);
+    len = (strlenW(driver_name) + 1) * sizeof(WCHAR);
+    ret = WriteFile( pipe, &len, sizeof(DWORD), &count, NULL );
+    if (!ret || count != sizeof(DWORD)) goto fail;
+    ret = WriteFile( pipe, driver_name, len, &count, NULL );
+    if (!ret || count != len) goto fail;
+    ret = WriteFile( pipe, event_name, EVENT_NAME_LEN, &count, NULL );
+    if (!ret || count != EVENT_NAME_LEN) goto fail;
+    ret = ReadFile( pipe, &loaded, sizeof(BOOL), &count, NULL );
+    if (!ret || count != sizeof(BOOL)) goto fail;
+    if (loaded)
+    {
+        CloseHandle( pipe );
+        return 0;
+    }
+fail:
+    CloseHandle( pipe );
+    return 1;
+}
+
+static HMODULE handle_loading_request( HANDLE pipe, DRIVER_OBJECT *driver_obj,
+                                       DRIVER_EXTENSION *driver_extension,
+                                       WCHAR **drv_name, WCHAR **event_name )
+{
+    HMODULE module = NULL;
+    BOOL ret, loaded = FALSE;
+    DWORD count, len;
+
+    *drv_name = NULL;
+    *event_name = NULL;
+    ret = ReadFile( pipe, &len, sizeof(DWORD), &count, NULL );
+    if (!ret || count != sizeof(DWORD)) goto end;
+    *drv_name = HeapAlloc( GetProcessHeap(), 0, len );
+    if (!*drv_name) goto end;
+    ret = ReadFile( pipe, *drv_name, len, &count, NULL );
+    if (!ret || count != len) goto end;
+    *event_name = HeapAlloc( GetProcessHeap(), 0, EVENT_NAME_LEN );
+    if (!*event_name) goto end;
+    ret = ReadFile( pipe, *event_name, EVENT_NAME_LEN, &count, NULL );
+    if (!ret || count != EVENT_NAME_LEN) goto end;
+    module = load_driver( *drv_name, driver_obj, driver_extension );
+    if (module) loaded = TRUE;
+    ret = WriteFile( pipe, &loaded, sizeof(BOOL), &count, NULL );
+    if (module && (!ret || count != sizeof(BOOL)))
+    {
+        unload_driver( module, driver_obj );
+        module = NULL;
+    }
+end:
+    DisconnectNamedPipe( pipe );
+    CloseHandle( pipe );
+    if (!module)
+    {
+        if (*drv_name) HeapFree( GetProcessHeap(), 0, *drv_name );
+        if (*event_name) HeapFree( GetProcessHeap(), 0, *drv_name );
+    }
+    return module;
+}
+
+static HANDLE create_named_event( WCHAR **event_name )
+{
+    static const WCHAR event_nameW[] = {'_','_','w','i','n','e','_',
+                                        'W','i','n','e','d','e','v','i','c','e','_','%','u',0};
+
+    HANDLE event;
+    unsigned int k = 0;
+
+    *event_name = HeapAlloc( GetProcessHeap(), 0, EVENT_NAME_LEN );
+    if (!*event_name) return NULL;
+    for (;;)
+    {
+        snprintfW( *event_name, EVENT_NAME_LEN / sizeof(WCHAR), event_nameW, k++ );
+        event = CreateEventW( NULL, TRUE, FALSE, *event_name );
+        if (event && GetLastError() != ERROR_ALREADY_EXISTS)
+            return event;
+        CloseHandle( event );
+    }
+}
+
 static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
 {
     SERVICE_STATUS status;
+    WCHAR *event_name;
 
     WINE_TRACE( "starting service %s\n", wine_dbgstr_w(driver_name) );
 
-    stop_event = CreateEventW( NULL, TRUE, FALSE, NULL );
-
+    stop_event = create_named_event( &event_name );
+    if (!stop_event)
+        return;
     service_handle = RegisterServiceCtrlHandlerExW( driver_name, service_handler, NULL );
     if (!service_handle)
+    {
+        HeapFree( GetProcessHeap(), 0, event_name );
         return;
+    }
 
     status.dwServiceType             = SERVICE_WIN32;
     status.dwCurrentState            = SERVICE_START_PENDING;
@@ -292,19 +445,45 @@ static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
     status.dwWaitHint                = 10000;
     SetServiceStatus( service_handle, &status );
 
-    if (load_driver())
+    if (!loading_request( event_name ))
+    {
+        status.dwCurrentState     = SERVICE_RUNNING;
+        status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+        SetServiceStatus( service_handle, &status );
+
+        WaitForSingleObject( stop_event, INFINITE );
+    }
+    else WINE_ERR( "driver %s failed to load\n", wine_dbgstr_w(driver_name) );
+
+    HeapFree( GetProcessHeap(), 0, event_name );
+    status.dwCurrentState     = SERVICE_STOPPED;
+    status.dwControlsAccepted = 0;
+    SetServiceStatus( service_handle, &status );
+    WINE_TRACE( "service %s stopped\n", wine_dbgstr_w(driver_name) );
+}
+
+static DWORD CALLBACK driver_thread( HANDLE pipe )
+{
+    DRIVER_OBJECT driver_obj;
+    DRIVER_EXTENSION driver_extension;
+    WCHAR *drv_name, *event_name;
+    HMODULE module;
+
+    RtlZeroMemory( &driver_obj, sizeof(driver_obj) );
+    RtlZeroMemory( &driver_extension, sizeof(driver_extension) );
+    module = handle_loading_request( pipe, &driver_obj, &driver_extension,
+            &drv_name, &event_name );
+    if (module)
     {
         UNICODE_STRING drvname;
         NTSTATUS ret = STATUS_SUCCESS;
         IRP *irp;
         IO_STACK_LOCATION *irpsp;
         PDRIVER_DISPATCH dispatch;
+        HANDLE loop_event;
 
-        status.dwCurrentState     = SERVICE_RUNNING;
-        status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
-        SetServiceStatus( service_handle, &status );
-
-        RtlInitUnicodeString( &drvname, driver_name );
+        loop_event = CreateEventW( NULL, TRUE, FALSE, event_name );
+        RtlInitUnicodeString( &drvname, drv_name );
 
         if (driver_extension.AddDevice)
         {
@@ -313,7 +492,7 @@ static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
             PDEVICE_OBJECT pdev_obj = NULL;
             UCHAR pdo_info[2 * PATH_MAX + 3 + MAX_DEVICE_ID_LEN];
             data_size_t reply_size = 0;
-            HANDLE events[2] = {stop_event, NULL};
+            HANDLE events[2] = {loop_event, NULL};
             DWORD wait_ret;
 
             while (!reply_size)
@@ -381,25 +560,64 @@ static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
                 ret = STATUS_UNSUCCESSFUL;
         }
 
-        wine_ntoskrnl_main_loop( stop_event );
-    }
-    else WINE_TRACE( "driver %s failed to load\n", wine_dbgstr_w(driver_name) );
+        wine_ntoskrnl_main_loop( loop_event );
+        /* Stop service if wine_ntoskrnl_main_loop exits */
+        SetEvent( loop_event );
 stop:
-    status.dwCurrentState     = SERVICE_STOPPED;
-    status.dwControlsAccepted = 0;
-    SetServiceStatus( service_handle, &status );
-    WINE_TRACE( "service %s stopped\n", wine_dbgstr_w(driver_name) );
+        CloseHandle( loop_event );
+        unload_driver( module, &driver_obj );
+        HeapFree( GetProcessHeap(), 0, drv_name );
+        HeapFree( GetProcessHeap(), 0, event_name );
+    }
+    return 0;
 }
 
-int wmain( int argc, WCHAR *argv[] )
+static int driver_process(void)
 {
-    SERVICE_TABLE_ENTRYW service_table[2];
+    HANDLE pipe, winedevice_mutex, thread;
 
-    if (!(driver_name = argv[1]))
+    __wine_make_process_system();
+    winedevice_mutex = CreateMutexW( NULL, TRUE, winedevice_mutexW );
+    if (GetLastError() == ERROR_ALREADY_EXISTS)
     {
-        WINE_ERR( "missing device name, winedevice isn't supposed to be run manually\n" );
+        CloseHandle( winedevice_mutex );
         return 1;
     }
+    for (;;)
+    {
+        pipe = CreateNamedPipeW( pipe_nameW, PIPE_ACCESS_DUPLEX,
+                PIPE_TYPE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
+                256, 256, 10000, NULL );
+        if (pipe == INVALID_HANDLE_VALUE)
+        {
+            WINE_ERR( "failed to create pipe\n" );
+            continue;
+        }
+        if (!ConnectNamedPipe( pipe, NULL ) &&
+            GetLastError() != ERROR_PIPE_CONNECTED)
+        {
+            CloseHandle( pipe );
+            continue;
+        }
+
+        thread = CreateThread( NULL, 0, driver_thread, pipe, 0, NULL );
+        if (!thread)
+        {
+            WINE_ERR( "failed to create thread\n" );
+            DisconnectNamedPipe( pipe );
+            CloseHandle( pipe );
+            continue;
+        }
+        CloseHandle( thread );
+    }
+}
+
+int wmain( int argc, WCHAR *argv[] )
+{
+    SERVICE_TABLE_ENTRYW service_table[2];
+
+    if (!argv[1]) return driver_process();
+    driver_name = argv[1];
 
     service_table[0].lpServiceName = argv[1];
     service_table[0].lpServiceProc = ServiceMain;
-- 
1.6.0.2.GIT



Подробная информация о списке рассылки Wine-patches