[PATCH v2 2/5] ntoskrnl.exe/tests: Add some pending / remove tests.

Rémi Bernon rbernon at codeweavers.com
Wed Jun 30 03:19:07 CDT 2021


This shows that removing a device should send IRP_MN_SURPRISE_REMOVAL
only, and that it's still possible to use DeviceIoControl on already
opened handles.

IRP_MN_REMOVE_DEVICE should only be sent when all then opened handles
are closed.

Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
 dlls/ntoskrnl.exe/tests/driver.h     |  2 +
 dlls/ntoskrnl.exe/tests/driver_pnp.c | 99 ++++++++++++++++++++++++++--
 dlls/ntoskrnl.exe/tests/ntoskrnl.c   | 30 ++++++++-
 3 files changed, 125 insertions(+), 6 deletions(-)

diff --git a/dlls/ntoskrnl.exe/tests/driver.h b/dlls/ntoskrnl.exe/tests/driver.h
index 2c62baa0a61..455695ad36b 100644
--- a/dlls/ntoskrnl.exe/tests/driver.h
+++ b/dlls/ntoskrnl.exe/tests/driver.h
@@ -35,6 +35,8 @@
 #define IOCTL_WINETEST_RETURN_STATUS    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80a, METHOD_BUFFERED, FILE_ANY_ACCESS)
 #define IOCTL_WINETEST_MISMATCHED_STATUS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80b, METHOD_NEITHER, FILE_ANY_ACCESS)
 #define IOCTL_WINETEST_COMPLETION       CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80c, METHOD_NEITHER, FILE_ANY_ACCESS)
+#define IOCTL_WINETEST_MARK_PENDING     CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80d, METHOD_NEITHER, FILE_ANY_ACCESS)
+#define IOCTL_WINETEST_CHECK_REMOVED    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80e, METHOD_NEITHER, 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/driver_pnp.c b/dlls/ntoskrnl.exe/tests/driver_pnp.c
index 774c98bae89..98ca2ff7961 100644
--- a/dlls/ntoskrnl.exe/tests/driver_pnp.c
+++ b/dlls/ntoskrnl.exe/tests/driver_pnp.c
@@ -41,6 +41,56 @@ static UNICODE_STRING control_symlink, bus_symlink;
 static DRIVER_OBJECT *driver_obj;
 static DEVICE_OBJECT *bus_fdo, *bus_pdo;
 
+static DWORD remove_device_count;
+static DWORD surprise_removal_count;
+static DWORD query_remove_device_count;
+static DWORD cancel_remove_device_count;
+
+struct irp_queue
+{
+    KSPIN_LOCK lock;
+    LIST_ENTRY list;
+};
+
+static IRP *irp_queue_pop(struct irp_queue *queue)
+{
+    KIRQL irql;
+    IRP *irp;
+
+    KeAcquireSpinLock(&queue->lock, &irql);
+    if (IsListEmpty(&queue->list)) irp = NULL;
+    else irp = CONTAINING_RECORD(RemoveHeadList(&queue->list), IRP, Tail.Overlay.ListEntry);
+    KeReleaseSpinLock(&queue->lock, irql);
+
+    return irp;
+}
+
+static void irp_queue_push(struct irp_queue *queue, IRP *irp)
+{
+    KIRQL irql;
+
+    KeAcquireSpinLock(&queue->lock, &irql);
+    InsertTailList(&queue->list, &irp->Tail.Overlay.ListEntry);
+    KeReleaseSpinLock(&queue->lock, irql);
+}
+
+static void irp_queue_clear(struct irp_queue *queue)
+{
+    IRP *irp;
+
+    while ((irp = irp_queue_pop(queue)))
+    {
+        irp->IoStatus.Status = STATUS_DELETE_PENDING;
+        IoCompleteRequest(irp, IO_NO_INCREMENT);
+    }
+}
+
+static void irp_queue_init(struct irp_queue *queue)
+{
+    KeInitializeSpinLock(&queue->lock);
+    InitializeListHead(&queue->list);
+}
+
 struct device
 {
     struct list entry;
@@ -49,6 +99,7 @@ struct device
     BOOL removed;
     UNICODE_STRING child_symlink;
     DEVICE_POWER_STATE power_state;
+    struct irp_queue irp_queue;
 };
 
 static struct list device_list = LIST_INIT(device_list);
