crypt32: Test CryptStringToBinary with weird Base64.
[wine.git] / programs / winedevice / device.c
blob44aa1a0f16a4d5c413983a89a9c22040a0ceb439
1 /*
2 * Service process to load a kernel driver
4 * Copyright 2007 Alexandre Julliard
5 * Copyright 2016 Sebastian Lackner
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "config.h"
23 #include "wine/port.h"
25 #include <stdarg.h>
27 #include "ntstatus.h"
28 #define WIN32_NO_STATUS
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winternl.h"
32 #include "winreg.h"
33 #include "winnls.h"
34 #include "winsvc.h"
35 #include "ddk/wdm.h"
36 #include "wine/rbtree.h"
37 #include "wine/svcctl.h"
38 #include "wine/unicode.h"
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(winedevice);
42 WINE_DECLARE_DEBUG_CHANNEL(relay);
44 extern NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event );
46 static const WCHAR winedeviceW[] = {'w','i','n','e','d','e','v','i','c','e',0};
47 static SERVICE_STATUS_HANDLE service_handle;
48 static PTP_CLEANUP_GROUP cleanup_group;
49 static SC_HANDLE manager_handle;
50 static BOOL shutdown_in_progress;
51 static HANDLE stop_event;
53 struct wine_driver
55 struct wine_rb_entry entry;
57 SERVICE_STATUS_HANDLE handle;
58 DRIVER_OBJECT *driver_obj;
59 WCHAR name[1];
62 static int wine_drivers_rb_compare( const void *key, const struct wine_rb_entry *entry )
64 const struct wine_driver *driver = WINE_RB_ENTRY_VALUE( entry, const struct wine_driver, entry );
65 return strcmpW( (const WCHAR *)key, driver->name );
68 static struct wine_rb_tree wine_drivers = { wine_drivers_rb_compare };
70 static CRITICAL_SECTION drivers_cs;
71 static CRITICAL_SECTION_DEBUG critsect_debug =
73 0, 0, &drivers_cs,
74 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
75 0, 0, { (DWORD_PTR)(__FILE__ ": drivers_cs") }
77 static CRITICAL_SECTION drivers_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
79 /* find the LDR_MODULE corresponding to the driver module */
80 static LDR_MODULE *find_ldr_module( HMODULE module )
82 LDR_MODULE *ldr;
83 ULONG_PTR magic;
85 LdrLockLoaderLock( 0, NULL, &magic );
86 if (LdrFindEntryForAddress( module, &ldr ))
88 WARN( "module not found for %p\n", module );
89 ldr = NULL;
91 LdrUnlockLoaderLock( 0, magic );
93 return ldr;
96 /* load the driver module file */
97 static HMODULE load_driver_module( const WCHAR *name )
99 IMAGE_NT_HEADERS *nt;
100 const IMAGE_IMPORT_DESCRIPTOR *imports;
101 SYSTEM_BASIC_INFORMATION info;
102 int i;
103 INT_PTR delta;
104 ULONG size;
105 HMODULE module = LoadLibraryW( name );
107 if (!module) return NULL;
108 nt = RtlImageNtHeader( module );
110 if (!(delta = (char *)module - (char *)nt->OptionalHeader.ImageBase)) return module;
112 /* the loader does not apply relocations to non page-aligned binaries or executables,
113 * we have to do it ourselves */
115 NtQuerySystemInformation( SystemBasicInformation, &info, sizeof(info), NULL );
116 if (nt->OptionalHeader.SectionAlignment < info.PageSize ||
117 !(nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
119 DWORD old;
120 IMAGE_BASE_RELOCATION *rel, *end;
122 if ((rel = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &size )))
124 WINE_TRACE( "%s: relocating from %p to %p\n",
125 wine_dbgstr_w(name), (char *)module - delta, module );
126 end = (IMAGE_BASE_RELOCATION *)((char *)rel + size);
127 while (rel < end && rel->SizeOfBlock)
129 void *page = (char *)module + rel->VirtualAddress;
130 VirtualProtect( page, info.PageSize, PAGE_EXECUTE_READWRITE, &old );
131 rel = LdrProcessRelocationBlock( page, (rel->SizeOfBlock - sizeof(*rel)) / sizeof(USHORT),
132 (USHORT *)(rel + 1), delta );
133 if (old != PAGE_EXECUTE_READWRITE) VirtualProtect( page, info.PageSize, old, &old );
134 if (!rel) goto error;
136 /* make sure we don't try again */
137 size = FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + nt->FileHeader.SizeOfOptionalHeader;
138 VirtualProtect( nt, size, PAGE_READWRITE, &old );
139 nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0;
140 VirtualProtect( nt, size, old, &old );
144 /* make sure imports are relocated too */
146 if ((imports = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size )))
148 for (i = 0; imports[i].Name && imports[i].FirstThunk; i++)
150 char *name = (char *)module + imports[i].Name;
151 WCHAR buffer[32], *p = buffer;
153 while (p < buffer + 32) if (!(*p++ = *name++)) break;
154 if (p <= buffer + 32) FreeLibrary( load_driver_module( buffer ) );
158 return module;
160 error:
161 FreeLibrary( module );
162 return NULL;
165 /* load the .sys module for a device driver */
166 static HMODULE load_driver( const WCHAR *driver_name, const UNICODE_STRING *keyname )
168 static const WCHAR driversW[] = {'\\','d','r','i','v','e','r','s','\\',0};
169 static const WCHAR systemrootW[] = {'\\','S','y','s','t','e','m','R','o','o','t','\\',0};
170 static const WCHAR postfixW[] = {'.','s','y','s',0};
171 static const WCHAR ntprefixW[] = {'\\','?','?','\\',0};
172 static const WCHAR ImagePathW[] = {'I','m','a','g','e','P','a','t','h',0};
173 HKEY driver_hkey;
174 HMODULE module;
175 LPWSTR path = NULL, str;
176 DWORD type, size;
178 if (RegOpenKeyW( HKEY_LOCAL_MACHINE, keyname->Buffer + 18 /* skip \registry\machine */, &driver_hkey ))
180 WINE_ERR( "cannot open key %s, err=%u\n", wine_dbgstr_w(keyname->Buffer), GetLastError() );
181 return NULL;
184 /* read the executable path from memory */
185 size = 0;
186 if (!RegQueryValueExW( driver_hkey, ImagePathW, NULL, &type, NULL, &size ))
188 str = HeapAlloc( GetProcessHeap(), 0, size );
189 if (!RegQueryValueExW( driver_hkey, ImagePathW, NULL, &type, (LPBYTE)str, &size ))
191 size = ExpandEnvironmentStringsW(str,NULL,0);
192 path = HeapAlloc(GetProcessHeap(),0,size*sizeof(WCHAR));
193 ExpandEnvironmentStringsW(str,path,size);
195 HeapFree( GetProcessHeap(), 0, str );
196 if (!path)
198 RegCloseKey( driver_hkey );
199 return NULL;
202 if (!strncmpiW( path, systemrootW, 12 ))
204 WCHAR buffer[MAX_PATH];
206 GetWindowsDirectoryW(buffer, MAX_PATH);
208 str = HeapAlloc(GetProcessHeap(), 0, (size -11 + strlenW(buffer))
209 * sizeof(WCHAR));
210 lstrcpyW(str, buffer);
211 lstrcatW(str, path + 11);
212 HeapFree( GetProcessHeap(), 0, path );
213 path = str;
215 else if (!strncmpW( path, ntprefixW, 4 ))
216 str = path + 4;
217 else
218 str = path;
220 else
222 /* default is to use the driver name + ".sys" */
223 WCHAR buffer[MAX_PATH];
224 GetSystemDirectoryW(buffer, MAX_PATH);
225 path = HeapAlloc(GetProcessHeap(),0,
226 (strlenW(buffer) + strlenW(driversW) + strlenW(driver_name) + strlenW(postfixW) + 1)
227 *sizeof(WCHAR));
228 lstrcpyW(path, buffer);
229 lstrcatW(path, driversW);
230 lstrcatW(path, driver_name);
231 lstrcatW(path, postfixW);
232 str = path;
234 RegCloseKey( driver_hkey );
236 WINE_TRACE( "loading driver %s\n", wine_dbgstr_w(str) );
238 module = load_driver_module( str );
239 HeapFree( GetProcessHeap(), 0, path );
240 return module;
243 /* call the driver init entry point */
244 static NTSTATUS WINAPI init_driver( DRIVER_OBJECT *driver_object, UNICODE_STRING *keyname )
246 unsigned int i;
247 NTSTATUS status;
248 const IMAGE_NT_HEADERS *nt;
249 const WCHAR *driver_name;
250 HMODULE module;
252 /* Retrieve driver name from the keyname */
253 driver_name = strrchrW( keyname->Buffer, '\\' );
254 driver_name++;
256 module = load_driver( driver_name, keyname );
257 if (!module)
258 return STATUS_DLL_INIT_FAILED;
260 driver_object->DriverSection = find_ldr_module( module );
262 nt = RtlImageNtHeader( module );
263 if (!nt->OptionalHeader.AddressOfEntryPoint) return STATUS_SUCCESS;
264 driver_object->DriverInit = (PDRIVER_INITIALIZE)((char *)module + nt->OptionalHeader.AddressOfEntryPoint);
266 if (WINE_TRACE_ON(relay))
267 WINE_DPRINTF( "%04x:Call driver init %p (obj=%p,str=%s)\n", GetCurrentThreadId(),
268 driver_object->DriverInit, driver_object, wine_dbgstr_w(keyname->Buffer) );
270 status = driver_object->DriverInit( driver_object, keyname );
272 if (WINE_TRACE_ON(relay))
273 WINE_DPRINTF( "%04x:Ret driver init %p (obj=%p,str=%s) retval=%08x\n", GetCurrentThreadId(),
274 driver_object->DriverInit, driver_object, wine_dbgstr_w(keyname->Buffer), status );
276 WINE_TRACE( "init done for %s obj %p\n", wine_dbgstr_w(driver_name), driver_object );
277 WINE_TRACE( "- DriverInit = %p\n", driver_object->DriverInit );
278 WINE_TRACE( "- DriverStartIo = %p\n", driver_object->DriverStartIo );
279 WINE_TRACE( "- DriverUnload = %p\n", driver_object->DriverUnload );
280 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
281 WINE_TRACE( "- MajorFunction[%d] = %p\n", i, driver_object->MajorFunction[i] );
283 return status;
286 /* helper function to update service status */
287 static void set_service_status( SERVICE_STATUS_HANDLE handle, DWORD state, DWORD accepted )
289 SERVICE_STATUS status;
290 status.dwServiceType = SERVICE_WIN32;
291 status.dwCurrentState = state;
292 status.dwControlsAccepted = accepted;
293 status.dwWin32ExitCode = 0;
294 status.dwServiceSpecificExitCode = 0;
295 status.dwCheckPoint = 0;
296 status.dwWaitHint = (state == SERVICE_START_PENDING) ? 10000 : 0;
297 SetServiceStatus( handle, &status );
300 static void WINAPI async_unload_driver( PTP_CALLBACK_INSTANCE instance, void *context )
302 struct wine_driver *driver = context;
303 DRIVER_OBJECT *driver_obj = driver->driver_obj;
304 LDR_MODULE *ldr;
306 if (WINE_TRACE_ON(relay))
307 WINE_DPRINTF( "%04x:Call driver unload %p (obj=%p)\n", GetCurrentThreadId(),
308 driver_obj->DriverUnload, driver_obj );
310 driver_obj->DriverUnload( driver_obj );
312 if (WINE_TRACE_ON(relay))
313 WINE_DPRINTF( "%04x:Ret driver unload %p (obj=%p)\n", GetCurrentThreadId(),
314 driver_obj->DriverUnload, driver_obj );
316 ldr = driver_obj->DriverSection;
317 FreeLibrary( ldr->BaseAddress );
318 IoDeleteDriver( driver_obj );
319 ObDereferenceObject( driver_obj );
321 set_service_status( driver->handle, SERVICE_STOPPED, 0 );
322 CloseServiceHandle( (void *)driver->handle );
323 HeapFree( GetProcessHeap(), 0, driver );
326 /* call the driver unload function */
327 static NTSTATUS unload_driver( struct wine_rb_entry *entry, BOOL destroy )
329 TP_CALLBACK_ENVIRON environment;
330 struct wine_driver *driver = WINE_RB_ENTRY_VALUE( entry, struct wine_driver, entry );
331 DRIVER_OBJECT *driver_obj = driver->driver_obj;
333 if (!driver_obj)
335 TRACE( "driver %s has not finished loading yet\n", wine_dbgstr_w(driver->name) );
336 return STATUS_UNSUCCESSFUL;
338 if (!driver_obj->DriverUnload)
340 TRACE( "driver %s does not support unloading\n", wine_dbgstr_w(driver->name) );
341 return STATUS_UNSUCCESSFUL;
344 TRACE( "stopping driver %s\n", wine_dbgstr_w(driver->name) );
345 set_service_status( driver->handle, SERVICE_STOP_PENDING, 0 );
347 if (destroy)
349 async_unload_driver( NULL, driver );
350 return STATUS_SUCCESS;
353 wine_rb_remove( &wine_drivers, &driver->entry );
355 memset( &environment, 0, sizeof(environment) );
356 environment.Version = 1;
357 environment.CleanupGroup = cleanup_group;
359 /* don't block the service control handler */
360 if (!TrySubmitThreadpoolCallback( async_unload_driver, driver, &environment ))
361 async_unload_driver( NULL, driver );
363 return STATUS_SUCCESS;
366 static void WINAPI async_create_driver( PTP_CALLBACK_INSTANCE instance, void *context )
368 static const WCHAR driverW[] = {'\\','D','r','i','v','e','r','\\',0};
369 struct wine_driver *driver = context;
370 DRIVER_OBJECT *driver_obj;
371 UNICODE_STRING drv_name;
372 NTSTATUS status;
373 WCHAR *str;
375 if (!(str = HeapAlloc( GetProcessHeap(), 0, sizeof(driverW) + strlenW(driver->name)*sizeof(WCHAR) )))
376 goto error;
378 lstrcpyW( str, driverW);
379 lstrcatW( str, driver->name );
380 RtlInitUnicodeString( &drv_name, str );
382 status = IoCreateDriver( &drv_name, init_driver );
383 if (status != STATUS_SUCCESS)
385 ERR( "failed to create driver %s: %08x\n", debugstr_w(driver->name), status );
386 RtlFreeUnicodeString( &drv_name );
387 goto error;
390 status = ObReferenceObjectByName( &drv_name, OBJ_CASE_INSENSITIVE, NULL,
391 0, NULL, KernelMode, NULL, (void **)&driver_obj );
392 RtlFreeUnicodeString( &drv_name );
393 if (status != STATUS_SUCCESS)
395 ERR( "failed to locate driver %s: %08x\n", debugstr_w(driver->name), status );
396 goto error;
399 EnterCriticalSection( &drivers_cs );
400 driver->driver_obj = driver_obj;
401 set_service_status( driver->handle, SERVICE_RUNNING,
402 SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN );
403 LeaveCriticalSection( &drivers_cs );
404 return;
406 error:
407 EnterCriticalSection( &drivers_cs );
408 wine_rb_remove( &wine_drivers, &driver->entry );
409 LeaveCriticalSection( &drivers_cs );
411 set_service_status( driver->handle, SERVICE_STOPPED, 0 );
412 CloseServiceHandle( (void *)driver->handle );
413 HeapFree( GetProcessHeap(), 0, driver );
416 /* load a driver and notify services.exe about the status change */
417 static NTSTATUS create_driver( const WCHAR *driver_name )
419 TP_CALLBACK_ENVIRON environment;
420 struct wine_driver *driver;
421 DWORD length;
423 length = FIELD_OFFSET( struct wine_driver, name[strlenW(driver_name) + 1] );
424 if (!(driver = HeapAlloc( GetProcessHeap(), 0, length )))
425 return STATUS_NO_MEMORY;
427 strcpyW( driver->name, driver_name );
428 driver->driver_obj = NULL;
430 if (!(driver->handle = (void *)OpenServiceW( manager_handle, driver_name, SERVICE_SET_STATUS )))
432 HeapFree( GetProcessHeap(), 0, driver );
433 return STATUS_UNSUCCESSFUL;
436 if (wine_rb_put( &wine_drivers, driver_name, &driver->entry ))
438 CloseServiceHandle( (void *)driver->handle );
439 HeapFree( GetProcessHeap(), 0, driver );
440 return STATUS_UNSUCCESSFUL;
443 TRACE( "starting driver %s\n", wine_dbgstr_w(driver_name) );
444 set_service_status( driver->handle, SERVICE_START_PENDING, 0 );
446 memset( &environment, 0, sizeof(environment) );
447 environment.Version = 1;
448 environment.CleanupGroup = cleanup_group;
450 /* don't block the service control handler */
451 if (!TrySubmitThreadpoolCallback( async_create_driver, driver, &environment ))
452 async_create_driver( NULL, driver );
454 return STATUS_SUCCESS;
457 static void wine_drivers_rb_destroy( struct wine_rb_entry *entry, void *context )
459 if (unload_driver( entry, TRUE ) != STATUS_SUCCESS)
461 struct wine_driver *driver = WINE_RB_ENTRY_VALUE( entry, struct wine_driver, entry );
462 ObDereferenceObject( driver->driver_obj );
463 CloseServiceHandle( (void *)driver->handle );
464 HeapFree( GetProcessHeap(), 0, driver );
468 static void WINAPI async_shutdown_drivers( PTP_CALLBACK_INSTANCE instance, void *context )
470 CloseThreadpoolCleanupGroupMembers( cleanup_group, FALSE, NULL );
472 EnterCriticalSection( &drivers_cs );
473 wine_rb_destroy( &wine_drivers, wine_drivers_rb_destroy, NULL );
474 LeaveCriticalSection( &drivers_cs );
476 SetEvent( stop_event );
479 static void shutdown_drivers( void )
481 if (shutdown_in_progress) return;
483 /* don't block the service control handler */
484 if (!TrySubmitThreadpoolCallback( async_shutdown_drivers, NULL, NULL ))
485 async_shutdown_drivers( NULL, NULL );
487 shutdown_in_progress = TRUE;
490 static DWORD device_handler( DWORD ctrl, const WCHAR *driver_name )
492 struct wine_rb_entry *entry;
493 DWORD result = NO_ERROR;
495 if (shutdown_in_progress)
496 return ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
498 EnterCriticalSection( &drivers_cs );
499 entry = wine_rb_get( &wine_drivers, driver_name );
501 switch (ctrl)
503 case SERVICE_CONTROL_START:
504 if (entry) break;
505 result = RtlNtStatusToDosError(create_driver( driver_name ));
506 break;
508 case SERVICE_CONTROL_STOP:
509 if (!entry) break;
510 result = RtlNtStatusToDosError(unload_driver( entry, FALSE ));
511 break;
513 default:
514 FIXME( "got driver ctrl %x for %s\n", ctrl, wine_dbgstr_w(driver_name) );
515 break;
517 LeaveCriticalSection( &drivers_cs );
518 return result;
521 static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context )
523 const WCHAR *service_group = context;
525 if (ctrl & SERVICE_CONTROL_FORWARD_FLAG)
527 if (!event_data) return ERROR_INVALID_PARAMETER;
528 return device_handler( ctrl & ~SERVICE_CONTROL_FORWARD_FLAG, (const WCHAR *)event_data );
531 switch (ctrl)
533 case SERVICE_CONTROL_STOP:
534 case SERVICE_CONTROL_SHUTDOWN:
535 TRACE( "shutting down %s\n", wine_dbgstr_w(service_group) );
536 set_service_status( service_handle, SERVICE_STOP_PENDING, 0 );
537 shutdown_drivers();
538 return NO_ERROR;
539 default:
540 FIXME( "got service ctrl %x for %s\n", ctrl, wine_dbgstr_w(service_group) );
541 set_service_status( service_handle, SERVICE_RUNNING,
542 SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN );
543 return NO_ERROR;
547 static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
549 const WCHAR *service_group = (argc >= 2) ? argv[1] : argv[0];
551 if (!(stop_event = CreateEventW( NULL, TRUE, FALSE, NULL )))
552 return;
553 if (!(cleanup_group = CreateThreadpoolCleanupGroup()))
554 return;
555 if (!(manager_handle = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT )))
556 return;
557 if (!(service_handle = RegisterServiceCtrlHandlerExW( winedeviceW, service_handler, (void *)service_group )))
558 return;
560 TRACE( "starting service group %s\n", wine_dbgstr_w(service_group) );
561 set_service_status( service_handle, SERVICE_RUNNING,
562 SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN );
564 wine_ntoskrnl_main_loop( stop_event );
566 TRACE( "service group %s stopped\n", wine_dbgstr_w(service_group) );
567 set_service_status( service_handle, SERVICE_STOPPED, 0 );
568 CloseServiceHandle( manager_handle );
569 CloseThreadpoolCleanupGroup( cleanup_group );
570 CloseHandle( stop_event );
573 int wmain( int argc, WCHAR *argv[] )
575 SERVICE_TABLE_ENTRYW service_table[2];
577 service_table[0].lpServiceName = (void *)winedeviceW;
578 service_table[0].lpServiceProc = ServiceMain;
579 service_table[1].lpServiceName = NULL;
580 service_table[1].lpServiceProc = NULL;
582 StartServiceCtrlDispatcherW( service_table );
583 return 0;