From 5f10c86d5bbd20466091a7a54424ad630a8d3812 Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Thu, 2 May 2019 13:21:19 +0200 Subject: [PATCH] ntoskrnl.exe: Implement IoCancelIrp. Signed-off-by: Jacek Caban Signed-off-by: Alexandre Julliard --- dlls/ntoskrnl.exe/ntoskrnl.c | 25 ++++++ dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 2 +- dlls/ntoskrnl.exe/tests/driver.c | 120 ++++++++++++++++++++++++++++ include/ddk/wdm.h | 4 + 4 files changed, 150 insertions(+), 1 deletion(-) diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c index 63f66477a66..6ac2a3e50bf 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/ntoskrnl.c @@ -2321,6 +2321,31 @@ void WINAPI IofCompleteRequest( IRP *irp, UCHAR priority_boost ) } +/*********************************************************************** + * IoCancelIrp (NTOSKRNL.EXE.@) + */ +BOOLEAN WINAPI IoCancelIrp( IRP *irp ) +{ + PDRIVER_CANCEL cancel_routine; + KIRQL irql; + + TRACE( "(%p)\n", irp ); + + IoAcquireCancelSpinLock( &irql ); + irp->Cancel = TRUE; + if (!(cancel_routine = IoSetCancelRoutine( irp, NULL ))) + { + IoReleaseCancelSpinLock( irp->CancelIrql ); + return FALSE; + } + + /* CancelRoutine is responsible for calling IoReleaseCancelSpinLock */ + irp->CancelIrql = irql; + cancel_routine( IoGetCurrentIrpStackLocation(irp)->DeviceObject, irp ); + return TRUE; +} + + /*********************************************************************** * InterlockedCompareExchange (NTOSKRNL.EXE.@) */ diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec index 20999d29157..af43b2a07b0 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec +++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec @@ -336,7 +336,7 @@ @ stdcall IoBuildSynchronousFsdRequest(long ptr ptr long ptr ptr ptr) @ stdcall IoCallDriver(ptr ptr) @ stub IoCancelFileOpen -@ stub IoCancelIrp +@ stdcall IoCancelIrp(ptr) @ stub IoCheckDesiredAccess @ stub IoCheckEaBufferValidity @ stub IoCheckFunctionAccess diff --git a/dlls/ntoskrnl.exe/tests/driver.c b/dlls/ntoskrnl.exe/tests/driver.c index 5e6d5cacb22..c2ef8903065 100644 --- a/dlls/ntoskrnl.exe/tests/driver.c +++ b/dlls/ntoskrnl.exe/tests/driver.c @@ -758,6 +758,125 @@ static void test_call_driver(DEVICE_OBJECT *device) ok(status == STATUS_SUCCESS, "got %#x\n", status); } +static int cancel_cnt; + +static void WINAPI cancel_irp(DEVICE_OBJECT *device, IRP *irp) +{ + IoReleaseCancelSpinLock(irp->CancelIrql); + ok(irp->Cancel == TRUE, "Cancel = %x\n", irp->Cancel); + ok(!irp->CancelRoutine, "CancelRoutine = %p\n", irp->CancelRoutine); + irp->IoStatus.Status = STATUS_CANCELLED; + irp->IoStatus.Information = 0; + cancel_cnt++; +} + +static NTSTATUS WINAPI cancel_test_completion(DEVICE_OBJECT *device, IRP *irp, void *context) +{ + ok(cancel_cnt == 1, "cancel_cnt = %d\n", cancel_cnt); + *(BOOL*)context = TRUE; + return STATUS_SUCCESS; +} + +static void test_cancel_irp(DEVICE_OBJECT *device) +{ + IO_STACK_LOCATION *irpsp; + IO_STATUS_BLOCK iosb; + IRP *irp = NULL; + BOOL completion_called; + BOOLEAN r; + NTSTATUS status; + + /* cancel IRP with no cancel routine */ + irp = IoBuildAsynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS, device, NULL, 0, NULL, &iosb); + + r = IoCancelIrp(irp); + ok(!r, "IoCancelIrp returned %x\n", r); + ok(irp->Cancel == TRUE, "Cancel = %x\n", irp->Cancel); + + r = IoCancelIrp(irp); + ok(!r, "IoCancelIrp returned %x\n", r); + IoFreeIrp(irp); + + irp = IoBuildAsynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS, device, NULL, 0, NULL, &iosb); + + /* cancel IRP with cancel routine */ + status = IoCallDriver(device, irp); + ok(status == STATUS_PENDING, "IoCallDriver returned %#x\n", status); + + ok(irp->CurrentLocation == 1, "CurrentLocation = %u\n", irp->CurrentLocation); + irpsp = IoGetCurrentIrpStackLocation(irp); + ok(irpsp->DeviceObject == device, "DeviceObject = %u\n", irpsp->DeviceObject); + + IoSetCancelRoutine(irp, cancel_irp); + cancel_cnt = 0; + r = IoCancelIrp(irp); + ok(r == TRUE, "IoCancelIrp returned %x\n", r); + ok(irp->Cancel == TRUE, "Cancel = %x\n", irp->Cancel); + ok(cancel_cnt == 1, "cancel_cnt = %d\n", cancel_cnt); + + cancel_cnt = 0; + r = IoCancelIrp(irp); + ok(!r, "IoCancelIrp returned %x\n", r); + ok(irp->Cancel == TRUE, "Cancel = %x\n", irp->Cancel); + ok(!cancel_cnt, "cancel_cnt = %d\n", cancel_cnt); + + IoCompleteRequest(irp, IO_NO_INCREMENT); + + /* cancel IRP with cancel and completion routines with no SL_INVOKE_ON_ERROR */ + irp = IoBuildAsynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS, device, NULL, 0, NULL, &iosb); + IoSetCompletionRoutine(irp, cancel_test_completion, &completion_called, TRUE, FALSE, TRUE); + + status = IoCallDriver(device, irp); + ok(status == STATUS_PENDING, "IoCallDriver returned %#x\n", status); + + IoSetCancelRoutine(irp, cancel_irp); + cancel_cnt = 0; + r = IoCancelIrp(irp); + ok(r == TRUE, "IoCancelIrp returned %x\n", r); + ok(cancel_cnt == 1, "cancel_cnt = %d\n", cancel_cnt); + ok(irp->Cancel == TRUE, "Cancel = %x\n", irp->Cancel); + + completion_called = FALSE; + IoCompleteRequest(irp, IO_NO_INCREMENT); + ok(completion_called, "completion not called\n"); + + /* cancel IRP with cancel and completion routines with no SL_INVOKE_ON_CANCEL flag */ + irp = IoBuildAsynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS, device, NULL, 0, NULL, &iosb); + IoSetCompletionRoutine(irp, cancel_test_completion, &completion_called, TRUE, TRUE, FALSE); + + status = IoCallDriver(device, irp); + ok(status == STATUS_PENDING, "IoCallDriver returned %#x\n", status); + + IoSetCancelRoutine(irp, cancel_irp); + cancel_cnt = 0; + r = IoCancelIrp(irp); + ok(r == TRUE, "IoCancelIrp returned %x\n", r); + ok(irp->Cancel == TRUE, "Cancel = %x\n", irp->Cancel); + ok(cancel_cnt == 1, "cancel_cnt = %d\n", cancel_cnt); + + completion_called = FALSE; + IoCompleteRequest(irp, IO_NO_INCREMENT); + ok(completion_called, "completion not called\n"); + + /* cancel IRP with cancel and completion routines, but no SL_INVOKE_ON_ERROR nor SL_INVOKE_ON_CANCEL flag */ + irp = IoBuildAsynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS, device, NULL, 0, NULL, &iosb); + IoSetCompletionRoutine(irp, cancel_test_completion, &completion_called, TRUE, FALSE, FALSE); + + status = IoCallDriver(device, irp); + ok(status == STATUS_PENDING, "IoCallDriver returned %#x\n", status); + + IoSetCancelRoutine(irp, cancel_irp); + cancel_cnt = 0; + r = IoCancelIrp(irp); + ok(r == TRUE, "IoCancelIrp returned %x\n", r); + ok(irp->Cancel == TRUE, "Cancel = %x\n", irp->Cancel); + ok(cancel_cnt == 1, "cancel_cnt = %d\n", cancel_cnt); + + completion_called = FALSE; + IoCompleteRequest(irp, IO_NO_INCREMENT); + ok(!completion_called, "completion not called\n"); +} + static int callout_cnt; static void WINAPI callout(void *parameter) @@ -1294,6 +1413,7 @@ static void WINAPI main_test_task(DEVICE_OBJECT *device, void *context) test_current_thread(TRUE); test_call_driver(device); + test_cancel_irp(device); /* print process report */ if (winetest_debug) diff --git a/include/ddk/wdm.h b/include/ddk/wdm.h index 1f6bdfab857..6fb718a40df 100644 --- a/include/ddk/wdm.h +++ b/include/ddk/wdm.h @@ -1493,6 +1493,9 @@ NTSTATUS WINAPI ObCloseHandle(IN HANDLE handle); # endif #endif +#define IoSetCancelRoutine(irp, routine) \ + ((PDRIVER_CANCEL)InterlockedExchangePointer((void **)&(irp)->CancelRoutine, routine)) + static inline void IoSetCompletionRoutine(IRP *irp, PIO_COMPLETION_ROUTINE routine, void *context, BOOLEAN on_success, BOOLEAN on_error, BOOLEAN on_cancel) { @@ -1563,6 +1566,7 @@ PIRP WINAPI IoBuildAsynchronousFsdRequest(ULONG,DEVICE_OBJECT*,void*,ULONG, PIRP WINAPI IoBuildDeviceIoControlRequest(ULONG,DEVICE_OBJECT*,PVOID,ULONG,PVOID,ULONG,BOOLEAN,PKEVENT,IO_STATUS_BLOCK*); PIRP WINAPI IoBuildSynchronousFsdRequest(ULONG,DEVICE_OBJECT*,PVOID,ULONG,PLARGE_INTEGER,PKEVENT,IO_STATUS_BLOCK*); NTSTATUS WINAPI IoCallDriver(DEVICE_OBJECT*,IRP*); +BOOLEAN WINAPI IoCancelIrp(IRP*); VOID WINAPI IoCompleteRequest(IRP*,UCHAR); NTSTATUS WINAPI IoCreateDevice(DRIVER_OBJECT*,ULONG,UNICODE_STRING*,DEVICE_TYPE,ULONG,BOOLEAN,DEVICE_OBJECT**); NTSTATUS WINAPI IoCreateDriver(UNICODE_STRING*,PDRIVER_INITIALIZE);