@@ -207,6 +258,8 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp)
             POWER_STATE state = {.DeviceState = PowerDeviceD0};
             NTSTATUS status;
 
+            irp_queue_init(&device->irp_queue);
+
             ok(!stack->Parameters.StartDevice.AllocatedResources, "expected no resources\n");
             ok(!stack->Parameters.StartDevice.AllocatedResourcesTranslated, "expected no translated resources\n");
 
@@ -223,6 +276,14 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp)
         }
 
         case IRP_MN_REMOVE_DEVICE:
+            /* should've been checked and reset by IOCTL_WINETEST_CHECK_REMOVED */
+            ok(remove_device_count == 0, "expected no IRP_MN_REMOVE_DEVICE\n");
+            todo_wine ok(surprise_removal_count == 0, "expected no IRP_MN_SURPRISE_REMOVAL\n");
+            ok(query_remove_device_count == 0, "expected no IRP_MN_QUERY_REMOVE_DEVICE\n");
+            ok(cancel_remove_device_count == 0, "expected no IRP_MN_CANCEL_REMOVE_DEVICE\n");
+
+            remove_device_count++;
+            irp_queue_clear(&device->irp_queue);
             if (device->removed)
             {
                 IoSetDeviceInterfaceState(&device->child_symlink, FALSE);
@@ -289,8 +350,20 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp)
             break;
         }
 
-        case IRP_MN_QUERY_REMOVE_DEVICE:
         case IRP_MN_SURPRISE_REMOVAL:
+            surprise_removal_count++;
+            irp_queue_clear(&device->irp_queue);
+            ret = STATUS_SUCCESS;
+            break;
+
+        case IRP_MN_QUERY_REMOVE_DEVICE:
+            query_remove_device_count++;
+            irp_queue_clear(&device->irp_queue);
+            ret = STATUS_SUCCESS;
+            break;
+
+        case IRP_MN_CANCEL_REMOVE_DEVICE:
+            cancel_remove_device_count++;
             ret = STATUS_SUCCESS;
             break;
     }
@@ -579,8 +652,10 @@ static NTSTATUS fdo_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG code)
     }
 }
 
