[PATCH 2/4] ntoskrnl/tests: Add more tests for IRP status handling.

Zebediah Figura zfigura at codeweavers.com
Thu Aug 26 11:46:28 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   | 422 ++++++++++++++++++++++-----
 4 files changed, 437 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 88988285fab..fd72d65215a 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,359 @@ 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, "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, "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, "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, "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)
+        {
+            ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
+        }
+        else
+        {
+            todo_wine
+            {
+                ok(!ret, "got %#x\n", 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,
+        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], &params);
+
+                    winetest_pop_context();
+                }
+            }
+        }
+    }
 }
 
 static BOOL compare_unicode_string(const WCHAR *buffer, ULONG len, const WCHAR *expect)
@@ -765,13 +1040,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 +1641,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 succeded\n");
     ok(GetLastError() == ERROR_IO_PENDING, "got error %u\n", GetLastError());
     ok(size == 0, "got size %u\n", size);
@@ -1367,7 +1654,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);
@@ -3056,7 +3343,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