/* * ntoskrnl.exe testing framework * * Copyright 2020 Paul Gofman for Codeweavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" #include "winternl.h" #include "winioctl.h" #include "ddk/ntddk.h" #include "ddk/ntifs.h" #include "ddk/wdm.h" #include "ddk/wsk.h" #include "driver.h" #include "utils.h" static DRIVER_OBJECT *driver_obj; static DEVICE_OBJECT *lower_device, *upper_device; static unsigned int create_count, close_count; static const WCHAR driver_link[] = L"\\DosDevices\\WineTestDriver4"; static const WCHAR device_name[] = L"\\Device\\WineTestDriver4"; static const WCHAR upper_name[] = L"\\Device\\WineTestUpper4"; static FILE_OBJECT *last_created_file; static void *create_caller_thread; static PETHREAD create_irp_thread; static POBJECT_TYPE *pIoDriverObjectType; static WSK_CLIENT_NPI client_npi; static WSK_REGISTRATION registration; static WSK_PROVIDER_NPI provider_npi; static KEVENT irp_complete_event; static IRP *wsk_irp; static NTSTATUS WINAPI irp_completion_routine(DEVICE_OBJECT *reserved, IRP *irp, void *context) { KEVENT *event = context; KeSetEvent(event, 1, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } static void netio_init(void) { static const WSK_CLIENT_DISPATCH client_dispatch = { MAKE_WSK_VERSION(1, 0), 0, NULL }; NTSTATUS status; client_npi.Dispatch = &client_dispatch; status = WskRegister(&client_npi, ®istration); ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); status = WskCaptureProviderNPI(®istration, WSK_INFINITE_WAIT, &provider_npi); ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); ok(provider_npi.Dispatch->Version >= MAKE_WSK_VERSION(1, 0), "Got unexpected version %#x.\n", provider_npi.Dispatch->Version); ok(!!provider_npi.Client, "Got null WSK_CLIENT.\n"); KeInitializeEvent(&irp_complete_event, SynchronizationEvent, FALSE); wsk_irp = IoAllocateIrp(1, FALSE); } static void netio_uninit(void) { IoFreeIrp(wsk_irp); WskReleaseProviderNPI(®istration); WskDeregister(®istration); } static void test_wsk_get_address_info(void) { UNICODE_STRING node_name, service_name; ADDRINFOEXW *result, *addr_info; unsigned int count; NTSTATUS status; RtlInitUnicodeString(&service_name, L"12345"); wsk_irp->IoStatus.Status = 0xdeadbeef; wsk_irp->IoStatus.Information = 0xdeadbeef; status = provider_npi.Dispatch->WskGetAddressInfo(provider_npi.Client, &node_name, &service_name, NS_ALL, NULL, NULL, &result, NULL, NULL, NULL); ok(status == STATUS_INVALID_PARAMETER, "Got unexpected status %#x.\n", status); ok(wsk_irp->IoStatus.Status == 0xdeadbeef, "Got unexpected status %#x.\n", wsk_irp->IoStatus.Status); ok(wsk_irp->IoStatus.Information == 0xdeadbeef, "Got unexpected Information %#lx.\n", wsk_irp->IoStatus.Information); RtlInitUnicodeString(&node_name, L"dead.beef"); IoReuseIrp(wsk_irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine(wsk_irp, irp_completion_routine, &irp_complete_event, TRUE, TRUE, TRUE); wsk_irp->IoStatus.Status = 0xdeadbeef; wsk_irp->IoStatus.Information = 0xdeadbeef; status = provider_npi.Dispatch->WskGetAddressInfo(provider_npi.Client, &node_name, &service_name, NS_ALL, NULL, NULL, &result, NULL, NULL, wsk_irp); ok(status == STATUS_PENDING, "Got unexpected status %#x.\n", status); status = KeWaitForSingleObject(&irp_complete_event, Executive, KernelMode, FALSE, NULL); ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); ok(wsk_irp->IoStatus.Status == STATUS_NOT_FOUND || broken(wsk_irp->IoStatus.Status == STATUS_NO_MATCH) /* Win7 */, "Got unexpected status %#x.\n", wsk_irp->IoStatus.Status); ok(wsk_irp->IoStatus.Information == 0, "Got unexpected Information %#lx.\n", wsk_irp->IoStatus.Information); RtlInitUnicodeString(&node_name, L"127.0.0.1"); IoReuseIrp(wsk_irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine(wsk_irp, irp_completion_routine, &irp_complete_event, TRUE, TRUE, TRUE); wsk_irp->IoStatus.Status = 0xdeadbeef; wsk_irp->IoStatus.Information = 0xdeadbeef; status = provider_npi.Dispatch->WskGetAddressInfo(provider_npi.Client, &node_name, &service_name, NS_ALL, NULL, NULL, &result, NULL, NULL, wsk_irp); ok(status == STATUS_PENDING, "Got unexpected status %#x.\n", status); status = KeWaitForSingleObject(&irp_complete_event, Executive, KernelMode, FALSE, NULL); ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); ok(wsk_irp->IoStatus.Status == STATUS_SUCCESS, "Got unexpected status %#x.\n", wsk_irp->IoStatus.Status); ok(wsk_irp->IoStatus.Information == 0, "Got unexpected Information %#lx.\n", wsk_irp->IoStatus.Information); count = 0; addr_info = result; while (addr_info) { struct sockaddr_in *addr = (struct sockaddr_in *)addr_info->ai_addr; ok(addr_info->ai_addrlen == sizeof(*addr), "Got unexpected ai_addrlen %u.\n", addr_info->ai_addrlen); ok(addr->sin_family == AF_INET, "Got unexpected sin_family %u.\n", addr->sin_family); ok(ntohs(addr->sin_port) == 12345, "Got unexpected sin_port %u.\n", ntohs(addr->sin_port)); ok(ntohl(addr->sin_addr.s_addr) == 0x7f000001, "Got unexpected sin_addr %#x.\n", ntohl(addr->sin_addr.s_addr)); ++count; addr_info = addr_info->ai_next; } ok(count, "Got zero addr_info count.\n"); provider_npi.Dispatch->WskFreeAddressInfo(provider_npi.Client, result); } struct socket_context { }; #define TEST_BUFFER_LENGTH 256 static void test_wsk_listen_socket(void) { static const char test_receive_string[] = "Client test string 1."; const WSK_PROVIDER_LISTEN_DISPATCH *tcp_dispatch, *udp_dispatch; static const char test_send_string[] = "Server test string 1."; static const WSK_CLIENT_LISTEN_DISPATCH client_listen_dispatch; const WSK_PROVIDER_CONNECTION_DISPATCH *accept_dispatch; WSK_SOCKET *tcp_socket, *udp_socket, *accept_socket; struct socket_context context; WSK_BUF wsk_buf1, wsk_buf2; void *buffer1, *buffer2; struct sockaddr_in addr; LARGE_INTEGER timeout; MDL *mdl1, *mdl2; NTSTATUS status; KEVENT event; IRP *irp; irp = IoAllocateIrp(1, FALSE); KeInitializeEvent(&event, SynchronizationEvent, FALSE); buffer1 = ExAllocatePool(NonPagedPool, TEST_BUFFER_LENGTH); mdl1 = IoAllocateMdl(buffer1, TEST_BUFFER_LENGTH, FALSE, FALSE, NULL); MmBuildMdlForNonPagedPool(mdl1); buffer2 = ExAllocatePool(NonPagedPool, TEST_BUFFER_LENGTH); mdl2 = IoAllocateMdl(buffer2, TEST_BUFFER_LENGTH, FALSE, FALSE, NULL); MmBuildMdlForNonPagedPool(mdl2); wsk_buf1.Mdl = mdl1; wsk_buf1.Offset = 0; wsk_buf1.Length = TEST_BUFFER_LENGTH; wsk_buf2 = wsk_buf1; wsk_buf2.Mdl = mdl2; status = provider_npi.Dispatch->WskSocket(NULL, AF_INET, SOCK_STREAM, IPPROTO_TCP, WSK_FLAG_LISTEN_SOCKET, &context, &client_listen_dispatch, NULL, NULL, NULL, NULL); ok(status == STATUS_INVALID_PARAMETER, "Got unexpected status %#x.\n", status); IoReuseIrp(wsk_irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine(wsk_irp, irp_completion_routine, &irp_complete_event, TRUE, TRUE, TRUE); status = provider_npi.Dispatch->WskSocket(NULL, AF_INET, SOCK_STREAM, IPPROTO_TCP, WSK_FLAG_LISTEN_SOCKET, &context, &client_listen_dispatch, NULL, NULL, NULL, wsk_irp); ok(status == STATUS_INVALID_HANDLE, "Got unexpected status %#x.\n", status); IoReuseIrp(wsk_irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine(wsk_irp, irp_completion_routine, &irp_complete_event, TRUE, TRUE, TRUE); wsk_irp->IoStatus.Status = 0xdeadbeef; wsk_irp->IoStatus.Information = 0xdeadbeef; status = provider_npi.Dispatch->WskSocket(provider_npi.Client, AF_INET, SOCK_STREAM, IPPROTO_TCP, WSK_FLAG_LISTEN_SOCKET, &context, &client_listen_dispatch, NULL, NULL, NULL, wsk_irp); ok(status == STATUS_PENDING, "Got unexpected status %#x.\n", status); status = KeWaitForSingleObject(&irp_complete_event, Executive, KernelMode, FALSE, NULL); ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); ok(wsk_irp->IoStatus.Status == STATUS_SUCCESS, "Got unexpected status %#x.\n", wsk_irp->IoStatus.Status); ok(wsk_irp->IoStatus.Information, "Got zero Information.\n"); tcp_socket = (WSK_SOCKET *)wsk_irp->IoStatus.Information; tcp_dispatch = tcp_socket->Dispatch; IoReuseIrp(wsk_irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine(wsk_irp, irp_completion_routine, &irp_complete_event, TRUE, TRUE, TRUE); status = provider_npi.Dispatch->WskSocket(provider_npi.Client, AF_INET, SOCK_DGRAM, IPPROTO_UDP, WSK_FLAG_LISTEN_SOCKET, &context, &client_listen_dispatch, NULL, NULL, NULL, wsk_irp); ok(status == STATUS_PENDING, "Got unexpected status %#x.\n", status); status = KeWaitForSingleObject(&irp_complete_event, Executive, KernelMode, FALSE, NULL); ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); ok(wsk_irp->IoStatus.Status == STATUS_SUCCESS, "Got unexpected status %#x.\n", wsk_irp->IoStatus.Status); ok(wsk_irp->IoStatus.Information, "Got zero Information.\n"); udp_socket = (WSK_SOCKET *)wsk_irp->IoStatus.Information; udp_dispatch = udp_socket->Dispatch; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(SERVER_LISTEN_PORT); IoReuseIrp(wsk_irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine(wsk_irp, irp_completion_routine, &irp_complete_event, TRUE, TRUE, TRUE); wsk_irp->IoStatus.Status = 0xdeadbeef; wsk_irp->IoStatus.Information = 0xdeadbeef; status = udp_dispatch->WskBind(udp_socket, (SOCKADDR *)&addr, 0, wsk_irp); ok(status == STATUS_PENDING, "Got unexpected status %#x.\n", status); status = KeWaitForSingleObject(&irp_complete_event, Executive, KernelMode, FALSE, NULL); ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); ok(wsk_irp->IoStatus.Status == STATUS_NOT_IMPLEMENTED, "Got unexpected status %#x.\n", wsk_irp->IoStatus.Status); ok(!wsk_irp->IoStatus.Information, "Got unexpected Information %#lx.\n", wsk_irp->IoStatus.Information); IoReuseIrp(wsk_irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine(wsk_irp, irp_completion_routine, &irp_complete_event, TRUE, TRUE, TRUE); status = udp_dispatch->Basic.WskCloseSocket(udp_socket, wsk_irp); ok(status == STATUS_PENDING, "Got unexpected status %#x.\n", status); status = KeWaitForSingleObject(&irp_complete_event, Executive, KernelMode, FALSE, NULL); ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); ok(wsk_irp->IoStatus.Status == STATUS_SUCCESS, "Got unexpected status %#x.\n", wsk_irp->IoStatus.Status); IoReuseIrp(wsk_irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine(wsk_irp, irp_completion_routine, &irp_complete_event, TRUE, TRUE, TRUE); wsk_irp->IoStatus.Status = 0xdeadbeef; wsk_irp->IoStatus.Information = 0xdeadbeef; status = tcp_dispatch->WskBind(tcp_socket, (SOCKADDR *)&addr, 0, wsk_irp); ok(status == STATUS_PENDING, "Got unexpected status %#x.\n", status); status = KeWaitForSingleObject(&irp_complete_event, Executive, KernelMode, FALSE, NULL); ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); ok(wsk_irp->IoStatus.Status == STATUS_SUCCESS, "Got unexpected status %#x.\n", wsk_irp->IoStatus.Status); ok(!wsk_irp->IoStatus.Information, "Got unexpected Information %#lx.\n", wsk_irp->IoStatus.Information); timeout.QuadPart = -1000 * 10000; IoReuseIrp(wsk_irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine(wsk_irp, irp_completion_routine, &irp_complete_event, TRUE, TRUE, TRUE); status = tcp_dispatch->WskAccept(tcp_socket, 0, NULL, NULL, NULL, NULL, wsk_irp); ok(status == STATUS_PENDING, "Got unexpected status %#x.\n", status); if (0) { /* Queuing another WskAccept in parallel with different irp results in Windows hang. */ IoReuseIrp(irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine(irp, irp_completion_routine, &event, TRUE, TRUE, TRUE); status = tcp_dispatch->WskAccept(tcp_socket, 0, NULL, NULL, NULL, NULL, irp); ok(status == STATUS_PENDING, "Got unexpected status %#x.\n", status); } status = KeWaitForSingleObject(&irp_complete_event, Executive, KernelMode, FALSE, &timeout); ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); ok(wsk_irp->IoStatus.Status == STATUS_SUCCESS, "Got unexpected status %#x.\n", wsk_irp->IoStatus.Status); ok(wsk_irp->IoStatus.Information, "Got zero Information.\n"); if (status == STATUS_SUCCESS && wsk_irp->IoStatus.Status == STATUS_SUCCESS) { accept_socket = (WSK_SOCKET *)wsk_irp->IoStatus.Information; accept_dispatch = accept_socket->Dispatch; IoReuseIrp(irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine(irp, irp_completion_routine, &event, TRUE, TRUE, TRUE); status = accept_dispatch->WskReceive(accept_socket, &wsk_buf2, 0, irp); ok(status == STATUS_PENDING, "Got unexpected status %#x.\n", status); IoReuseIrp(wsk_irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine(wsk_irp, irp_completion_routine, &irp_complete_event, TRUE, TRUE, TRUE); strcpy(buffer1, test_send_string); /* Setting Length in WSK_BUF greater than MDL allocation size BSODs Windows. * wsk_buf1.Length = TEST_BUFFER_LENGTH * 2; */ status = accept_dispatch->WskSend(accept_socket, &wsk_buf1, 0, wsk_irp); ok(status == STATUS_PENDING, "Got unexpected status %#x.\n", status); status = KeWaitForSingleObject(&irp_complete_event, Executive, KernelMode, FALSE, &timeout); ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); ok(wsk_irp->IoStatus.Status == STATUS_SUCCESS, "Got unexpected status %#x.\n", wsk_irp->IoStatus.Status); ok(wsk_irp->IoStatus.Information == TEST_BUFFER_LENGTH, "Got unexpected status %#x.\n", wsk_irp->IoStatus.Status); status = KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, &timeout); ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); ok(irp->IoStatus.Status == STATUS_SUCCESS, "Got unexpected status %#x.\n", irp->IoStatus.Status); ok(irp->IoStatus.Information == sizeof(test_receive_string), "Got unexpected Information %#lx.\n", irp->IoStatus.Information); ok(!strcmp(buffer2, test_receive_string), "Received unexpected data.\n"); IoReuseIrp(wsk_irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine(wsk_irp, irp_completion_routine, &irp_complete_event, TRUE, TRUE, TRUE); status = accept_dispatch->Basic.WskCloseSocket(accept_socket, wsk_irp); ok(status == STATUS_PENDING, "Got unexpected status %#x.\n", status); status = KeWaitForSingleObject(&irp_complete_event, Executive, KernelMode, FALSE, NULL); ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); ok(wsk_irp->IoStatus.Status == STATUS_SUCCESS, "Got unexpected status %#x.\n", wsk_irp->IoStatus.Status); ok(!wsk_irp->IoStatus.Information, "Got unexpected Information %#lx.\n", wsk_irp->IoStatus.Information); } /* WskAccept to be aborted by WskCloseSocket(). */ IoReuseIrp(irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine(irp, irp_completion_routine, &event, TRUE, TRUE, TRUE); status = tcp_dispatch->WskAccept(tcp_socket, 0, NULL, NULL, NULL, NULL, irp); ok(status == STATUS_PENDING, "Got unexpected status %#x.\n", status); IoReuseIrp(wsk_irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine(wsk_irp, irp_completion_routine, &irp_complete_event, TRUE, TRUE, TRUE); wsk_irp->IoStatus.Status = 0xdeadbeef; wsk_irp->IoStatus.Information = 0xdeadbeef; status = tcp_dispatch->Basic.WskCloseSocket(tcp_socket, wsk_irp); ok(status == STATUS_PENDING, "Got unexpected status %#x.\n", status); status = KeWaitForSingleObject(&irp_complete_event, Executive, KernelMode, FALSE, NULL); ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); ok(wsk_irp->IoStatus.Status == STATUS_SUCCESS, "Got unexpected status %#x.\n", wsk_irp->IoStatus.Status); ok(!wsk_irp->IoStatus.Information, "Got unexpected Information %#lx.\n", wsk_irp->IoStatus.Information); status = KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, &timeout); ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); ok(irp->IoStatus.Status == STATUS_CANCELLED, "Got unexpected status %#x.\n", irp->IoStatus.Status); ok(!irp->IoStatus.Information, "Got unexpected Information %#lx.\n", irp->IoStatus.Information); IoFreeIrp(irp); IoFreeMdl(mdl1); IoFreeMdl(mdl2); ExFreePool(buffer1); ExFreePool(buffer2); } static NTSTATUS main_test(DEVICE_OBJECT *device, IRP *irp, IO_STACK_LOCATION *stack) { ULONG length = stack->Parameters.DeviceIoControl.OutputBufferLength; void *buffer = irp->AssociatedIrp.SystemBuffer; struct test_input *test_input = buffer; OBJECT_ATTRIBUTES attr = {0}; UNICODE_STRING pathU; IO_STATUS_BLOCK io; if (!buffer) return STATUS_ACCESS_VIOLATION; if (length < sizeof(failures)) return STATUS_BUFFER_TOO_SMALL; attr.Length = sizeof(attr); RtlInitUnicodeString(&pathU, test_input->path); running_under_wine = test_input->running_under_wine; winetest_debug = test_input->winetest_debug; winetest_report_success = test_input->winetest_report_success; attr.ObjectName = &pathU; attr.Attributes = OBJ_KERNEL_HANDLE; /* needed to be accessible from system threads */ ZwOpenFile(&okfile, FILE_APPEND_DATA | SYNCHRONIZE, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT); netio_init(); test_wsk_get_address_info(); test_wsk_listen_socket(); if (winetest_debug) { kprintf("%04x:ntoskrnl: %d tests executed (%d marked as todo, %d %s), %d skipped.\n", PsGetCurrentProcessId(), successes + failures + todo_successes + todo_failures, todo_successes, failures + todo_failures, (failures + todo_failures != 1) ? "failures" : "failure", skipped ); } ZwClose(okfile); *((LONG *)buffer) = failures; irp->IoStatus.Information = sizeof(failures); return STATUS_SUCCESS; } static NTSTATUS WINAPI driver_iocontrol(DEVICE_OBJECT *device, IRP *irp) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); NTSTATUS status = STATUS_NOT_SUPPORTED; switch (stack->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_WINETEST_MAIN_TEST: status = main_test(device, irp, stack); break; case IOCTL_WINETEST_DETACH: IoDetachDevice(lower_device); status = STATUS_SUCCESS; break; default: break; } if (status != STATUS_PENDING) { irp->IoStatus.Status = status; IoCompleteRequest(irp, IO_NO_INCREMENT); } return status; } static NTSTATUS WINAPI driver_create(DEVICE_OBJECT *device, IRP *irp) { IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); DWORD *context = ExAllocatePool(PagedPool, sizeof(*context)); last_created_file = irpsp->FileObject; ++create_count; if (context) *context = create_count; irpsp->FileObject->FsContext = context; create_caller_thread = KeGetCurrentThread(); create_irp_thread = irp->Tail.Overlay.Thread; irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } static NTSTATUS WINAPI driver_close(DEVICE_OBJECT *device, IRP *irp) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); netio_uninit(); ++close_count; if (stack->FileObject->FsContext) ExFreePool(stack->FileObject->FsContext); irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } static VOID WINAPI driver_unload(DRIVER_OBJECT *driver) { UNICODE_STRING linkW; DbgPrint("Unloading driver.\n"); RtlInitUnicodeString(&linkW, driver_link); IoDeleteSymbolicLink(&linkW); IoDeleteDevice(upper_device); IoDeleteDevice(lower_device); } NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, PUNICODE_STRING registry) { static const WCHAR IoDriverObjectTypeW[] = L"IoDriverObjectType"; static const WCHAR driver_nameW[] = L"\\Driver\\WineTestDriver4"; UNICODE_STRING nameW, linkW; NTSTATUS status; void *obj; DbgPrint("Loading driver.\n"); driver_obj = driver; driver->DriverUnload = driver_unload; driver->MajorFunction[IRP_MJ_CREATE] = driver_create; driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = driver_iocontrol; driver->MajorFunction[IRP_MJ_CLOSE] = driver_close; RtlInitUnicodeString(&nameW, IoDriverObjectTypeW); pIoDriverObjectType = MmGetSystemRoutineAddress(&nameW); RtlInitUnicodeString(&nameW, driver_nameW); if ((status = ObReferenceObjectByName(&nameW, 0, NULL, 0, *pIoDriverObjectType, KernelMode, NULL, &obj))) return status; if (obj != driver) { ObDereferenceObject(obj); return STATUS_UNSUCCESSFUL; } ObDereferenceObject(obj); RtlInitUnicodeString(&nameW, device_name); RtlInitUnicodeString(&linkW, driver_link); if (!(status = IoCreateDevice(driver, 0, &nameW, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &lower_device))) { status = IoCreateSymbolicLink(&linkW, &nameW); lower_device->Flags &= ~DO_DEVICE_INITIALIZING; } if (!status) { RtlInitUnicodeString(&nameW, upper_name); status = IoCreateDevice(driver, 0, &nameW, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &upper_device); } if (!status) { IoAttachDeviceToDeviceStack(upper_device, lower_device); upper_device->Flags &= ~DO_DEVICE_INITIALIZING; } return status; }