-static NTSTATUS pdo_ioctl(struct device *device, IRP *irp, IO_STACK_LOCATION *stack, ULONG code)
+static NTSTATUS pdo_ioctl(DEVICE_OBJECT *device_obj, IRP *irp, IO_STACK_LOCATION *stack, ULONG code)
 {
+    struct device *device = device_obj->DeviceExtension;
+
     switch (code)
     {
         case IOCTL_WINETEST_CHILD_GET_ID:
@@ -590,6 +665,22 @@ static NTSTATUS pdo_ioctl(struct device *device, IRP *irp, IO_STACK_LOCATION *st
             irp->IoStatus.Information = sizeof(device->id);
             return STATUS_SUCCESS;
 
+        case IOCTL_WINETEST_MARK_PENDING:
+            IoMarkIrpPending(irp);
+            irp_queue_push(&device->irp_queue, irp);
+            return STATUS_PENDING;
+
+        case IOCTL_WINETEST_CHECK_REMOVED:
+            ok(remove_device_count == 0, "expected IRP_MN_REMOVE_DEVICE\n");
+            ok(surprise_removal_count == 1, "expected IRP_MN_SURPRISE_REMOVAL\n");
+            ok(query_remove_device_count == 0, "expected no IRP_MN_QUERY_REMOVE_DEVICE\n");
+            ok(cancel_remove_device_count == 0, "expected no IRP_MN_CANCEL_REMOVE_DEVICE\n");
+            remove_device_count = 0;
+            surprise_removal_count = 0;
+            query_remove_device_count = 0;
+            cancel_remove_device_count = 0;
+            return STATUS_SUCCESS;
+
         default:
             ok(0, "Unexpected ioctl %#x.\n", code);
             return STATUS_NOT_IMPLEMENTED;
@@ -605,10 +696,10 @@ static NTSTATUS WINAPI driver_ioctl(DEVICE_OBJECT *device, IRP *irp)
     if (device == bus_fdo)
         status = fdo_ioctl(irp, stack, code);
     else
-        status = pdo_ioctl(device->DeviceExtension, irp, stack, code);
+        status = pdo_ioctl(device, irp, stack, code);
 
     irp->IoStatus.Status = status;
-    IoCompleteRequest(irp, IO_NO_INCREMENT);
+    if (status != STATUS_PENDING) IoCompleteRequest(irp, IO_NO_INCREMENT);
     return status;
 }
 
diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c
index a3c2a303a59..71dbfe1a5e9 100644
--- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c
+++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c
@@ -1150,10 +1150,11 @@ static void test_pnp_devices(void)
     };
     HDEVNOTIFY notify_handle;
     DWORD size, type, dword;
+    HANDLE bus, child, tmp;
     OBJECT_ATTRIBUTES attr;
     UNICODE_STRING string;
+    OVERLAPPED ovl = {0};
     IO_STATUS_BLOCK io;
-    HANDLE bus, child;
     HDEVINFO set;
     HWND window;
     BOOL ret;
@@ -1349,6 +1350,14 @@ static void test_pnp_devices(void)
 
     CloseHandle(child);
 
+    ret = NtOpenFile(&child, SYNCHRONIZE, &attr, &io, 0, 0);
+    ok(!ret, "failed to open child: %#x\n", ret);
+
+    ret = DeviceIoControl(child, IOCTL_WINETEST_MARK_PENDING, NULL, 0, NULL, 0, &size, &ovl);
+    ok(!ret, "DeviceIoControl succeded\n");
+    ok(GetLastError() == ERROR_IO_PENDING, "got error %u\n", GetLastError());
+    ok(size == 0, "got size %u\n", size);
+
     id = 1;
     ret = DeviceIoControl(bus, IOCTL_WINETEST_BUS_REMOVE_CHILD, &id, sizeof(id), NULL, 0, &size, NULL);
     ok(ret, "got error %u\n", GetLastError());
@@ -1357,7 +1366,24 @@ static void test_pnp_devices(void)
     ok(got_child_arrival == 1, "got %u child arrival messages\n", got_child_arrival);
     ok(got_child_removal == 1, "got %u child removal messages\n", got_child_removal);
 
-    ret = NtOpenFile(&child, SYNCHRONIZE, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT);
+    ret = DeviceIoControl(child, IOCTL_WINETEST_CHECK_REMOVED, NULL, 0, NULL, 0, &size, NULL);
+    todo_wine ok(ret, "got error %u\n", GetLastError());
+
+    ret = NtOpenFile(&tmp, SYNCHRONIZE, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT);
+    todo_wine ok(ret == STATUS_NO_SUCH_DEVICE, "got %#x\n", ret);
+
+    ret = GetOverlappedResult(child, &ovl, &size, TRUE);
+    ok(!ret, "unexpected success.\n");
+    ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError());
+    ok(size == 0, "got size %u\n", size);
+
+    CloseHandle(child);
+
+    pump_messages();
+    ok(got_child_arrival == 1, "got %u child arrival messages\n", got_child_arrival);
+    ok(got_child_removal == 1, "got %u child removal messages\n", got_child_removal);
+
+    ret = NtOpenFile(&tmp, SYNCHRONIZE, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT);
     ok(ret == STATUS_OBJECT_NAME_NOT_FOUND, "got %#x\n", ret);
 
     CloseHandle(bus);
-- 
2.32.0




More information about the wine-devel mailing list