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@codeweavers.com --- dlls/ntoskrnl.exe/tests/driver.h | 2 + dlls/ntoskrnl.exe/tests/driver_pnp.c | 81 ++++++++++++++++++++++++++-- dlls/ntoskrnl.exe/tests/ntoskrnl.c | 30 ++++++++++- 3 files changed, 107 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..58dd32039cf 100644 --- a/dlls/ntoskrnl.exe/tests/driver_pnp.c +++ b/dlls/ntoskrnl.exe/tests/driver_pnp.c @@ -41,6 +41,11 @@ 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 device { struct list entry; @@ -51,6 +56,36 @@ struct device DEVICE_POWER_STATE power_state; };
+static IRP *pop_pending_irp(DEVICE_OBJECT *device) +{ + KDEVICE_QUEUE_ENTRY *entry; + IRP *irp; + + if (!(entry = KeRemoveDeviceQueue(&device->DeviceQueue))) irp = NULL; + else irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.DeviceQueueEntry); + + return irp; +} + +static NTSTATUS push_pending_irp(DEVICE_OBJECT *device, IRP *irp) +{ + do { IoMarkIrpPending(irp); } + while (!KeInsertDeviceQueue(&device->DeviceQueue, &irp->Tail.Overlay.DeviceQueueEntry)); + + return STATUS_PENDING; +} + +static void remove_pending_irps(DEVICE_OBJECT *device) +{ + IRP *irp; + + while ((irp = pop_pending_irp(device))) + { + irp->IoStatus.Status = STATUS_DEVICE_REMOVED; + IoCompleteRequest(irp, IO_NO_INCREMENT); + } +} + static struct list device_list = LIST_INIT(device_list);
static FAST_MUTEX driver_lock; @@ -207,6 +242,8 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp) POWER_STATE state = {.DeviceState = PowerDeviceD0}; NTSTATUS status;
+ KeInitializeDeviceQueue(&device_obj->DeviceQueue); + ok(!stack->Parameters.StartDevice.AllocatedResources, "expected no resources\n"); ok(!stack->Parameters.StartDevice.AllocatedResourcesTranslated, "expected no translated resources\n");
@@ -223,6 +260,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++; + remove_pending_irps(device_obj); if (device->removed) { IoSetDeviceInterfaceState(&device->child_symlink, FALSE); @@ -289,8 +334,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++; + remove_pending_irps(device_obj); + ret = STATUS_SUCCESS; + break; + + case IRP_MN_QUERY_REMOVE_DEVICE: + query_remove_device_count++; + remove_pending_irps(device_obj); + ret = STATUS_SUCCESS; + break; + + case IRP_MN_CANCEL_REMOVE_DEVICE: + cancel_remove_device_count++; ret = STATUS_SUCCESS; break; } @@ -579,8 +636,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 +649,20 @@ 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: + return push_pending_irp(device_obj, irp); + + 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 +678,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 432bc168259..8fdb6c75f99 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_DEVICE_REMOVED, "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);