[PATCH v2 2/4] ntoskrnl/tests: Add more tests for IRP status handling.
Zebediah Figura
zfigura at codeweavers.com
Thu Aug 26 20:54:10 CDT 2021
Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
---
dlls/ntoskrnl.exe/tests/driver.c | 99 +++---
dlls/ntoskrnl.exe/tests/driver.h | 40 ++-
dlls/ntoskrnl.exe/tests/driver_pnp.c | 6 +-
dlls/ntoskrnl.exe/tests/ntoskrnl.c | 439 ++++++++++++++++++++++-----
4 files changed, 454 insertions(+), 130 deletions(-)
diff --git a/dlls/ntoskrnl.exe/tests/driver.c b/dlls/ntoskrnl.exe/tests/driver.c
index ce74b9e9512..0d40053b0e6 100644
--- a/dlls/ntoskrnl.exe/tests/driver.c
+++ b/dlls/ntoskrnl.exe/tests/driver.c
@@ -2395,22 +2395,64 @@ static NTSTATUS get_fscontext(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *inf
return STATUS_SUCCESS;
}
-static NTSTATUS return_status(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info)
+static NTSTATUS return_status(IRP *irp, IO_STACK_LOCATION *stack, ULONG code)
{
- char *buffer = irp->AssociatedIrp.SystemBuffer;
- NTSTATUS ret;
+ ULONG input_length = stack->Parameters.DeviceIoControl.InputBufferLength;
+ ULONG output_length = stack->Parameters.DeviceIoControl.OutputBufferLength;
+ const struct return_status_params *input_buffer;
+ struct return_status_params params;
+ void *output_buffer;
- if (!buffer)
+ if (code == IOCTL_WINETEST_RETURN_STATUS_NEITHER)
+ {
+ input_buffer = stack->Parameters.DeviceIoControl.Type3InputBuffer;
+ output_buffer = irp->UserBuffer;
+ }
+ else if (code == IOCTL_WINETEST_RETURN_STATUS_DIRECT)
+ {
+ input_buffer = irp->AssociatedIrp.SystemBuffer;
+ output_buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority);
+ }
+ else
+ {
+ input_buffer = irp->AssociatedIrp.SystemBuffer;
+ output_buffer = irp->AssociatedIrp.SystemBuffer;
+ }
+
+ if (!input_buffer || !output_buffer)
+ {
+ irp->IoStatus.Status = STATUS_ACCESS_VIOLATION;
+ IoCompleteRequest(irp, IO_NO_INCREMENT);
return STATUS_ACCESS_VIOLATION;
+ }
- if (stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(DWORD)
- || stack->Parameters.DeviceIoControl.OutputBufferLength < 3)
+ if (input_length < sizeof(params) || output_length < 6)
+ {
+ irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
+ IoCompleteRequest(irp, IO_NO_INCREMENT);
return STATUS_BUFFER_TOO_SMALL;
+ }
- ret = *(DWORD *)irp->AssociatedIrp.SystemBuffer;
- memcpy(buffer, "ghi", 3);
- *info = 3;
- return ret;
+ params = *input_buffer;
+
+ if (params.ret_status == STATUS_PENDING && !params.pending)
+ {
+ /* this causes kernel hangs under certain conditions */
+ irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+ IoCompleteRequest(irp, IO_NO_INCREMENT);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (params.pending)
+ IoMarkIrpPending(irp);
+
+ /* intentionally report the wrong information (and status) */
+ memcpy(output_buffer, "ghijkl", 6);
+ irp->IoStatus.Information = 3;
+ irp->IoStatus.Status = params.iosb_status;
+ IoCompleteRequest(irp, IO_NO_INCREMENT);
+
+ return params.ret_status;
}
static NTSTATUS test_load_driver_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info)
@@ -2430,34 +2472,6 @@ static NTSTATUS test_load_driver_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG
return ZwUnloadDriver(&name);
}
-static NTSTATUS test_mismatched_status_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info)
-{
- ULONG length = stack->Parameters.DeviceIoControl.OutputBufferLength;
- char *buffer = irp->UserBuffer;
-
- if (!buffer)
- {
- irp->IoStatus.Status = STATUS_ACCESS_VIOLATION;
- IoCompleteRequest(irp, IO_NO_INCREMENT);
- return STATUS_ACCESS_VIOLATION;
- }
-
- if (length < sizeof(teststr))
- {
- irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
- IoCompleteRequest(irp, IO_NO_INCREMENT);
- return STATUS_BUFFER_TOO_SMALL;
- }
-
- memcpy(buffer, teststr, sizeof(teststr));
-
- /* This is deliberate; some broken drivers do this */
- *info = 0;
- irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
- IoCompleteRequest(irp, IO_NO_INCREMENT);
- return STATUS_SUCCESS;
-}
-
static NTSTATUS completion_ioctl(DEVICE_OBJECT *device, IRP *irp, IO_STACK_LOCATION *stack)
{
if (device == upper_device)
@@ -2564,15 +2578,14 @@ static NTSTATUS WINAPI driver_IoControl(DEVICE_OBJECT *device, IRP *irp)
case IOCTL_WINETEST_GET_FSCONTEXT:
status = get_fscontext(irp, stack, &irp->IoStatus.Information);
break;
- case IOCTL_WINETEST_RETURN_STATUS:
- status = return_status(irp, stack, &irp->IoStatus.Information);
- break;
+ case IOCTL_WINETEST_RETURN_STATUS_BUFFERED:
+ case IOCTL_WINETEST_RETURN_STATUS_DIRECT:
+ case IOCTL_WINETEST_RETURN_STATUS_NEITHER:
+ return return_status(irp, stack, stack->Parameters.DeviceIoControl.IoControlCode);
case IOCTL_WINETEST_DETACH:
IoDetachDevice(lower_device);
status = STATUS_SUCCESS;
break;
- case IOCTL_WINETEST_MISMATCHED_STATUS:
- return test_mismatched_status_ioctl(irp, stack, &irp->IoStatus.Information);
case IOCTL_WINETEST_COMPLETION:
return completion_ioctl(device, irp, stack);
default:
diff --git a/dlls/ntoskrnl.exe/tests/driver.h b/dlls/ntoskrnl.exe/tests/driver.h
index 8c41010c503..9126e3cf2bf 100644
--- a/dlls/ntoskrnl.exe/tests/driver.h
+++ b/dlls/ntoskrnl.exe/tests/driver.h
@@ -22,21 +22,20 @@
/* All custom IOCTLs need to have a function value >= 0x800. */
-#define IOCTL_WINETEST_BASIC_IOCTL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
-#define IOCTL_WINETEST_MAIN_TEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
-#define IOCTL_WINETEST_LOAD_DRIVER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
-#define IOCTL_WINETEST_RESET_CANCEL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)
-#define IOCTL_WINETEST_TEST_CANCEL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)
-#define IOCTL_WINETEST_GET_CANCEL_COUNT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS)
-#define IOCTL_WINETEST_DETACH CTL_CODE(FILE_DEVICE_UNKNOWN, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS)
-#define IOCTL_WINETEST_GET_CREATE_COUNT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x807, METHOD_BUFFERED, FILE_ANY_ACCESS)
-#define IOCTL_WINETEST_GET_CLOSE_COUNT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x808, METHOD_BUFFERED, FILE_ANY_ACCESS)
-#define IOCTL_WINETEST_GET_FSCONTEXT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS)
-#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_BASIC_IOCTL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_WINETEST_MAIN_TEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_WINETEST_LOAD_DRIVER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_WINETEST_RESET_CANCEL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_WINETEST_TEST_CANCEL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_WINETEST_GET_CANCEL_COUNT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_WINETEST_DETACH CTL_CODE(FILE_DEVICE_UNKNOWN, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_WINETEST_GET_CREATE_COUNT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x807, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_WINETEST_GET_CLOSE_COUNT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x808, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_WINETEST_GET_FSCONTEXT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_WINETEST_RETURN_STATUS_BUFFERED CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80a, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#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_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)
@@ -45,7 +44,9 @@
#define IOCTL_WINETEST_BUS_ADD_CHILD CTL_CODE(FILE_DEVICE_BUS_EXTENDER, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_WINETEST_BUS_REMOVE_CHILD CTL_CODE(FILE_DEVICE_BUS_EXTENDER, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS)
-#define IOCTL_WINETEST_CHILD_GET_ID CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_WINETEST_CHILD_GET_ID CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_WINETEST_CHILD_MARK_PENDING CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_NEITHER, FILE_ANY_ACCESS)
+#define IOCTL_WINETEST_CHILD_CHECK_REMOVED CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS)
static const char teststr[] = "Wine is not an emulator";
@@ -65,6 +66,13 @@ struct main_test_input
ULONG64 *modified_value;
};
+struct return_status_params
+{
+ NTSTATUS ret_status;
+ NTSTATUS iosb_status;
+ BOOL pending;
+};
+
static const GUID control_class = {0xdeadbeef, 0x29ef, 0x4538, {0xa5, 0xfd, 0xb6, 0x95, 0x73, 0xa3, 0x62, 0xc0}};
#define SERVER_LISTEN_PORT 9374
diff --git a/dlls/ntoskrnl.exe/tests/driver_pnp.c b/dlls/ntoskrnl.exe/tests/driver_pnp.c
index 98ca2ff7961..aa078ab0b2f 100644
--- a/dlls/ntoskrnl.exe/tests/driver_pnp.c
+++ b/dlls/ntoskrnl.exe/tests/driver_pnp.c
@@ -276,7 +276,7 @@ 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 */
+ /* should've been checked and reset by IOCTL_WINETEST_CHILD_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");
@@ -665,12 +665,12 @@ static NTSTATUS pdo_ioctl(DEVICE_OBJECT *device_obj, IRP *irp, IO_STACK_LOCATION
irp->IoStatus.Information = sizeof(device->id);
return STATUS_SUCCESS;
- case IOCTL_WINETEST_MARK_PENDING:
+ case IOCTL_WINETEST_CHILD_MARK_PENDING:
IoMarkIrpPending(irp);
irp_queue_push(&device->irp_queue, irp);
return STATUS_PENDING;
- case IOCTL_WINETEST_CHECK_REMOVED:
+ case IOCTL_WINETEST_CHILD_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");
diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c
index 774021c5ba2..b1fc73fb773 100644
--- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c
+++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c
@@ -430,18 +430,6 @@ static void test_basic_ioctl(void)
ok(!strcmp(buf, "Wine is no"), "got '%s'\n", buf);
}
-static void test_mismatched_status_ioctl(void)
-{
- DWORD written;
- char buf[32];
- BOOL res;
-
- res = DeviceIoControl(device, IOCTL_WINETEST_MISMATCHED_STATUS, NULL, 0, buf,
- sizeof(buf), &written, NULL);
- todo_wine ok(res, "DeviceIoControl failed: %u\n", GetLastError());
- todo_wine ok(!strcmp(buf, teststr), "got '%s'\n", buf);
-}
-
static void test_overlapped(void)
{
OVERLAPPED overlapped, overlapped2, *o;
@@ -631,72 +619,376 @@ static void test_file_handles(void)
ok(count == 3, "got %u\n", count);
}
-static void test_return_status(void)
+static unsigned int got_return_status_apc;
+
+static void WINAPI return_status_apc(void *apc_user, IO_STATUS_BLOCK *io, ULONG reserved)
{
- NTSTATUS status;
+ ++got_return_status_apc;
+ ok(apc_user == (void *)456, "got %p\n", apc_user);
+ ok(!reserved, "got reserved %#x\n", reserved);
+}
+
+static void do_return_status(ULONG ioctl, struct return_status_params *params)
+{
+ const char *expect_buffer;
+ LARGE_INTEGER zero = {0};
+ HANDLE file, port, event;
+ NTSTATUS expect_status;
+ ULONG_PTR key, value;
+ IO_STATUS_BLOCK io;
char buffer[7];
- DWORD ret_size;
+ DWORD size;
BOOL ret;
- strcpy(buffer, "abcdef");
- status = STATUS_SUCCESS;
- SetLastError(0xdeadbeef);
- ret = DeviceIoControl(device, IOCTL_WINETEST_RETURN_STATUS, &status,
- sizeof(status), buffer, sizeof(buffer), &ret_size, NULL);
- ok(ret, "ioctl failed\n");
- ok(GetLastError() == 0xdeadbeef, "got error %u\n", GetLastError());
- ok(!strcmp(buffer, "ghidef"), "got buffer %s\n", buffer);
- ok(ret_size == 3, "got size %u\n", ret_size);
+ if (params->ret_status == STATUS_PENDING && !params->pending)
+ {
+ /* this causes kernel hangs under certain conditions */
+ return;
+ }
+
+ event = CreateEventW(NULL, TRUE, FALSE, NULL);
+
+ if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
+ expect_buffer = "ghijkl";
+ else if (NT_ERROR(params->iosb_status))
+ expect_buffer = "abcdef";
+ else
+ expect_buffer = "ghidef";
+
+ /* Test the non-overlapped case. */
+
+ expect_status = (params->ret_status == STATUS_PENDING ? params->iosb_status : params->ret_status);
strcpy(buffer, "abcdef");
- status = STATUS_TIMEOUT;
- SetLastError(0xdeadbeef);
- ret = DeviceIoControl(device, IOCTL_WINETEST_RETURN_STATUS, &status,
- sizeof(status), buffer, sizeof(buffer), &ret_size, NULL);
- todo_wine ok(ret, "ioctl failed\n");
- todo_wine ok(GetLastError() == 0xdeadbeef, "got error %u\n", GetLastError());
- ok(!strcmp(buffer, "ghidef"), "got buffer %s\n", buffer);
- ok(ret_size == 3, "got size %u\n", ret_size);
+ size = 0xdeadf00d;
+ SetLastError(0xdeadf00d);
+ ret = DeviceIoControl(device, ioctl, params, sizeof(*params), buffer, sizeof(buffer), &size, NULL);
+ todo_wine_if (NT_SUCCESS(expect_status) != !params->iosb_status)
+ ok(ret == NT_SUCCESS(expect_status), "got %d\n", ret);
+ if (NT_SUCCESS(expect_status))
+ {
+ todo_wine_if (params->iosb_status)
+ ok(GetLastError() == 0xdeadf00d, "got error %u\n", GetLastError());
+ }
+ else
+ {
+ todo_wine_if (RtlNtStatusToDosError(expect_status) != RtlNtStatusToDosError(params->iosb_status)
+ || params->iosb_status == STATUS_PENDING)
+ ok(GetLastError() == RtlNtStatusToDosError(expect_status), "got error %u\n", GetLastError());
+ }
+ if (NT_ERROR(expect_status))
+ todo_wine ok(size == 0xdeadf00d, "got size %u\n", size);
+ else if (!NT_ERROR(params->iosb_status))
+ ok(size == 3, "got size %u\n", size);
+ /* size is garbage if !NT_ERROR(expect_status) && NT_ERROR(iosb_status) */
+ todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
+ ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
strcpy(buffer, "abcdef");
- status = 0x0eadbeef;
- SetLastError(0xdeadbeef);
- ret = DeviceIoControl(device, IOCTL_WINETEST_RETURN_STATUS, &status,
- sizeof(status), buffer, sizeof(buffer), &ret_size, NULL);
- todo_wine ok(ret, "ioctl failed\n");
- todo_wine ok(GetLastError() == 0xdeadbeef, "got error %u\n", GetLastError());
- ok(!strcmp(buffer, "ghidef"), "got buffer %s\n", buffer);
- ok(ret_size == 3, "got size %u\n", ret_size);
+ io.Status = 0xdeadf00d;
+ io.Information = 0xdeadf00d;
+ ret = NtDeviceIoControlFile(device, NULL, NULL, NULL, &io,
+ ioctl, params, sizeof(*params), buffer, sizeof(buffer));
+ todo_wine_if ((params->ret_status != params->iosb_status && params->ret_status != STATUS_PENDING)
+ || params->iosb_status == STATUS_PENDING)
+ ok(ret == expect_status, "got %#x\n", ret);
+ if (NT_ERROR(params->iosb_status))
+ {
+ todo_wine ok(io.Status == 0xdeadf00d, "got %#x\n", io.Status);
+ todo_wine ok(io.Information == 0xdeadf00d, "got size %Iu\n", io.Information);
+ }
+ else
+ {
+ todo_wine_if (params->iosb_status == STATUS_PENDING)
+ ok(io.Status == params->iosb_status, "got %#x\n", io.Status);
+ ok(io.Information == 3, "got size %Iu\n", io.Information);
+ }
+ todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
+ ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
+
+ /* Test the overlapped case. */
+
+ file = CreateFileA("\\\\.\\WineTestDriver", FILE_ALL_ACCESS,
+ 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+ ok(file != INVALID_HANDLE_VALUE, "failed to open device, error %u\n", GetLastError());
+ port = CreateIoCompletionPort(file, NULL, 123, 0);
+ ok(port != NULL, "failed to create port, error %u\n", GetLastError());
+
+ ret = WaitForSingleObject(file, 0);
+ todo_wine ok(!ret, "got %d\n", ret);
+
+ ResetEvent(event);
+ strcpy(buffer, "abcdef");
+ io.Status = 0xdeadf00d;
+ io.Information = 0xdeadf00d;
+ ret = NtDeviceIoControlFile(file, event, NULL, (void *)456, &io,
+ ioctl, params, sizeof(*params), buffer, sizeof(buffer));
+ todo_wine_if (params->ret_status != params->iosb_status || params->ret_status == STATUS_PENDING)
+ ok(ret == params->ret_status
+ || broken(NT_WARNING(params->ret_status) && ret == STATUS_PENDING), /* win10 */
+ "got %#x\n", ret);
+ if (!params->pending && NT_ERROR(params->iosb_status))
+ {
+ todo_wine ok(io.Status == 0xdeadf00d, "got %#x\n", io.Status);
+ todo_wine ok(io.Information == 0xdeadf00d, "got size %Iu\n", io.Information);
+ ret = WaitForSingleObject(event, 0);
+ todo_wine ok(ret == WAIT_TIMEOUT, "got %d\n", ret);
+ }
+ else
+ {
+ todo_wine_if (params->iosb_status == STATUS_PENDING)
+ ok(io.Status == params->iosb_status, "got %#x\n", io.Status);
+ ok(io.Information == 3, "got size %Iu\n", io.Information);
+ ret = WaitForSingleObject(event, 0);
+ ok(!ret, "got %d\n", ret);
+ }
+ todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
+ ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
+
+ ret = WaitForSingleObject(file, 0);
+ ok(ret == WAIT_TIMEOUT, "got %d\n", ret);
+
+ key = 0xdeadf00d;
+ value = 0xdeadf00d;
+ memset(&io, 0xcc, sizeof(io));
+ ret = NtRemoveIoCompletion(port, &key, &value, &io, &zero);
+ if (!params->pending && NT_ERROR(params->iosb_status))
+ {
+ todo_wine ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
+ }
+ else
+ {
+ ok(!ret, "got %#x\n", ret);
+ ok(key == 123, "got key %Iu\n", key);
+ ok(value == 456, "got value %Iu\n", value);
+ todo_wine_if (params->iosb_status == STATUS_PENDING)
+ ok(io.Status == params->iosb_status, "got iosb status %#x\n", io.Status);
+ ok(io.Information == 3, "got information %Iu\n", io.Information);
+ }
+
+ /* As above, but set the event first, to show that the event is always
+ * reset. */
+ ResetEvent(event);
+ strcpy(buffer, "abcdef");
+ io.Status = 0xdeadf00d;
+ io.Information = 0xdeadf00d;
+ ret = NtDeviceIoControlFile(file, event, NULL, NULL, &io,
+ ioctl, params, sizeof(*params), buffer, sizeof(buffer));
+ todo_wine_if (params->ret_status != params->iosb_status || params->ret_status == STATUS_PENDING)
+ ok(ret == params->ret_status
+ || broken(NT_WARNING(params->ret_status) && ret == STATUS_PENDING), /* win10 */
+ "got %#x\n", ret);
+ if (!params->pending && NT_ERROR(params->iosb_status))
+ {
+ todo_wine ok(io.Status == 0xdeadf00d, "got %#x\n", io.Status);
+ todo_wine ok(io.Information == 0xdeadf00d, "got size %Iu\n", io.Information);
+ ret = WaitForSingleObject(event, 0);
+ todo_wine ok(ret == WAIT_TIMEOUT, "got %d\n", ret);
+ }
+ else
+ {
+ todo_wine_if (params->iosb_status == STATUS_PENDING)
+ ok(io.Status == params->iosb_status, "got %#x\n", io.Status);
+ ok(io.Information == 3, "got size %Iu\n", io.Information);
+ ret = WaitForSingleObject(event, 0);
+ ok(!ret, "got %d\n", ret);
+ }
+ todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
+ ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
+
+ /* As above, but use the file handle instead of an event. */
+ ret = WaitForSingleObject(file, 0);
+ ok(ret == WAIT_TIMEOUT, "got %d\n", ret);
strcpy(buffer, "abcdef");
- status = 0x4eadbeef;
- SetLastError(0xdeadbeef);
- ret = DeviceIoControl(device, IOCTL_WINETEST_RETURN_STATUS, &status,
- sizeof(status), buffer, sizeof(buffer), &ret_size, NULL);
- todo_wine ok(ret, "ioctl failed\n");
- todo_wine ok(GetLastError() == 0xdeadbeef, "got error %u\n", GetLastError());
- ok(!strcmp(buffer, "ghidef"), "got buffer %s\n", buffer);
- ok(ret_size == 3, "got size %u\n", ret_size);
+ io.Status = 0xdeadf00d;
+ io.Information = 0xdeadf00d;
+ ret = NtDeviceIoControlFile(file, NULL, NULL, NULL, &io,
+ ioctl, params, sizeof(*params), buffer, sizeof(buffer));
+ todo_wine_if (params->ret_status != params->iosb_status || params->ret_status == STATUS_PENDING)
+ ok(ret == params->ret_status
+ || broken(NT_WARNING(params->ret_status) && ret == STATUS_PENDING), /* win10 */
+ "got %#x\n", ret);
+ if (!params->pending && NT_ERROR(params->iosb_status))
+ {
+ todo_wine ok(io.Status == 0xdeadf00d, "got %#x\n", io.Status);
+ todo_wine ok(io.Information == 0xdeadf00d, "got size %Iu\n", io.Information);
+ ret = WaitForSingleObject(file, 0);
+ todo_wine ok(ret == WAIT_TIMEOUT, "got %d\n", ret);
+ }
+ else
+ {
+ todo_wine_if (params->iosb_status == STATUS_PENDING)
+ ok(io.Status == params->iosb_status, "got %#x\n", io.Status);
+ ok(io.Information == 3, "got size %Iu\n", io.Information);
+ ret = WaitForSingleObject(file, 0);
+ ok(!ret, "got %d\n", ret);
+ }
+ todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
+ ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
+
+ /* Test FILE_SKIP_COMPLETION_PORT_ON_SUCCESS. */
+
+ if (pSetFileCompletionNotificationModes)
+ {
+ ret = pSetFileCompletionNotificationModes(file, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS);
+ ok(ret, "got error %u\n", GetLastError());
+
+ SetEvent(event);
+ strcpy(buffer, "abcdef");
+ io.Status = 0xdeadf00d;
+ io.Information = 0xdeadf00d;
+ ret = NtDeviceIoControlFile(file, event, NULL, (void *)456, &io,
+ ioctl, params, sizeof(*params), buffer, sizeof(buffer));
+ todo_wine_if (params->ret_status != params->iosb_status || params->ret_status == STATUS_PENDING)
+ ok(ret == params->ret_status
+ || broken(NT_WARNING(params->ret_status) && ret == STATUS_PENDING), /* win10 */
+ "got %#x\n", ret);
+ if (!params->pending && NT_ERROR(params->iosb_status))
+ {
+ todo_wine ok(io.Status == 0xdeadf00d, "got %#x\n", io.Status);
+ todo_wine ok(io.Information == 0xdeadf00d, "got size %Iu\n", io.Information);
+ ret = WaitForSingleObject(event, 0);
+ todo_wine ok(ret == WAIT_TIMEOUT, "got %d\n", ret);
+ }
+ else
+ {
+ todo_wine_if (params->iosb_status == STATUS_PENDING)
+ ok(io.Status == params->iosb_status, "got %#x\n", io.Status);
+ ok(io.Information == 3, "got size %Iu\n", io.Information);
+ ret = WaitForSingleObject(event, 0);
+ ok(!ret, "got %d\n", ret);
+ }
+ todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
+ ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
+
+ key = 0xdeadf00d;
+ value = 0xdeadf00d;
+ memset(&io, 0xcc, sizeof(io));
+ ret = NtRemoveIoCompletion(port, &key, &value, &io, &zero);
+ if (!params->pending)
+ {
+ /* Completion is skipped on non-pending NT_ERROR regardless of file
+ * options. Windows < 8 interprets
+ * FILE_SKIP_COMPLETION_PORT_ON_SUCCESS to mean that !NT_ERROR
+ * should also be skipped. Windows >= 8 restricts this to
+ * NT_SUCCESS, which has the weird effect that non-pending
+ * NT_WARNING does *not* skip completion. It's not clear whether
+ * this is a bug or not—it looks like one, but on the other hand it
+ * arguably follows the letter of the documentation more closely. */
+ ok(ret == STATUS_TIMEOUT || (NT_WARNING(params->iosb_status) && !ret), "got %#x\n", ret);
+ }
+ else
+ {
+ todo_wine ok(!ret, "got %#x\n", ret);
+ }
+ if (!ret)
+ {
+ ok(key == 123, "got key %Iu\n", key);
+ ok(value == 456, "got value %Iu\n", value);
+ ok(io.Status == params->iosb_status, "got iosb status %#x\n", io.Status);
+ ok(io.Information == 3, "got information %Iu\n", io.Information);
+ }
+ }
+
+ ret = CloseHandle(file);
+ ok(ret, "failed to close file, error %u\n", GetLastError());
+ ret = CloseHandle(port);
+ ok(ret, "failed to close port, error %u\n", GetLastError());
+
+ /* Test with an APC. */
+
+ got_return_status_apc = 0;
+
+ file = CreateFileA("\\\\.\\WineTestDriver", FILE_ALL_ACCESS,
+ 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+ ok(file != INVALID_HANDLE_VALUE, "failed to open device, error %u\n", GetLastError());
strcpy(buffer, "abcdef");
- status = 0x8eadbeef;
- SetLastError(0xdeadbeef);
- ret = DeviceIoControl(device, IOCTL_WINETEST_RETURN_STATUS, &status,
- sizeof(status), buffer, sizeof(buffer), &ret_size, NULL);
- ok(!ret, "ioctl succeeded\n");
- ok(GetLastError() == ERROR_MR_MID_NOT_FOUND, "got error %u\n", GetLastError());
- ok(!strcmp(buffer, "ghidef"), "got buffer %s\n", buffer);
- ok(ret_size == 3, "got size %u\n", ret_size);
+ io.Status = 0xdeadf00d;
+ io.Information = 0xdeadf00d;
+ ret = NtDeviceIoControlFile(file, NULL, return_status_apc, (void *)456, &io,
+ ioctl, params, sizeof(*params), buffer, sizeof(buffer));
+ todo_wine_if (params->ret_status != params->iosb_status || params->ret_status == STATUS_PENDING)
+ ok(ret == params->ret_status, "got %#x\n", ret);
+ if (!params->pending && NT_ERROR(params->iosb_status))
+ {
+ todo_wine ok(io.Status == 0xdeadf00d, "got %#x\n", io.Status);
+ todo_wine ok(io.Information == 0xdeadf00d, "got size %Iu\n", io.Information);
+ }
+ else
+ {
+ todo_wine_if (params->iosb_status == STATUS_PENDING)
+ ok(io.Status == params->iosb_status, "got %#x\n", io.Status);
+ ok(io.Information == 3, "got size %Iu\n", io.Information);
+ }
+ todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
+ ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
- strcpy(buffer, "abcdef");
- status = 0xceadbeef;
- SetLastError(0xdeadbeef);
- ret = DeviceIoControl(device, IOCTL_WINETEST_RETURN_STATUS, &status,
- sizeof(status), buffer, sizeof(buffer), &ret_size, NULL);
- ok(!ret, "ioctl succeeded\n");
- ok(GetLastError() == ERROR_MR_MID_NOT_FOUND, "got error %u\n", GetLastError());
- ok(!strcmp(buffer, "abcdef"), "got buffer %s\n", buffer);
- ok(ret_size == 3, "got size %u\n", ret_size);
+ ret = SleepEx(0, TRUE);
+ if (!params->pending && NT_ERROR(params->iosb_status))
+ {
+ todo_wine ok(!ret, "got %d\n", ret);
+ todo_wine ok(!got_return_status_apc, "got %u APC calls\n", got_return_status_apc);
+ }
+ else
+ {
+ ok(ret == WAIT_IO_COMPLETION, "got %d\n", ret);
+ ok(got_return_status_apc == 1, "got %u APC calls\n", got_return_status_apc);
+ }
+
+ ret = CloseHandle(file);
+ ok(ret, "failed to close file, error %u\n", GetLastError());
+
+ CloseHandle(event);
+}
+
+static void test_return_status(void)
+{
+ struct return_status_params params;
+ unsigned int i, j, k;
+
+ static const ULONG method_tests[] =
+ {
+ IOCTL_WINETEST_RETURN_STATUS_BUFFERED,
+ IOCTL_WINETEST_RETURN_STATUS_DIRECT,
+ IOCTL_WINETEST_RETURN_STATUS_NEITHER,
+ };
+
+ static const NTSTATUS status_tests[] =
+ {
+ STATUS_SUCCESS,
+ STATUS_PENDING,
+ STATUS_TIMEOUT,
+ 0x0eadbeef,
+ 0x4eadbeef,
+ STATUS_BUFFER_OVERFLOW,
+ 0x8eadbeef,
+ STATUS_NOT_IMPLEMENTED,
+ 0xceadbeef,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(status_tests); ++i)
+ {
+ for (j = 0; j < ARRAY_SIZE(status_tests); ++j)
+ {
+ for (params.pending = 0; params.pending <= 1; ++params.pending)
+ {
+ for (k = 0; k < ARRAY_SIZE(method_tests); ++k)
+ {
+ params.ret_status = status_tests[i];
+ params.iosb_status = status_tests[j];
+
+ winetest_push_context("return 0x%08x, iosb 0x%08x, pending %d, method %u",
+ params.ret_status, params.iosb_status, params.pending, method_tests[k] & 3);
+
+ do_return_status(method_tests[k], ¶ms);
+
+ winetest_pop_context();
+ }
+ }
+ }
+ }
}
static BOOL compare_unicode_string(const WCHAR *buffer, ULONG len, const WCHAR *expect)
@@ -765,13 +1057,25 @@ static void test_object_info(void)
ok(compare_unicode_string(file_info->FileName, file_info->FileNameLength, L"\\subfile"),
"wrong name %s\n", debugstr_wn(file_info->FileName, file_info->FileNameLength / sizeof(WCHAR)));
+ io.Status = 0xdeadf00d;
+ io.Information = 0xdeadf00d;
status = NtQueryVolumeInformationFile(file, &io, buffer, sizeof(buffer), FileFsVolumeInformation);
ok(!status, "got %#x\n", status);
+ ok(!io.Status, "got status %#x\n", io.Status);
+ size = offsetof(FILE_FS_VOLUME_INFORMATION, VolumeLabel) + volume_info->VolumeLabelLength;
+ ok(io.Information == size, "expected information %Iu, got %Iu\n", size, io.Information);
ok(volume_info->VolumeSerialNumber == 0xdeadbeef,
"wrong serial number 0x%08x\n", volume_info->VolumeSerialNumber);
ok(compare_unicode_string(volume_info->VolumeLabel, volume_info->VolumeLabelLength, L"WineTestDriver"),
"wrong name %s\n", debugstr_wn(volume_info->VolumeLabel, volume_info->VolumeLabelLength / sizeof(WCHAR)));
+ io.Status = 0xdeadf00d;
+ io.Information = 0xdeadf00d;
+ status = NtQueryVolumeInformationFile(file, &io, buffer, sizeof(buffer), FileFsAttributeInformation);
+ ok(status == STATUS_NOT_IMPLEMENTED, "got %#x\n", status);
+ ok(io.Status == 0xdeadf00d, "got status %#x\n", io.Status);
+ ok(io.Information == 0xdeadf00d, "got information %Iu\n", io.Information);
+
CloseHandle(file);
file = CreateFileA("\\\\.\\WineTestDriver\\notimpl", 0, 0, NULL, OPEN_EXISTING, 0, NULL);
@@ -1354,7 +1658,7 @@ static void test_pnp_devices(void)
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);
+ ret = DeviceIoControl(child, IOCTL_WINETEST_CHILD_MARK_PENDING, NULL, 0, NULL, 0, &size, &ovl);
ok(!ret, "DeviceIoControl succeeded\n");
ok(GetLastError() == ERROR_IO_PENDING, "got error %u\n", GetLastError());
ok(size == 0, "got size %u\n", size);
@@ -1367,7 +1671,7 @@ 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 = DeviceIoControl(child, IOCTL_WINETEST_CHECK_REMOVED, NULL, 0, NULL, 0, &size, NULL);
+ ret = DeviceIoControl(child, IOCTL_WINETEST_CHILD_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);
@@ -3162,7 +3466,6 @@ START_TEST(ntoskrnl)
ok(device != INVALID_HANDLE_VALUE, "failed to open device: %u\n", GetLastError());
test_basic_ioctl();
- test_mismatched_status_ioctl();
main_test();
todo_wine ok(modified_value == 0xdeadbeeffeedcafe, "Got unexpected value %#I64x.\n", modified_value);
--
2.30.2
More information about the wine-devel
mailing list