[KMTESTS:IO]
authorThomas Faber <thomas.faber@reactos.org>
Fri, 1 May 2015 11:03:21 +0000 (11:03 +0000)
committerThomas Faber <thomas.faber@reactos.org>
Fri, 1 May 2015 11:03:21 +0000 (11:03 +0000)
- Add a test for the interaction between NtReadFile and a file system driver
CORE-9624

svn path=/trunk/; revision=67487

rostests/kmtests/CMakeLists.txt
rostests/kmtests/include/kmt_platform.h
rostests/kmtests/kmtest/testlist.c
rostests/kmtests/ntos_io/CMakeLists.txt
rostests/kmtests/ntos_io/IoReadWrite.h [new file with mode: 0644]
rostests/kmtests/ntos_io/IoReadWrite_drv.c [new file with mode: 0644]
rostests/kmtests/ntos_io/IoReadWrite_user.c [new file with mode: 0644]

index 6cd30f6..7053d90 100644 (file)
@@ -109,6 +109,7 @@ list(APPEND KMTEST_SOURCE
     example/Example_user.c
     kernel32/FindFile_user.c
     ntos_io/IoDeviceObject_user.c
+    ntos_io/IoReadWrite_user.c
     tcpip/TcpIp_user.c
     ${COMMON_SOURCE}
 
@@ -132,6 +133,7 @@ add_dependencies(kmtest_drivers
     example_drv
     iodeviceobject_drv
     iohelper_drv
+    ioreadwrite_drv
     tcpip_drv)
 
 add_custom_target(kmtest_all)
index 8cb2fe5..4efdc58 100644 (file)
@@ -34,6 +34,7 @@
 #include <winreg.h>
 #include <winsvc.h>
 #include <ndk/cmfuncs.h>
+#include <ndk/iofuncs.h>
 #include <ndk/obfuncs.h>
 #include <ndk/rtlfuncs.h>
 #include <ndk/mmfuncs.h>
index 9688ec7..8f16578 100644 (file)
@@ -10,6 +10,7 @@
 KMT_TESTFUNC Test_Example;
 KMT_TESTFUNC Test_FindFile;
 KMT_TESTFUNC Test_IoDeviceObject;
+KMT_TESTFUNC Test_IoReadWrite;
 KMT_TESTFUNC Test_RtlAvlTree;
 KMT_TESTFUNC Test_RtlException;
 KMT_TESTFUNC Test_RtlIntSafe;
@@ -27,6 +28,7 @@ const KMT_TEST TestList[] =
     { "-Example",           Test_Example },
     { "FindFile",           Test_FindFile },
     { "IoDeviceObject",     Test_IoDeviceObject },
+    { "IoReadWrite",        Test_IoReadWrite },
     { "RtlAvlTree",         Test_RtlAvlTree },
     { "RtlException",       Test_RtlException },
     { "RtlIntSafe",         Test_RtlIntSafe },
index f05b5dc..9a00001 100644 (file)
@@ -30,3 +30,18 @@ add_importlibs(iohelper_drv ntoskrnl hal)
 add_target_compile_definitions(iohelper_drv KMT_STANDALONE_DRIVER)
 #add_pch(iohelper_drv ../include/kmt_test.h)
 add_cd_file(TARGET iohelper_drv DESTINATION reactos/bin FOR all)
+
+#
+# IoReadWrite
+#
+list(APPEND IOREADWRITE_DRV_SOURCE
+    ../kmtest_drv/kmtest_standalone.c
+    IoReadWrite_drv.c)
+
+add_library(ioreadwrite_drv SHARED ${IOREADWRITE_DRV_SOURCE})
+set_module_type(ioreadwrite_drv kernelmodedriver)
+target_link_libraries(ioreadwrite_drv kmtest_printf ${PSEH_LIB})
+add_importlibs(ioreadwrite_drv ntoskrnl hal)
+add_target_compile_definitions(ioreadwrite_drv KMT_STANDALONE_DRIVER)
+#add_pch(ioreadwrite_drv ../include/kmt_test.h)
+add_cd_file(TARGET ioreadwrite_drv DESTINATION reactos/bin FOR all)
diff --git a/rostests/kmtests/ntos_io/IoReadWrite.h b/rostests/kmtests/ntos_io/IoReadWrite.h
new file mode 100644 (file)
index 0000000..28001c7
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * PROJECT:         ReactOS kernel-mode tests
+ * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
+ * PURPOSE:         Read/Write operations test declarations
+ * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
+ */
+
+#ifndef _KMTEST_IOREADFILE_H_
+#define _KMTEST_IOREADFILE_H_
+
+#define TEST_FILE_SIZE 17
+
+#define KEY_SUCCEED                 0x00
+#define KEY_FAIL_MISALIGNED         0x81
+#define KEY_FAIL_OVERFLOW           0x82
+#define KEY_FAIL_PARTIAL            0x83
+#define KEY_FAIL_BUSY               0x84
+#define KEY_FAIL_VERIFY_REQUIRED    0x85
+
+#define KEY_FAIL_UNSUCCESSFUL       0xc1
+#define KEY_FAIL_NOT_IMPLEMENTED    0xc2
+#define KEY_FAIL_ACCESS_VIOLATION   0xc3
+#define KEY_FAIL_IN_PAGE_ERROR      0xc4
+#define KEY_FAIL_EOF                0xc5
+#define KEY_FAIL_ACCESS_DENIED      0xc6
+#define KEY_FAIL_MISALIGNED_ERROR   0xc7
+#define KEY_RESULT_MASK             0xff
+
+#define KEY_NEXT(key) ( (key) == KEY_FAIL_MISALIGNED_ERROR ? 0xff :                 \
+                        (key) == KEY_FAIL_VERIFY_REQUIRED ? KEY_FAIL_UNSUCCESSFUL : \
+                        (key) == KEY_SUCCEED ? KEY_FAIL_MISALIGNED :                \
+                        (key) + 1 )
+#define KEY_ERROR(key) (((key) & 0xc0) == 0xc0)
+static
+NTSTATUS
+TestGetReturnStatus(
+    _In_ ULONG LockKey)
+{
+    switch (LockKey & KEY_RESULT_MASK)
+    {
+        case KEY_SUCCEED:
+            return STATUS_SUCCESS;
+
+        case KEY_FAIL_MISALIGNED:
+            return STATUS_DATATYPE_MISALIGNMENT;
+        case KEY_FAIL_OVERFLOW:
+            return STATUS_BUFFER_OVERFLOW;
+        case KEY_FAIL_PARTIAL:
+            return STATUS_PARTIAL_COPY;
+        case KEY_FAIL_BUSY:
+            return STATUS_DEVICE_BUSY;
+        case KEY_FAIL_VERIFY_REQUIRED:
+            return STATUS_VERIFY_REQUIRED;
+
+        case KEY_FAIL_UNSUCCESSFUL:
+            return STATUS_UNSUCCESSFUL;
+        case KEY_FAIL_NOT_IMPLEMENTED:
+            return STATUS_NOT_IMPLEMENTED;
+        case KEY_FAIL_ACCESS_VIOLATION:
+            return STATUS_ACCESS_VIOLATION;
+        case KEY_FAIL_IN_PAGE_ERROR:
+            return STATUS_IN_PAGE_ERROR;
+        case KEY_FAIL_EOF:
+            return STATUS_END_OF_FILE;
+        case KEY_FAIL_ACCESS_DENIED:
+            return STATUS_ACCESS_DENIED;
+        case KEY_FAIL_MISALIGNED_ERROR:
+            return STATUS_DATATYPE_MISALIGNMENT_ERROR;
+        default:
+            ok(0, "Key = %lx\n", LockKey);
+            return STATUS_INVALID_PARAMETER;
+    }
+}
+
+#define KEY_USE_FASTIO              0x100
+#define KEY_RETURN_PENDING          0x200
+
+#define KEY_DATA(c) (((c) & 0xff) << 24)
+#define KEY_GET_DATA(key) ((key) >> 24)
+
+#endif /* !defined _KMTEST_IOREADFILE_H_ */
diff --git a/rostests/kmtests/ntos_io/IoReadWrite_drv.c b/rostests/kmtests/ntos_io/IoReadWrite_drv.c
new file mode 100644 (file)
index 0000000..9a7679f
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * PROJECT:         ReactOS kernel-mode tests
+ * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
+ * PURPOSE:         Test driver for Read/Write operations
+ * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
+ */
+
+#include <kmt_test.h>
+#include "IoReadWrite.h"
+
+#define NDEBUG
+#include <debug.h>
+
+typedef struct _TEST_FCB
+{
+    FSRTL_ADVANCED_FCB_HEADER Header;
+    SECTION_OBJECT_POINTERS SectionObjectPointers;
+    FAST_MUTEX HeaderMutex;
+    BOOLEAN Cached;
+} TEST_FCB, *PTEST_FCB;
+
+static KMT_IRP_HANDLER TestIrpHandler;
+static FAST_IO_READ TestFastIoRead;
+static FAST_IO_WRITE TestFastIoWrite;
+
+static FAST_IO_DISPATCH TestFastIoDispatch;
+static ULONG TestLastFastReadKey;
+static ULONG TestLastFastWriteKey;
+static PFILE_OBJECT TestFileObject;
+static PDEVICE_OBJECT TestDeviceObject;
+
+NTSTATUS
+TestEntry(
+    _In_ PDRIVER_OBJECT DriverObject,
+    _In_ PCUNICODE_STRING RegistryPath,
+    _Out_ PCWSTR *DeviceName,
+    _Inout_ INT *Flags)
+{
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    PAGED_CODE();
+
+    UNREFERENCED_PARAMETER(RegistryPath);
+
+    *DeviceName = L"IoReadWrite";
+    *Flags = TESTENTRY_NO_EXCLUSIVE_DEVICE | TESTENTRY_BUFFERED_IO_DEVICE;
+
+    TestFastIoDispatch.FastIoRead = TestFastIoRead;
+    TestFastIoDispatch.FastIoWrite = TestFastIoWrite;
+    DriverObject->FastIoDispatch = &TestFastIoDispatch;
+
+    KmtRegisterIrpHandler(IRP_MJ_CREATE, NULL, TestIrpHandler);
+    KmtRegisterIrpHandler(IRP_MJ_CLEANUP, NULL, TestIrpHandler);
+    KmtRegisterIrpHandler(IRP_MJ_READ, NULL, TestIrpHandler);
+    //KmtRegisterIrpHandler(IRP_MJ_WRITE, NULL, TestIrpHandler);
+
+    return Status;
+}
+
+VOID
+TestUnload(
+    _In_ PDRIVER_OBJECT DriverObject)
+{
+    PAGED_CODE();
+}
+
+static
+BOOLEAN
+NTAPI
+TestAcquireForLazyWrite(
+    _In_ PVOID Context,
+    _In_ BOOLEAN Wait)
+{
+    ok_eq_pointer(Context, NULL);
+    ok(0, "Unexpected call to AcquireForLazyWrite\n");
+    return TRUE;
+}
+
+static
+VOID
+NTAPI
+TestReleaseFromLazyWrite(
+    _In_ PVOID Context)
+{
+    ok_eq_pointer(Context, NULL);
+    ok(0, "Unexpected call to ReleaseFromLazyWrite\n");
+}
+
+static
+BOOLEAN
+NTAPI
+TestAcquireForReadAhead(
+    _In_ PVOID Context,
+    _In_ BOOLEAN Wait)
+{
+    ok_eq_pointer(Context, NULL);
+    ok(0, "Unexpected call to AcquireForReadAhead\n");
+    return TRUE;
+}
+
+static
+VOID
+NTAPI
+TestReleaseFromReadAhead(
+    _In_ PVOID Context)
+{
+    ok_eq_pointer(Context, NULL);
+    ok(0, "Unexpected call to ReleaseFromReadAhead\n");
+}
+
+static
+NTSTATUS
+TestCommonRead(
+    _In_ PVOID Buffer,
+    _In_ ULONG Length,
+    _In_ LONGLONG FileOffset,
+    _In_ ULONG LockKey,
+    _Out_ PIO_STATUS_BLOCK IoStatus)
+{
+    if (FileOffset >= TEST_FILE_SIZE)
+    {
+        trace("FileOffset %I64d > file size\n", FileOffset);
+        IoStatus->Status = STATUS_END_OF_FILE;
+        IoStatus->Information = 0;
+    }
+    else if (Length == 0 || Buffer == NULL)
+    {
+        IoStatus->Status = STATUS_BUFFER_OVERFLOW;
+        IoStatus->Information = TEST_FILE_SIZE - FileOffset;
+    }
+    else
+    {
+        Length = min(Length, TEST_FILE_SIZE - FileOffset);
+        RtlFillMemory(Buffer, Length, KEY_GET_DATA(LockKey));
+        IoStatus->Status = TestGetReturnStatus(LockKey);
+        IoStatus->Information = Length;
+    }
+    if (LockKey & KEY_RETURN_PENDING)
+        return STATUS_PENDING;
+    return IoStatus->Status;
+}
+
+static
+BOOLEAN
+NTAPI
+TestFastIoRead(
+    _In_ PFILE_OBJECT FileObject,
+    _In_ PLARGE_INTEGER FileOffset,
+    _In_ ULONG Length,
+    _In_ BOOLEAN Wait,
+    _In_ ULONG LockKey,
+    _Out_ PVOID Buffer,
+    _Out_ PIO_STATUS_BLOCK IoStatus,
+    _In_ PDEVICE_OBJECT DeviceObject)
+{
+    PTEST_FCB Fcb;
+    NTSTATUS Status;
+
+    //trace("FastIoRead: %p %lx %I64d+%lu -> %p\n", FileObject, LockKey, FileOffset->QuadPart, Length, Buffer);
+    ok_eq_pointer(FileObject, TestFileObject);
+    ok_bool_true(Wait, "Wait is");
+    ok_eq_pointer(DeviceObject, TestDeviceObject);
+    Fcb = FileObject->FsContext;
+    ok_bool_true(Fcb->Cached, "Cached is");
+
+    TestLastFastReadKey = LockKey;
+    ok((ULONG_PTR)Buffer < MM_USER_PROBE_ADDRESS, "Buffer is %p\n", Buffer);
+    ok((ULONG_PTR)FileOffset > MM_USER_PROBE_ADDRESS, "FileOffset is %p\n", FileOffset);
+    ok((ULONG_PTR)IoStatus > MM_USER_PROBE_ADDRESS, "IoStatus is %p\n", IoStatus);
+    _SEH2_TRY
+    {
+        Status = TestCommonRead(Buffer, Length, FileOffset->QuadPart, LockKey, IoStatus);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        IoStatus->Status = _SEH2_GetExceptionCode();
+        return FALSE;
+    }
+    _SEH2_END;
+
+    if (Status == STATUS_PENDING)
+        return FALSE;
+
+    if (LockKey & KEY_USE_FASTIO)
+        return TRUE;
+    else
+        return FALSE;
+}
+
+static
+BOOLEAN
+NTAPI
+TestFastIoWrite(
+    _In_ PFILE_OBJECT FileObject,
+    _In_ PLARGE_INTEGER FileOffset,
+    _In_ ULONG Length,
+    _In_ BOOLEAN Wait,
+    _In_ ULONG LockKey,
+    _In_ PVOID Buffer,
+    _Out_ PIO_STATUS_BLOCK IoStatus,
+    _In_ PDEVICE_OBJECT DeviceObject)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+static
+NTSTATUS
+TestIrpHandler(
+    _In_ PDEVICE_OBJECT DeviceObject,
+    _In_ PIRP Irp,
+    _In_ PIO_STACK_LOCATION IoStack)
+{
+    NTSTATUS Status;
+    PTEST_FCB Fcb;
+    CACHE_MANAGER_CALLBACKS Callbacks;
+    CACHE_UNINITIALIZE_EVENT CacheUninitEvent;
+
+    PAGED_CODE();
+
+    DPRINT("IRP %x/%x\n", IoStack->MajorFunction, IoStack->MinorFunction);
+    ASSERT(IoStack->MajorFunction == IRP_MJ_CREATE ||
+           IoStack->MajorFunction == IRP_MJ_CLEANUP ||
+           IoStack->MajorFunction == IRP_MJ_READ ||
+           IoStack->MajorFunction == IRP_MJ_WRITE);
+
+    Status = STATUS_NOT_SUPPORTED;
+    Irp->IoStatus.Information = 0;
+
+    if (IoStack->MajorFunction == IRP_MJ_CREATE)
+    {
+        if (IoStack->FileObject->FileName.Length >= 2 * sizeof(WCHAR))
+        {
+            TestDeviceObject = DeviceObject;
+            TestFileObject = IoStack->FileObject;
+        }
+        Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*Fcb), 'FwrI');
+        RtlZeroMemory(Fcb, sizeof(*Fcb));
+        ExInitializeFastMutex(&Fcb->HeaderMutex);
+        FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex);
+        Fcb->Header.AllocationSize.QuadPart = TEST_FILE_SIZE;
+        Fcb->Header.FileSize.QuadPart = TEST_FILE_SIZE;
+        Fcb->Header.ValidDataLength.QuadPart = TEST_FILE_SIZE;
+        IoStack->FileObject->FsContext = Fcb;
+        IoStack->FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
+        if (IoStack->FileObject->FileName.Length >= 2 * sizeof(WCHAR) &&
+            IoStack->FileObject->FileName.Buffer[1] != 'N')
+        {
+            Fcb->Cached = TRUE;
+            Callbacks.AcquireForLazyWrite = TestAcquireForLazyWrite;
+            Callbacks.ReleaseFromLazyWrite = TestReleaseFromLazyWrite;
+            Callbacks.AcquireForReadAhead = TestAcquireForReadAhead;
+            Callbacks.ReleaseFromReadAhead = TestReleaseFromReadAhead;
+            CcInitializeCacheMap(IoStack->FileObject,
+                                 (PCC_FILE_SIZES)&Fcb->Header.AllocationSize,
+                                 FALSE,
+                                 &Callbacks,
+                                 NULL);
+        }
+        Irp->IoStatus.Information = FILE_OPENED;
+        Status = STATUS_SUCCESS;
+    }
+    else if (IoStack->MajorFunction == IRP_MJ_CLEANUP)
+    {
+        KeInitializeEvent(&CacheUninitEvent.Event, NotificationEvent, FALSE);
+        CcUninitializeCacheMap(IoStack->FileObject, NULL, &CacheUninitEvent);
+        KeWaitForSingleObject(&CacheUninitEvent.Event, Executive, KernelMode, FALSE, NULL);
+        Fcb = IoStack->FileObject->FsContext;
+        ExFreePoolWithTag(Fcb, 'FwrI');
+        IoStack->FileObject->FsContext = NULL;
+        Status = STATUS_SUCCESS;
+    }
+    else if (IoStack->MajorFunction == IRP_MJ_READ)
+    {
+        //trace("IRP_MJ_READ: %p %lx %I64d+%lu -> %p\n", IoStack->FileObject, IoStack->Parameters.Read.Key, IoStack->Parameters.Read.ByteOffset.QuadPart, IoStack->Parameters.Read.Length, Irp->AssociatedIrp.SystemBuffer);
+        ok_eq_pointer(DeviceObject, TestDeviceObject);
+        ok_eq_pointer(IoStack->FileObject, TestFileObject);
+        Fcb = IoStack->FileObject->FsContext;
+        if (Fcb->Cached)
+            ok_eq_hex(IoStack->Parameters.Read.Key, TestLastFastReadKey);
+        ok(Irp->AssociatedIrp.SystemBuffer == NULL ||
+           (ULONG_PTR)Irp->AssociatedIrp.SystemBuffer > MM_USER_PROBE_ADDRESS,
+           "Buffer is %p\n",
+           Irp->AssociatedIrp.SystemBuffer);
+        Status = TestCommonRead(Irp->AssociatedIrp.SystemBuffer,
+                                IoStack->Parameters.Read.Length,
+                                IoStack->Parameters.Read.ByteOffset.QuadPart,
+                                IoStack->Parameters.Read.Key,
+                                &Irp->IoStatus);
+    }
+    else if (IoStack->MajorFunction == IRP_MJ_WRITE)
+    {
+        ok_eq_pointer(DeviceObject, TestDeviceObject);
+        ok_eq_pointer(IoStack->FileObject, TestFileObject);
+        UNIMPLEMENTED;
+        Status = STATUS_NOT_IMPLEMENTED;
+    }
+
+    if (Status == STATUS_PENDING)
+    {
+        IoMarkIrpPending(Irp);
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        Status = STATUS_PENDING;
+    }
+    else
+    {
+        Irp->IoStatus.Status = Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    }
+
+    return Status;
+}
diff --git a/rostests/kmtests/ntos_io/IoReadWrite_user.c b/rostests/kmtests/ntos_io/IoReadWrite_user.c
new file mode 100644 (file)
index 0000000..429dbc2
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * PROJECT:         ReactOS kernel-mode tests
+ * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
+ * PURPOSE:         Test for Read/Write operations
+ * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
+ */
+
+#include <kmt_test.h>
+#include "IoReadWrite.h"
+
+static
+VOID
+TestRead(
+    _In_ HANDLE FileHandle,
+    _In_ BOOLEAN Cached,
+    _In_ BOOLEAN UseFastIo,
+    _In_ BOOLEAN ReturnPending)
+{
+    NTSTATUS Status;
+    IO_STATUS_BLOCK IoStatus;
+    HANDLE EventHandle;
+    UCHAR Buffer[32];
+    LARGE_INTEGER Offset;
+    ULONG BaseKey, StatusKey, Key;
+    DWORD WaitStatus;
+
+    BaseKey = (UseFastIo ? KEY_USE_FASTIO : 0) |
+              (ReturnPending ? KEY_RETURN_PENDING : 0);
+
+    EventHandle = CreateEventW(NULL, TRUE, FALSE, NULL);
+    ok(EventHandle != NULL, "CreateEvent failed with %lu\n", GetLastError());
+
+    for (StatusKey = KEY_SUCCEED ; StatusKey != 0xff; StatusKey = KEY_NEXT(StatusKey))
+    {
+        trace("\tSTATUS KEY: %lx\n", StatusKey);
+        ResetEvent(EventHandle);
+        RtlFillMemory(&IoStatus, sizeof(IoStatus), 0x55);
+        Key = BaseKey | StatusKey | KEY_DATA(0x11);
+        Offset.QuadPart = 0;
+        Status = NtReadFile(FileHandle,
+                            EventHandle,
+                            NULL,
+                            NULL,
+                            &IoStatus,
+                            NULL,
+                            0,
+                            &Offset,
+                            &Key);
+        WaitStatus = WaitForSingleObject(EventHandle, 0);
+        if (ReturnPending)
+            ok_eq_hex(Status, STATUS_ACCESS_VIOLATION);
+        else
+            ok_eq_hex(Status, STATUS_BUFFER_OVERFLOW);
+        if (Cached && UseFastIo && !ReturnPending)
+        {
+            ok_eq_ulong(WaitStatus, WAIT_OBJECT_0);
+            ok_eq_hex(IoStatus.Status, STATUS_BUFFER_OVERFLOW);
+            ok_eq_ulongptr(IoStatus.Information, TEST_FILE_SIZE);
+        }
+        else
+        {
+            ok_eq_ulong(WaitStatus, WAIT_TIMEOUT);
+            ok_eq_hex(IoStatus.Status, 0x55555555);
+            ok_eq_ulongptr(IoStatus.Information, (ULONG_PTR)0x5555555555555555);
+        }
+
+        KmtStartSeh()
+        ResetEvent(EventHandle);
+        RtlFillMemory(&IoStatus, sizeof(IoStatus), 0x55);
+        Key = BaseKey | StatusKey | KEY_DATA(0x22);
+        Offset.QuadPart = 0;
+        Status = NtReadFile(FileHandle,
+                            EventHandle,
+                            NULL,
+                            NULL,
+                            &IoStatus,
+                            NULL,
+                            sizeof(Buffer),
+                            &Offset,
+                            &Key);
+        WaitStatus = WaitForSingleObject(EventHandle, 0);
+        ok_eq_ulong(WaitStatus, WAIT_TIMEOUT);
+        ok_eq_hex(Status, STATUS_ACCESS_VIOLATION);
+        ok_eq_hex(IoStatus.Status, 0x55555555);
+        ok_eq_ulongptr(IoStatus.Information, (ULONG_PTR)0x5555555555555555);
+        KmtEndSeh(STATUS_SUCCESS);
+
+        ResetEvent(EventHandle);
+        RtlFillMemory(&IoStatus, sizeof(IoStatus), 0x55);
+        RtlFillMemory(Buffer, sizeof(Buffer), 0x55);
+        Key = BaseKey | StatusKey | KEY_DATA(0x33);
+        Offset.QuadPart = 0;
+        Status = NtReadFile(FileHandle,
+                            EventHandle,
+                            NULL,
+                            NULL,
+                            &IoStatus,
+                            Buffer,
+                            0,
+                            &Offset,
+                            &Key);
+        WaitStatus = WaitForSingleObject(EventHandle, 0);
+        if (ReturnPending)
+            ok_eq_hex(Status, STATUS_ACCESS_VIOLATION);
+        else
+            ok_eq_hex(Status, STATUS_BUFFER_OVERFLOW);
+        if (Cached && UseFastIo && !ReturnPending)
+        {
+            ok_eq_ulong(WaitStatus, WAIT_OBJECT_0);
+            ok_eq_hex(IoStatus.Status, STATUS_BUFFER_OVERFLOW);
+            ok_eq_ulongptr(IoStatus.Information, TEST_FILE_SIZE);
+        }
+        else
+        {
+            ok_eq_ulong(WaitStatus, WAIT_TIMEOUT);
+            ok_eq_hex(IoStatus.Status, 0x55555555);
+            ok_eq_ulongptr(IoStatus.Information, (ULONG_PTR)0x5555555555555555);
+        }
+        ok_eq_uint(Buffer[0], 0x55);
+
+        ResetEvent(EventHandle);
+        RtlFillMemory(&IoStatus, sizeof(IoStatus), 0x55);
+        RtlFillMemory(Buffer, sizeof(Buffer), 0x55);
+        Key = BaseKey | StatusKey | KEY_DATA(0x44);
+        Offset.QuadPart = 0;
+        Status = NtReadFile(FileHandle,
+                            EventHandle,
+                            NULL,
+                            NULL,
+                            &IoStatus,
+                            Buffer,
+                            sizeof(Buffer),
+                            &Offset,
+                            &Key);
+        WaitStatus = WaitForSingleObject(EventHandle, 0);
+        ok_eq_hex(Status, TestGetReturnStatus(StatusKey));
+        if ((Cached && UseFastIo && !ReturnPending &&
+             (StatusKey == KEY_SUCCEED || StatusKey == KEY_FAIL_OVERFLOW || StatusKey == KEY_FAIL_EOF)) ||
+            !KEY_ERROR(StatusKey))
+        {
+            ok_eq_ulong(WaitStatus, WAIT_OBJECT_0);
+            ok_eq_hex(IoStatus.Status, TestGetReturnStatus(StatusKey));
+            ok_eq_ulongptr(IoStatus.Information, TEST_FILE_SIZE);
+        }
+        else
+        {
+            ok_eq_ulong(WaitStatus, WAIT_TIMEOUT);
+            ok_eq_hex(IoStatus.Status, 0x55555555);
+            ok_eq_ulongptr(IoStatus.Information, (ULONG_PTR)0x5555555555555555);
+        }
+        if ((StatusKey != KEY_FAIL_VERIFY_REQUIRED && !KEY_ERROR(StatusKey)) ||
+            Cached)
+        {
+            ok_eq_uint(Buffer[0], 0x44);
+            ok_eq_uint(Buffer[TEST_FILE_SIZE - 1], 0x44);
+            ok_eq_uint(Buffer[TEST_FILE_SIZE], 0x55);
+        }
+        else
+        {
+            ok_eq_uint(Buffer[0], 0x55);
+        }
+    }
+}
+
+START_TEST(IoReadWrite)
+{
+    HANDLE FileHandle;
+    UNICODE_STRING CachedFileName = RTL_CONSTANT_STRING(L"\\Device\\Kmtest-IoReadWrite\\Cached");
+    UNICODE_STRING NonCachedFileName = RTL_CONSTANT_STRING(L"\\Device\\Kmtest-IoReadWrite\\NonCached");
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    IO_STATUS_BLOCK IoStatus;
+    NTSTATUS Status;
+
+    KmtLoadDriver(L"IoReadWrite", FALSE);
+    KmtOpenDriver();
+
+    RtlFillMemory(&IoStatus, sizeof(IoStatus), 0x55);
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &NonCachedFileName,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+    Status = NtOpenFile(&FileHandle,
+                        FILE_ALL_ACCESS,
+                        &ObjectAttributes,
+                        &IoStatus,
+                        0,
+                        FILE_NON_DIRECTORY_FILE |
+                        FILE_SYNCHRONOUS_IO_NONALERT);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    if (!skip(NT_SUCCESS(Status), "No file\n"))
+    {
+        ok_eq_hex(IoStatus.Status, STATUS_SUCCESS);
+        ok_eq_ulongptr(IoStatus.Information, FILE_OPENED);
+        trace("Non-Cached, no FastIo, direct return\n");
+        TestRead(FileHandle, FALSE, FALSE, FALSE);
+        trace("Non-Cached, allow FastIo, direct return\n");
+        TestRead(FileHandle, FALSE, TRUE, FALSE);
+        trace("Non-Cached, no FastIo, pending return\n");
+        TestRead(FileHandle, FALSE, FALSE, TRUE);
+        trace("Non-Cached, allow FastIo, pending return\n");
+        TestRead(FileHandle, FALSE, TRUE, TRUE);
+        NtClose(FileHandle);
+    }
+
+    RtlFillMemory(&IoStatus, sizeof(IoStatus), 0x55);
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &CachedFileName,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+    Status = NtOpenFile(&FileHandle,
+                        FILE_ALL_ACCESS,
+                        &ObjectAttributes,
+                        &IoStatus,
+                        0,
+                        FILE_NON_DIRECTORY_FILE |
+                        FILE_SYNCHRONOUS_IO_NONALERT);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    if (!skip(NT_SUCCESS(Status), "No file\n"))
+    {
+        ok_eq_hex(IoStatus.Status, STATUS_SUCCESS);
+        ok_eq_ulongptr(IoStatus.Information, FILE_OPENED);
+        trace("Cached, no FastIo, direct return\n");
+        TestRead(FileHandle, TRUE, FALSE, FALSE);
+        trace("Cached, allow FastIo, direct return\n");
+        TestRead(FileHandle, TRUE, TRUE, FALSE);
+        trace("Cached, no FastIo, pending return\n");
+        TestRead(FileHandle, TRUE, FALSE, TRUE);
+        trace("Cached, allow FastIo, pending return\n");
+        TestRead(FileHandle, TRUE, TRUE, TRUE);
+        NtClose(FileHandle);
+    }
+
+    KmtCloseDriver();
+    KmtUnloadDriver();
+}