From: Matteo Bruni mbruni@codeweavers.com
--- dlls/ntoskrnl.exe/tests/driver.c | 32 ++++++ dlls/ntoskrnl.exe/tests/driver.h | 2 + dlls/ntoskrnl.exe/tests/ntoskrnl.c | 178 ++++++++++++++++++++++++++++- 3 files changed, 211 insertions(+), 1 deletion(-)
diff --git a/dlls/ntoskrnl.exe/tests/driver.c b/dlls/ntoskrnl.exe/tests/driver.c index dda8806ba74..326af858260 100644 --- a/dlls/ntoskrnl.exe/tests/driver.c +++ b/dlls/ntoskrnl.exe/tests/driver.c @@ -58,6 +58,9 @@ static int kmemcmp( const void *ptr1, const void *ptr2, size_t n ) static DRIVER_OBJECT *driver_obj; static DEVICE_OBJECT *lower_device, *upper_device;
+static IRP *queued_async_irps[2]; +static unsigned int queued_async_count; + static POBJECT_TYPE *pExEventObjectType, *pIoFileObjectType, *pPsThreadType, *pIoDriverObjectType; static PEPROCESS *pPsInitialSystemProcess; static void *create_caller_thread; @@ -2682,10 +2685,17 @@ static NTSTATUS WINAPI driver_Create(DEVICE_OBJECT *device, IRP *irp) return STATUS_SUCCESS; }
+static void cancel_complete(IRP *irp) +{ + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(irp, IO_NO_INCREMENT); +} + static NTSTATUS WINAPI driver_IoControl(DEVICE_OBJECT *device, IRP *irp) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); NTSTATUS status = STATUS_NOT_SUPPORTED; + unsigned int i;
switch (stack->Parameters.DeviceIoControl.IoControlCode) { @@ -2699,6 +2709,7 @@ static NTSTATUS WINAPI driver_IoControl(DEVICE_OBJECT *device, IRP *irp) status = test_load_driver_ioctl(irp, stack, &irp->IoStatus.Information); break; case IOCTL_WINETEST_RESET_CANCEL: + ok(!queued_async_count, "expected no queued asyncs left\n"); cancel_cnt = 0; status = STATUS_SUCCESS; break; @@ -2706,6 +2717,27 @@ static NTSTATUS WINAPI driver_IoControl(DEVICE_OBJECT *device, IRP *irp) IoSetCancelRoutine(irp, cancel_ioctl_irp); IoMarkIrpPending(irp); return STATUS_PENDING; + case IOCTL_WINETEST_QUEUE_ASYNC: + if (queued_async_count >= ARRAY_SIZE(queued_async_irps)) + { + ok(FALSE, "unexpected queued_async_count %u\n", queued_async_count); + status = ERROR_TOO_MANY_CMDS; + break; + } + queued_async_irps[queued_async_count++] = irp; + IoMarkIrpPending(irp); + return STATUS_PENDING; + case IOCTL_WINETEST_COMPLETE_ASYNC: + /* complete the pending IRPs gathered from + * IOCTL_WINETEST_QUEUE_ASYNC */ + for (i = 0; i < queued_async_count; i++) + { + cancel_complete(queued_async_irps[i]); + queued_async_irps[i] = NULL; + } + queued_async_count = 0; + status = STATUS_SUCCESS; + break; case IOCTL_WINETEST_GET_CANCEL_COUNT: status = get_dword(irp, stack, &irp->IoStatus.Information, cancel_cnt); break; diff --git a/dlls/ntoskrnl.exe/tests/driver.h b/dlls/ntoskrnl.exe/tests/driver.h index e22d756e69e..524ba6abeed 100644 --- a/dlls/ntoskrnl.exe/tests/driver.h +++ b/dlls/ntoskrnl.exe/tests/driver.h @@ -36,6 +36,8 @@ #define IOCTL_WINETEST_RETURN_STATUS_DIRECT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80a, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) #define IOCTL_WINETEST_RETURN_STATUS_NEITHER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80a, METHOD_NEITHER, FILE_ANY_ACCESS) #define IOCTL_WINETEST_COMPLETION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80c, METHOD_NEITHER, FILE_ANY_ACCESS) +#define IOCTL_WINETEST_QUEUE_ASYNC CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80d, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_WINETEST_COMPLETE_ASYNC CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80e, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_WINETEST_BUS_MAIN CTL_CODE(FILE_DEVICE_BUS_EXTENDER, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_WINETEST_BUS_REGISTER_IFACE CTL_CODE(FILE_DEVICE_BUS_EXTENDER, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index c1902ba5de9..1c59bb411ff 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -438,12 +438,95 @@ static void test_basic_ioctl(void) ok(!strcmp(buf, "Wine is no"), "got '%s'\n", buf); }
+static BOOL cancelioex_returned; + +enum cancel_test +{ + cancel, + cancelex, + cancelex_cancel, + cancel_cancelex, + cancelsync, +}; + +struct cancel_thread_ctx +{ + HANDLE file; + enum cancel_test test; +}; + +static DWORD WINAPI test_cancel_thread(void *param) +{ + struct cancel_thread_ctx *ctx = param; + DWORD cancel_cnt, size; + OVERLAPPED o, o2, o3; + BOOL res; + + res = DeviceIoControl(ctx->file, IOCTL_WINETEST_RESET_CANCEL, NULL, 0, NULL, 0, NULL, &o); + ok(res, "DeviceIoControl failed: %lu\n", GetLastError()); + + if (ctx->test != cancelsync) + { + res = DeviceIoControl(ctx->file, IOCTL_WINETEST_QUEUE_ASYNC, NULL, 0, NULL, 0, NULL, &o); + ok(!res && GetLastError() == ERROR_IO_PENDING, "DeviceIoControl failed: %lu\n", GetLastError()); + + res = DeviceIoControl(ctx->file, IOCTL_WINETEST_QUEUE_ASYNC, NULL, 0, NULL, 0, NULL, &o2); + ok(!res && GetLastError() == ERROR_IO_PENDING, "DeviceIoControl failed: %lu\n", GetLastError()); + } + + cancel_cnt = 0xdeadbeef; + res = DeviceIoControl(ctx->file, IOCTL_WINETEST_GET_CANCEL_COUNT, NULL, 0, &cancel_cnt, sizeof(cancel_cnt), NULL, &o3); + ok(res, "DeviceIoControl failed: %lu\n", GetLastError()); + ok(cancel_cnt == 0, "cancel_cnt = %lu\n", cancel_cnt); + + if (ctx->test == cancelsync) + { + /* This hangs */ + if (0) + { + res = DeviceIoControl(ctx->file, IOCTL_WINETEST_QUEUE_ASYNC, NULL, 0, NULL, 0, &size, NULL); + ok(res, "DeviceIoControl failed: %lu\n", GetLastError()); + } + /* This hangs if the cancel routine doesn't call IoCompleteRequest() + * (regardless of Status / Information values) */ + res = DeviceIoControl(ctx->file, IOCTL_WINETEST_TEST_CANCEL, NULL, 0, NULL, 0, &size, NULL); + ok(!res && GetLastError() == ERROR_OPERATION_ABORTED, "DeviceIoControl failed: %lu\n", GetLastError()); + } + + if (ctx->test == cancel || ctx->test == cancel_cancelex) + { + res = CancelIo(ctx->file); + ok(res, "CancelIo failed: %lu\n", GetLastError()); + } + else if (ctx->test == cancelex || ctx->test == cancelex_cancel) + { + res = CancelIoEx(ctx->file, NULL); + ok(res, "CancelIoEx failed: %lu\n", GetLastError()); + res = CancelIoEx(ctx->file, NULL); + ok(res, "CancelIoEx failed: %lu\n", GetLastError()); + cancelioex_returned = TRUE; + } + if (ctx->test == cancelex_cancel) + { + res = CancelIo(ctx->file); + ok(res, "CancelIo failed: %lu\n", GetLastError()); + } + else if (ctx->test == cancel_cancelex) + { + res = CancelIoEx(ctx->file, NULL); + ok(!res && GetLastError() == ERROR_NOT_FOUND, "CancelIoEx failed: %lu\n", GetLastError()); + } + return res; +} + static void test_overlapped(void) { OVERLAPPED overlapped, overlapped2, *o; + struct cancel_thread_ctx ctx; + HANDLE file, port, thread; DWORD cancel_cnt, size; - HANDLE file, port; ULONG_PTR key; + DWORD ret; BOOL res;
overlapped.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); @@ -502,6 +585,98 @@ static void test_overlapped(void) ok(cancel_cnt == 2, "cancel_cnt = %lu\n", cancel_cnt); }
+ /* test cancelling uncancellable requests - CancelIo */ + ctx.file = file; + ctx.test = cancel; + thread = CreateThread(NULL, 0, test_cancel_thread, &ctx, 0, NULL); + ret = WaitForSingleObject(thread, 100); + ok(ret == WAIT_TIMEOUT, "CancelIo didn't block\n"); + res = DeviceIoControl(file, IOCTL_WINETEST_COMPLETE_ASYNC, NULL, 0, NULL, 0, NULL, &overlapped); + ok(res, "DeviceIoControl failed: %lu\n", GetLastError()); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + + cancel_cnt = 0xdeadbeef; + res = DeviceIoControl(file, IOCTL_WINETEST_GET_CANCEL_COUNT, NULL, 0, &cancel_cnt, sizeof(cancel_cnt), NULL, &overlapped2); + ok(res, "DeviceIoControl failed: %lu\n", GetLastError()); + ok(cancel_cnt == 0, "cancel_cnt = %lu\n", cancel_cnt); + + /* test cancelling uncancellable requests - CancelIoEx */ + ctx.file = file; + ctx.test = cancelex; + cancelioex_returned = FALSE; + thread = CreateThread(NULL, 0, test_cancel_thread, &ctx, 0, NULL); + /* CancelIoEx() doesn't block, but the thread doesn't terminate either + * until the pending IOCTL is completed */ + ret = WaitForSingleObject(thread, 100); + todo_wine ok(ret == WAIT_TIMEOUT, "Thread already terminated\n"); + ok(cancelioex_returned, "CancelIoEx did block\n"); + res = DeviceIoControl(file, IOCTL_WINETEST_COMPLETE_ASYNC, NULL, 0, NULL, 0, NULL, &overlapped); + ok(res, "DeviceIoControl failed: %lu\n", GetLastError()); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + + cancel_cnt = 0xdeadbeef; + res = DeviceIoControl(file, IOCTL_WINETEST_GET_CANCEL_COUNT, NULL, 0, &cancel_cnt, sizeof(cancel_cnt), NULL, &overlapped2); + ok(res, "DeviceIoControl failed: %lu\n", GetLastError()); + ok(cancel_cnt == 0, "cancel_cnt = %lu\n", cancel_cnt); + + /* test cancelling uncancellable requests - CancelIoEx + CancelIo */ + ctx.file = file; + ctx.test = cancelex_cancel; + cancelioex_returned = FALSE; + thread = CreateThread(NULL, 0, test_cancel_thread, &ctx, 0, NULL); + ret = WaitForSingleObject(thread, 100); + ok(ret == WAIT_TIMEOUT, "Thread already terminated\n"); + ok(cancelioex_returned, "CancelIoEx did block\n"); + res = DeviceIoControl(file, IOCTL_WINETEST_COMPLETE_ASYNC, NULL, 0, NULL, 0, NULL, &overlapped); + ok(res, "DeviceIoControl failed: %lu\n", GetLastError()); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + + cancel_cnt = 0xdeadbeef; + res = DeviceIoControl(file, IOCTL_WINETEST_GET_CANCEL_COUNT, NULL, 0, &cancel_cnt, sizeof(cancel_cnt), NULL, &overlapped2); + ok(res, "DeviceIoControl failed: %lu\n", GetLastError()); + ok(cancel_cnt == 0, "cancel_cnt = %lu\n", cancel_cnt); + + /* test cancelling uncancellable requests - CancelIo + CancelIoEx */ + ctx.file = file; + ctx.test = cancel_cancelex; + thread = CreateThread(NULL, 0, test_cancel_thread, &ctx, 0, NULL); + ret = WaitForSingleObject(thread, 100); + ok(ret == WAIT_TIMEOUT, "CancelIo didn't block\n"); + res = CancelIoEx(file, NULL); + ok(res, "CancelIoEx failed: %lu\n", GetLastError()); + ret = WaitForSingleObject(thread, 100); + ok(ret == WAIT_TIMEOUT, "CancelIo didn't block\n"); + res = DeviceIoControl(file, IOCTL_WINETEST_COMPLETE_ASYNC, NULL, 0, NULL, 0, NULL, &overlapped); + ok(res, "DeviceIoControl failed: %lu\n", GetLastError()); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + + cancel_cnt = 0xdeadbeef; + res = DeviceIoControl(file, IOCTL_WINETEST_GET_CANCEL_COUNT, NULL, 0, &cancel_cnt, sizeof(cancel_cnt), NULL, &overlapped2); + ok(res, "DeviceIoControl failed: %lu\n", GetLastError()); + ok(cancel_cnt == 0, "cancel_cnt = %lu\n", cancel_cnt); + + /* test cancelling uncancellable requests - CancelSynchronousIo */ + ctx.file = device; + ctx.test = cancelsync; + thread = CreateThread(NULL, 0, test_cancel_thread, &ctx, 0, NULL); + WaitForSingleObject(thread, 100); + ok(ret == WAIT_TIMEOUT, "Synchronous DeviceIoControl() already returned\n"); + res = CancelSynchronousIo(thread); + ok(res, "CancelSynchronousIo failed: %lu\n", GetLastError()); + ret = WaitForSingleObject(thread, INFINITE); + ok(ret == WAIT_OBJECT_0, "CancelIoEx did block\n"); + CloseHandle(thread); + + cancel_cnt = 0xdeadbeef; + res = DeviceIoControl(device, IOCTL_WINETEST_GET_CANCEL_COUNT, NULL, 0, &cancel_cnt, sizeof(cancel_cnt), &size, NULL); + ok(res, "DeviceIoControl failed: %lu\n", GetLastError()); + ok(cancel_cnt == 1, "cancel_cnt = %lu\n", cancel_cnt); + + /* test completion port */ port = CreateIoCompletionPort(file, NULL, 0xdeadbeef, 0); ok(port != NULL, "CreateIoCompletionPort failed, error %lu\n", GetLastError()); res = GetQueuedCompletionStatus(port, &size, &key, &o, 0); @@ -1265,6 +1440,7 @@ static void test_driver_netio(struct testsign_context *ctx) hthread = CreateThread(NULL, 0, wsk_test_thread, NULL, 0, NULL); main_test(); WaitForSingleObject(hthread, INFINITE); + CloseHandle(hthread);
CloseHandle(device);