[KMTESTS:NPFS]
authorThomas Faber <thomas.faber@reactos.org>
Wed, 9 Apr 2014 18:00:33 +0000 (18:00 +0000)
committerThomas Faber <thomas.faber@reactos.org>
Wed, 9 Apr 2014 18:00:33 +0000 (18:00 +0000)
- Add some (pretty incomplete) tests for NPFS Create, Connect and Read/Write operations
CORE-7451

svn path=/trunk/; revision=62695

rostests/kmtests/CMakeLists.txt
rostests/kmtests/kmtest_drv/testlist.c
rostests/kmtests/npfs/NpfsConnect.c [new file with mode: 0644]
rostests/kmtests/npfs/NpfsCreate.c [new file with mode: 0644]
rostests/kmtests/npfs/NpfsHelpers.c [new file with mode: 0644]
rostests/kmtests/npfs/NpfsReadWrite.c [new file with mode: 0644]
rostests/kmtests/npfs/npfs.h [new file with mode: 0644]

index 828a290..11d1369 100644 (file)
@@ -28,6 +28,10 @@ list(APPEND KMTEST_DRV_SOURCE
 
     example/Example.c
     example/KernelType.c
+    npfs/NpfsConnect.c
+    npfs/NpfsCreate.c
+    npfs/NpfsHelpers.c
+    npfs/NpfsReadWrite.c
     ntos_ex/ExCallback.c
     ntos_ex/ExDoubleList.c
     ntos_ex/ExFastMutex.c
index 0920bdf..8cb7c06 100644 (file)
@@ -38,6 +38,9 @@ KMT_TESTFUNC Test_KeProcessor;
 KMT_TESTFUNC Test_KeTimer;
 KMT_TESTFUNC Test_KernelType;
 KMT_TESTFUNC Test_MmSection;
+KMT_TESTFUNC Test_NpfsConnect;
+KMT_TESTFUNC Test_NpfsCreate;
+KMT_TESTFUNC Test_NpfsReadWrite;
 KMT_TESTFUNC Test_ObReference;
 KMT_TESTFUNC Test_ObType;
 KMT_TESTFUNC Test_ObTypeClean;
@@ -89,6 +92,9 @@ const KMT_TEST TestList[] =
     { "KeTimer",                            Test_KeTimer },
     { "-KernelType",                        Test_KernelType },
     { "MmSection",                          Test_MmSection },
+    { "NpfsConnect",                        Test_NpfsConnect },
+    { "NpfsCreate",                         Test_NpfsCreate },
+    { "NpfsReadWrite",                      Test_NpfsReadWrite },
     { "ObReference",                        Test_ObReference },
     { "ObType",                             Test_ObType },
     { "-ObTypeClean",                       Test_ObTypeClean },
diff --git a/rostests/kmtests/npfs/NpfsConnect.c b/rostests/kmtests/npfs/NpfsConnect.c
new file mode 100644 (file)
index 0000000..2c53971
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * PROJECT:         ReactOS kernel-mode tests
+ * LICENSE:         LGPLv2+ - See COPYING.LIB in the top level directory
+ * PURPOSE:         Kernel-Mode Test Suite NPFS Connect test
+ * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
+ */
+
+#include <kmt_test.h>
+#include "npfs.h"
+
+#define MAX_INSTANCES   5
+#define IN_QUOTA        4096
+#define OUT_QUOTA       4096
+
+#define CheckServer(ServerHandle, State)                        \
+    NpCheckServerPipe(ServerHandle,                             \
+                      BYTE_STREAM, QUEUE, BYTE_STREAM, DUPLEX,  \
+                      MAX_INSTANCES, 1,                         \
+                      IN_QUOTA, 0,                              \
+                      OUT_QUOTA, OUT_QUOTA,                     \
+                      State)
+
+#define CheckClient(ClientHandle, State)                        \
+    NpCheckClientPipe(ClientHandle,                             \
+                      BYTE_STREAM, QUEUE, BYTE_STREAM, DUPLEX,  \
+                      MAX_INSTANCES, 1,                         \
+                      IN_QUOTA, 0,                              \
+                      OUT_QUOTA, OUT_QUOTA,                     \
+                      State)
+
+static
+VOID
+ConnectPipe(
+    IN OUT PTHREAD_CONTEXT Context)
+{
+    HANDLE ClientHandle;
+
+    ClientHandle = NULL;
+    Context->Connect.Status = NpOpenPipe(&ClientHandle,
+                                         Context->Connect.PipePath,
+                                         FILE_PIPE_FULL_DUPLEX);
+    Context->Connect.ClientHandle = ClientHandle;
+}
+
+static
+VOID
+ListenPipe(
+    IN OUT PTHREAD_CONTEXT Context)
+{
+    Context->Listen.Status = NpListenPipe(Context->Listen.ServerHandle);
+}
+
+static
+BOOLEAN
+CheckConnectPipe(
+    IN PTHREAD_CONTEXT Context,
+    IN PCWSTR PipePath,
+    IN ULONG MilliSeconds)
+{
+    Context->Work = ConnectPipe;
+    Context->Connect.PipePath = PipePath;
+    return TriggerWork(Context, MilliSeconds);
+}
+
+static
+BOOLEAN
+CheckListenPipe(
+    IN PTHREAD_CONTEXT Context,
+    IN HANDLE ServerHandle,
+    IN ULONG MilliSeconds)
+{
+    Context->Work = ListenPipe;
+    Context->Listen.ServerHandle = ServerHandle;
+    return TriggerWork(Context, MilliSeconds);
+}
+
+static
+VOID
+TestConnect(
+    IN HANDLE ServerHandle,
+    IN PCWSTR PipePath)
+{
+    NTSTATUS Status;
+    THREAD_CONTEXT ConnectContext;
+    THREAD_CONTEXT ListenContext;
+    BOOLEAN Okay;
+    HANDLE ClientHandle;
+
+    StartWorkerThread(&ConnectContext);
+    StartWorkerThread(&ListenContext);
+
+    /* Server should start out listening */
+    CheckServer(ServerHandle, FILE_PIPE_LISTENING_STATE);
+
+    /* Connect a client */
+    ClientHandle = NULL;
+    Okay = CheckConnectPipe(&ConnectContext, PipePath, 100);
+    ok_bool_true(Okay, "CheckConnectPipe returned");
+    ok_eq_hex(ConnectContext.Connect.Status, STATUS_SUCCESS);
+    if (NT_SUCCESS(ConnectContext.Connect.Status))
+    {
+        ClientHandle = ConnectContext.Connect.ClientHandle;
+        CheckClient(ClientHandle, FILE_PIPE_CONNECTED_STATE);
+    }
+    CheckServer(ServerHandle, FILE_PIPE_CONNECTED_STATE);
+
+    /* Connect another client */
+    Okay = CheckConnectPipe(&ConnectContext, PipePath, 100);
+    ok_bool_true(Okay, "CheckConnectPipe returned");
+    ok_eq_hex(ConnectContext.Connect.Status, STATUS_PIPE_NOT_AVAILABLE);
+    if (NT_SUCCESS(ConnectContext.Connect.Status))
+        ObCloseHandle(ConnectContext.Connect.ClientHandle, KernelMode);
+    CheckClient(ClientHandle, FILE_PIPE_CONNECTED_STATE);
+    CheckServer(ServerHandle, FILE_PIPE_CONNECTED_STATE);
+
+    /* Disconnecting the client should fail */
+    Status = NpDisconnectPipe(ClientHandle);
+    ok_eq_hex(Status, STATUS_ILLEGAL_FUNCTION);
+    CheckClient(ClientHandle, FILE_PIPE_CONNECTED_STATE);
+    CheckServer(ServerHandle, FILE_PIPE_CONNECTED_STATE);
+
+    /* Listening on the client should fail */
+    Status = NpListenPipe(ClientHandle);
+    ok_eq_hex(Status, STATUS_ILLEGAL_FUNCTION);
+    CheckClient(ClientHandle, FILE_PIPE_CONNECTED_STATE);
+    CheckServer(ServerHandle, FILE_PIPE_CONNECTED_STATE);
+
+    /* Close client */
+    if (ClientHandle)
+        ObCloseHandle(ClientHandle, KernelMode);
+    CheckServer(ServerHandle, FILE_PIPE_CLOSING_STATE);
+
+    /* Connecting a client now should fail */
+    Okay = CheckConnectPipe(&ConnectContext, PipePath, 100);
+    ok_bool_true(Okay, "CheckConnectPipe returned");
+    ok_eq_hex(ConnectContext.Connect.Status, STATUS_PIPE_NOT_AVAILABLE);
+    if (NT_SUCCESS(ConnectContext.Connect.Status))
+        ObCloseHandle(ConnectContext.Connect.ClientHandle, KernelMode);
+    CheckServer(ServerHandle, FILE_PIPE_CLOSING_STATE);
+
+    /* Listening should fail */
+    Okay = CheckListenPipe(&ListenContext, ServerHandle, 100);
+    ok_bool_true(Okay, "CheckListenPipe returned");
+    if (!skip(Okay, "Listen succeeded unexpectedly\n"))
+        CheckServer(ServerHandle, FILE_PIPE_CLOSING_STATE);
+
+    /* Disconnect server */
+    Status = NpDisconnectPipe(ServerHandle);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    CheckServer(ServerHandle, FILE_PIPE_DISCONNECTED_STATE);
+
+    /* Disconnecting again should fail */
+    Status = NpDisconnectPipe(ServerHandle);
+    ok_eq_hex(Status, STATUS_PIPE_DISCONNECTED);
+    CheckServer(ServerHandle, FILE_PIPE_DISCONNECTED_STATE);
+
+    /* Connecting a client now should fail */
+    Okay = CheckConnectPipe(&ConnectContext, PipePath, 100);
+    ok_bool_true(Okay, "CheckConnectPipe returned");
+    ok_eq_hex(ConnectContext.Connect.Status, STATUS_PIPE_NOT_AVAILABLE);
+    if (NT_SUCCESS(ConnectContext.Connect.Status))
+        ObCloseHandle(ConnectContext.Connect.ClientHandle, KernelMode);
+    CheckServer(ServerHandle, FILE_PIPE_DISCONNECTED_STATE);
+
+    /**************************************************************************/
+    /* Now listen again */
+    Okay = CheckListenPipe(&ListenContext, ServerHandle, 100);
+    ok_bool_false(Okay, "CheckListenPipe returned");
+    //blocks: CheckServer(ServerHandle, FILE_PIPE_LISTENING_STATE);
+
+    /* Connect client */
+    ClientHandle = NULL;
+    Okay = CheckConnectPipe(&ConnectContext, PipePath, 100);
+    ok_bool_true(Okay, "CheckConnectPipe returned");
+    ok_eq_hex(ConnectContext.Connect.Status, STATUS_SUCCESS);
+    if (NT_SUCCESS(ConnectContext.Connect.Status))
+    {
+        ClientHandle = ConnectContext.Connect.ClientHandle;
+        CheckClient(ClientHandle, FILE_PIPE_CONNECTED_STATE);
+    }
+    Okay = WaitForWork(&ListenContext, 100);
+    ok_bool_true(Okay, "WaitForWork returned");
+    ok_eq_hex(ListenContext.Listen.Status, STATUS_SUCCESS);
+    CheckServer(ServerHandle, FILE_PIPE_CONNECTED_STATE);
+
+    /* Listening again should fail */
+    Okay = CheckListenPipe(&ListenContext, ServerHandle, 100);
+    ok_bool_true(Okay, "CheckListenPipe returned");
+    ok_eq_hex(ListenContext.Listen.Status, STATUS_PIPE_CONNECTED);
+    CheckClient(ClientHandle, FILE_PIPE_CONNECTED_STATE);
+    CheckServer(ServerHandle, FILE_PIPE_CONNECTED_STATE);
+
+    /* Disconnect server */
+    Status = NpDisconnectPipe(ServerHandle);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    NpQueryPipe(ClientHandle, STATUS_PIPE_DISCONNECTED);
+    CheckServer(ServerHandle, FILE_PIPE_DISCONNECTED_STATE);
+
+    /* Close client */
+    if (ClientHandle)
+        ObCloseHandle(ClientHandle, KernelMode);
+    CheckServer(ServerHandle, FILE_PIPE_DISCONNECTED_STATE);
+
+    /**************************************************************************/
+    /* Listen once more */
+    Okay = CheckListenPipe(&ListenContext, ServerHandle, 100);
+    ok_bool_false(Okay, "CheckListenPipe returned");
+    //blocks: CheckServer(ServerHandle, FILE_PIPE_LISTENING_STATE);
+
+    /* Connect client */
+    ClientHandle = NULL;
+    Okay = CheckConnectPipe(&ConnectContext, PipePath, 100);
+    ok_bool_true(Okay, "CheckConnectPipe returned");
+    ok_eq_hex(ConnectContext.Connect.Status, STATUS_SUCCESS);
+    if (NT_SUCCESS(ConnectContext.Connect.Status))
+    {
+        ClientHandle = ConnectContext.Connect.ClientHandle;
+        CheckClient(ClientHandle, FILE_PIPE_CONNECTED_STATE);
+    }
+    Okay = WaitForWork(&ListenContext, 100);
+    ok_bool_true(Okay, "WaitForWork returned");
+    ok_eq_hex(ListenContext.Listen.Status, STATUS_SUCCESS);
+    CheckServer(ServerHandle, FILE_PIPE_CONNECTED_STATE);
+
+    /* Close server */
+    Status = ObCloseHandle(ServerHandle, KernelMode);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    CheckClient(ClientHandle, FILE_PIPE_CLOSING_STATE);
+
+    /* Close client */
+    if (ClientHandle)
+        ObCloseHandle(ClientHandle, KernelMode);
+
+    FinishWorkerThread(&ListenContext);
+    FinishWorkerThread(&ConnectContext);
+}
+
+static KSTART_ROUTINE RunTest;
+static
+VOID
+NTAPI
+RunTest(
+    IN PVOID Context)
+{
+    NTSTATUS Status;
+    HANDLE ServerHandle;
+
+    UNREFERENCED_PARAMETER(Context);
+
+    ServerHandle = INVALID_HANDLE_VALUE;
+    Status = NpCreatePipe(&ServerHandle,
+                          DEVICE_NAMED_PIPE L"\\KmtestNpfsConnectTestPipe",
+                          BYTE_STREAM, QUEUE, BYTE_STREAM, DUPLEX,
+                          MAX_INSTANCES,
+                          IN_QUOTA,
+                          OUT_QUOTA);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    ok(ServerHandle != NULL && ServerHandle != INVALID_HANDLE_VALUE, "ServerHandle = %p\n", ServerHandle);
+    if (!skip(NT_SUCCESS(Status) && ServerHandle != NULL && ServerHandle != INVALID_HANDLE_VALUE, "No pipe\n"))
+    {
+        CheckServer(ServerHandle, FILE_PIPE_LISTENING_STATE);
+        TestConnect(ServerHandle, DEVICE_NAMED_PIPE L"\\KmtestNpfsConnectTestPipe");
+    }
+}
+
+START_TEST(NpfsConnect)
+{
+    PKTHREAD Thread;
+
+    Thread = KmtStartThread(RunTest, NULL);
+    KmtFinishThread(Thread, NULL);
+}
diff --git a/rostests/kmtests/npfs/NpfsCreate.c b/rostests/kmtests/npfs/NpfsCreate.c
new file mode 100644 (file)
index 0000000..1c104ca
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * PROJECT:         ReactOS kernel-mode tests
+ * LICENSE:         LGPLv2+ - See COPYING.LIB in the top level directory
+ * PURPOSE:         Kernel-Mode Test Suite NPFS Create test
+ * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
+ */
+
+#include <kmt_test.h>
+#include "npfs.h"
+
+static
+VOID
+TestCreateNamedPipe(VOID)
+{
+    NTSTATUS Status;
+    HANDLE ServerHandle;
+    ULONG MaxInstances;
+    ULONG InQuota, OutQuota;
+    ULONG Quotas[] = { 0, 1, 2, 1024, PAGE_SIZE - 1, PAGE_SIZE, PAGE_SIZE + 1, 2 * PAGE_SIZE, 8 * PAGE_SIZE, 64 * PAGE_SIZE, 64 * PAGE_SIZE + 1, 128 * PAGE_SIZE };
+    ULONG i;
+    LARGE_INTEGER Timeout;
+
+    /* Invalid pipe name */
+    MaxInstances = 1;
+    InQuota = 4096;
+    OutQuota = 4096;
+    ServerHandle = INVALID_HANDLE_VALUE;
+    Status = NpCreatePipe(&ServerHandle,
+                          DEVICE_NAMED_PIPE L"",
+                          BYTE_STREAM, QUEUE, BYTE_STREAM, DUPLEX,
+                          MaxInstances,
+                          InQuota,
+                          OutQuota);
+    ok_eq_hex(Status, STATUS_OBJECT_NAME_INVALID);
+    ok_eq_pointer(ServerHandle, INVALID_HANDLE_VALUE);
+    if (ServerHandle != NULL && ServerHandle != INVALID_HANDLE_VALUE)
+        ObCloseHandle(ServerHandle, KernelMode);
+
+    ServerHandle = INVALID_HANDLE_VALUE;
+    Status = NpCreatePipe(&ServerHandle,
+                          DEVICE_NAMED_PIPE L"\\",
+                          BYTE_STREAM, QUEUE, BYTE_STREAM, DUPLEX,
+                          MaxInstances,
+                          InQuota,
+                          OutQuota);
+    ok_eq_hex(Status, STATUS_OBJECT_NAME_INVALID);
+    ok_eq_pointer(ServerHandle, INVALID_HANDLE_VALUE);
+    if (ServerHandle != NULL && ServerHandle != INVALID_HANDLE_VALUE)
+        ObCloseHandle(ServerHandle, KernelMode);
+
+    ServerHandle = INVALID_HANDLE_VALUE;
+    Status = NpCreatePipe(&ServerHandle,
+                          DEVICE_NAMED_PIPE L"\\\\",
+                          BYTE_STREAM, QUEUE, BYTE_STREAM, DUPLEX,
+                          MaxInstances,
+                          InQuota,
+                          OutQuota);
+    ok_eq_hex(Status, STATUS_OBJECT_NAME_INVALID);
+    ok_eq_pointer(ServerHandle, INVALID_HANDLE_VALUE);
+    if (ServerHandle != NULL && ServerHandle != INVALID_HANDLE_VALUE)
+        ObCloseHandle(ServerHandle, KernelMode);
+
+    /* Test in-quota */
+    MaxInstances = 1;
+    OutQuota = 4096;
+    for (i = 0; i < RTL_NUMBER_OF(Quotas); i++)
+    {
+        InQuota = Quotas[i];
+        ServerHandle = INVALID_HANDLE_VALUE;
+        Status = NpCreatePipe(&ServerHandle,
+                              DEVICE_NAMED_PIPE L"\\KmtestNpfsCreateTestPipe",
+                              BYTE_STREAM, QUEUE, BYTE_STREAM, DUPLEX,
+                              MaxInstances,
+                              InQuota,
+                              OutQuota);
+        ok_eq_hex(Status, STATUS_SUCCESS);
+        ok(ServerHandle != NULL && ServerHandle != INVALID_HANDLE_VALUE, "ServerHandle = %p\n", ServerHandle);
+        if (!skip(NT_SUCCESS(Status) && ServerHandle != NULL && ServerHandle != INVALID_HANDLE_VALUE, "No pipe\n"))
+        {
+            NpCheckServerPipe(ServerHandle,
+                              BYTE_STREAM, QUEUE, BYTE_STREAM, DUPLEX,
+                              MaxInstances, 1,
+                              InQuota, 0,
+                              OutQuota, OutQuota,
+                              FILE_PIPE_LISTENING_STATE);
+            ObCloseHandle(ServerHandle, KernelMode);
+            Timeout.QuadPart = -100 * 1000 * 10;
+            Status = KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
+            ok_eq_hex(Status, STATUS_SUCCESS);
+        }
+    }
+}
+
+static
+VOID
+TestCreate(VOID)
+{
+}
+
+static KSTART_ROUTINE RunTest;
+static
+VOID
+NTAPI
+RunTest(
+    IN PVOID Context)
+{
+    UNREFERENCED_PARAMETER(Context);
+    TestCreateNamedPipe();
+    TestCreate();
+}
+
+START_TEST(NpfsCreate)
+{
+    PKTHREAD Thread;
+
+    Thread = KmtStartThread(RunTest, NULL);
+    KmtFinishThread(Thread, NULL);
+}
diff --git a/rostests/kmtests/npfs/NpfsHelpers.c b/rostests/kmtests/npfs/NpfsHelpers.c
new file mode 100644 (file)
index 0000000..f81c61a
--- /dev/null
@@ -0,0 +1,774 @@
+/*
+ * PROJECT:         ReactOS kernel-mode tests
+ * LICENSE:         LGPLv2+ - See COPYING.LIB in the top level directory
+ * PURPOSE:         Kernel-Mode Test Suite Helper functions for NPFS tests
+ * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
+ */
+
+#include <kmt_test.h>
+#include "npfs.h"
+
+NTSTATUS
+NpCreatePipeEx(
+    OUT PHANDLE ServerHandle,
+    IN PCWSTR PipePath,
+    IN ULONG ReadMode,
+    IN ULONG CompletionMode,
+    IN ULONG NamedPipeType,
+    IN ULONG ShareAccess,
+    IN ULONG MaximumInstances,
+    IN ULONG InboundQuota,
+    IN ULONG OutboundQuota,
+    IN ACCESS_MASK DesiredAccess,
+    IN ULONG Disposition,
+    IN ULONG CreateOptions,
+    IN PLARGE_INTEGER DefaultTimeout OPTIONAL)
+{
+    UNICODE_STRING ObjectName;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    NAMED_PIPE_CREATE_PARAMETERS Params;
+    IO_STATUS_BLOCK IoStatusBlock;
+    NTSTATUS Status;
+
+    RtlInitUnicodeString(&ObjectName, PipePath);
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &ObjectName,
+                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                               NULL,
+                               NULL);
+
+    Params.NamedPipeType = NamedPipeType;
+    Params.ReadMode = ReadMode;
+    Params.CompletionMode = CompletionMode;
+    Params.MaximumInstances = MaximumInstances;
+    Params.InboundQuota = InboundQuota;
+    Params.OutboundQuota = OutboundQuota;
+    if (DefaultTimeout)
+    {
+        Params.DefaultTimeout.QuadPart = DefaultTimeout->QuadPart;
+        Params.TimeoutSpecified = TRUE;
+    }
+    else
+    {
+        Params.DefaultTimeout.QuadPart = 0;
+        Params.TimeoutSpecified = FALSE;
+    }
+
+    RtlFillMemory(&IoStatusBlock, sizeof(IoStatusBlock), 0x55);
+    Status = IoCreateFile(ServerHandle,
+                          DesiredAccess,
+                          &ObjectAttributes,
+                          &IoStatusBlock,
+                          NULL, /* AllocationSize */
+                          0, /* FileAttributes */
+                          ShareAccess,
+                          Disposition,
+                          CreateOptions,
+                          NULL, /* EaBuffer */
+                          0, /* EaLength */
+                          CreateFileTypeNamedPipe,
+                          &Params,
+                          0);
+    if (NT_SUCCESS(Status))
+    {
+        ok_eq_hex(IoStatusBlock.Status, Status);
+        ok_eq_ulongptr(IoStatusBlock.Information, FILE_CREATED);
+    }
+    else
+    {
+        ok_eq_hex(IoStatusBlock.Status, 0x55555555UL);
+        ok_eq_ulongptr(IoStatusBlock.Information, 0x5555555555555555ULL);
+    }
+    return Status;
+}
+
+NTSTATUS
+NpCreatePipe(
+    OUT PHANDLE ServerHandle,
+    PCWSTR PipePath,
+    ULONG ReadMode,
+    ULONG CompletionMode,
+    ULONG NamedPipeType,
+    ULONG NamedPipeConfiguration,
+    ULONG MaximumInstances,
+    ULONG InboundQuota,
+    ULONG OutboundQuota)
+{
+    ULONG ShareAccess;
+    LARGE_INTEGER DefaultTimeout;
+
+    if (NamedPipeConfiguration == FILE_PIPE_INBOUND)
+        ShareAccess = FILE_SHARE_WRITE;
+    else if (NamedPipeConfiguration == FILE_PIPE_OUTBOUND)
+        ShareAccess = FILE_SHARE_READ;
+    else if (NamedPipeConfiguration == FILE_PIPE_FULL_DUPLEX)
+        ShareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE;
+
+    DefaultTimeout.QuadPart = -50 * 1000 * 10;
+
+    return NpCreatePipeEx(ServerHandle,
+                          PipePath,
+                          ReadMode,
+                          CompletionMode,
+                          NamedPipeType,
+                          ShareAccess,
+                          MaximumInstances,
+                          InboundQuota,
+                          OutboundQuota,
+                          SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
+                          FILE_OPEN_IF,
+                          FILE_SYNCHRONOUS_IO_NONALERT,
+                          &DefaultTimeout);
+}
+
+NTSTATUS
+NpOpenPipeEx(
+    OUT PHANDLE ClientHandle,
+    IN PCWSTR PipePath,
+    IN ACCESS_MASK DesiredAccess,
+    IN ULONG ShareAccess,
+    IN ULONG Disposition,
+    IN ULONG CreateOptions)
+{
+    UNICODE_STRING ObjectName;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    NTSTATUS Status;
+    IO_STATUS_BLOCK IoStatusBlock;
+
+    RtlInitUnicodeString(&ObjectName, PipePath);
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &ObjectName,
+                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                               NULL,
+                               NULL);
+
+    RtlFillMemory(&IoStatusBlock, sizeof(IoStatusBlock), 0x55);
+    Status = IoCreateFile(ClientHandle,
+                          DesiredAccess,
+                          &ObjectAttributes,
+                          &IoStatusBlock,
+                          NULL, /* AllocationSize */
+                          0, /* FileAttributes */
+                          ShareAccess,
+                          Disposition,
+                          CreateOptions,
+                          NULL, /* EaBuffer */
+                          0, /* EaLength */
+                          CreateFileTypeNone,
+                          NULL,
+                          0);
+    if (NT_SUCCESS(Status))
+    {
+        ok(Status != STATUS_PENDING, "IoCreateFile returned pending\n");
+        ok_eq_hex(IoStatusBlock.Status, Status);
+        ok_eq_ulongptr(IoStatusBlock.Information, FILE_OPENED);
+    }
+    else
+    {
+        ok_eq_hex(IoStatusBlock.Status, 0x55555555UL);
+        ok_eq_ulongptr(IoStatusBlock.Information, 0x5555555555555555ULL);
+    }
+    return Status;
+}
+
+NTSTATUS
+NpOpenPipe(
+    OUT PHANDLE ClientHandle,
+    IN PCWSTR PipePath,
+    IN ULONG NamedPipeConfiguration)
+{
+    ULONG ShareAccess;
+
+    if (NamedPipeConfiguration == FILE_PIPE_INBOUND)
+        ShareAccess = FILE_SHARE_WRITE;
+    else if (NamedPipeConfiguration == FILE_PIPE_OUTBOUND)
+        ShareAccess = FILE_SHARE_READ;
+    else if (NamedPipeConfiguration == FILE_PIPE_FULL_DUPLEX)
+        ShareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE;
+
+    return NpOpenPipeEx(ClientHandle,
+                        PipePath,
+                        SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
+                        ShareAccess,
+                        FILE_OPEN,
+                        FILE_SYNCHRONOUS_IO_NONALERT);
+}
+
+NTSTATUS
+NpControlPipe(
+    IN HANDLE ServerHandle,
+    IN ULONG FsControlCode,
+    IN PVOID InputBuffer,
+    IN ULONG InputBufferLength)
+{
+    NTSTATUS Status;
+    IO_STATUS_BLOCK IoStatusBlock;
+
+    RtlFillMemory(&IoStatusBlock, sizeof(IoStatusBlock), 0x55);
+    Status = ZwFsControlFile(ServerHandle,
+                             NULL,
+                             NULL,
+                             NULL,
+                             &IoStatusBlock,
+                             FsControlCode,
+                             InputBuffer,
+                             InputBufferLength,
+                             NULL,
+                             0);
+    if (Status == STATUS_PENDING)
+    {
+        Status = ZwWaitForSingleObject(ServerHandle,
+                                       FALSE,
+                                       NULL);
+        ok_eq_hex(Status, STATUS_SUCCESS);
+        Status = IoStatusBlock.Status;
+    }
+    if (NT_SUCCESS(Status))
+    {
+        ok_eq_hex(IoStatusBlock.Status, Status);
+        ok_eq_ulongptr(IoStatusBlock.Information, 0);
+    }
+    else
+    {
+        ok_eq_hex(IoStatusBlock.Status, 0x55555555UL);
+        ok_eq_ulongptr(IoStatusBlock.Information, 0x5555555555555555ULL);
+    }
+    return Status;
+}
+
+NTSTATUS
+NpWaitPipe(
+    IN PCWSTR PipeName,
+    IN PLARGE_INTEGER Timeout)
+{
+    NTSTATUS Status;
+    HANDLE RootHandle;
+    UNICODE_STRING RootDirectoryName = RTL_CONSTANT_STRING(DEVICE_NAMED_PIPE);
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    IO_STATUS_BLOCK IoStatusBlock;
+    PFILE_PIPE_WAIT_FOR_BUFFER WaitForBuffer;
+    ULONG NameLength;
+    ULONG BufferSize;
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &RootDirectoryName,
+                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                               NULL,
+                               NULL);
+
+    RtlFillMemory(&IoStatusBlock, sizeof(IoStatusBlock), 0x55);
+    Status = IoCreateFile(&RootHandle,
+                          FILE_READ_ATTRIBUTES | SYNCHRONIZE,
+                          &ObjectAttributes,
+                          &IoStatusBlock,
+                          NULL,
+                          0,
+                          FILE_SHARE_READ | FILE_SHARE_WRITE,
+                          FILE_OPEN,
+                          FILE_SYNCHRONOUS_IO_NONALERT,
+                          NULL,
+                          0,
+                          CreateFileTypeNone,
+                          NULL,
+                          0);
+    if (!NT_SUCCESS(Status))
+    {
+        ok_eq_hex(IoStatusBlock.Status, 0x55555555UL);
+        ok_eq_ulongptr(IoStatusBlock.Information, 0x5555555555555555ULL);
+        return Status;
+    }
+    ok(Status != STATUS_PENDING, "IoCreateFile returned pending\n");
+    ok_eq_hex(IoStatusBlock.Status, Status);
+    ok_eq_ulongptr(IoStatusBlock.Information, FILE_OPENED);
+
+    NameLength = wcslen(PipeName) * sizeof(WCHAR);
+    BufferSize = FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER,
+                              Name[NameLength / sizeof(WCHAR)]);
+    WaitForBuffer = ExAllocatePoolWithTag(NonPagedPool, BufferSize, 'WPmK');
+    if (WaitForBuffer == NULL)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    if (Timeout)
+    {
+        WaitForBuffer->Timeout.QuadPart = Timeout->QuadPart;
+        WaitForBuffer->TimeoutSpecified = TRUE;
+    }
+    else
+    {
+        WaitForBuffer->Timeout.QuadPart = 0;
+        WaitForBuffer->TimeoutSpecified = FALSE;
+    }
+    WaitForBuffer->NameLength = NameLength;
+    RtlCopyMemory(WaitForBuffer->Name, PipeName, NameLength);
+    Status = NpControlPipe(RootHandle,
+                           FSCTL_PIPE_WAIT,
+                           WaitForBuffer,
+                           BufferSize);
+    ExFreePoolWithTag(WaitForBuffer, 'WPmK');
+    return Status;
+}
+
+NTSTATUS
+NpReadPipe(
+    IN HANDLE PipeHandle,
+    OUT PVOID Buffer,
+    IN ULONG BufferSize,
+    OUT PULONG_PTR BytesRead)
+{
+    NTSTATUS Status;
+    IO_STATUS_BLOCK IoStatusBlock;
+    BOOLEAN PendingReturned = FALSE;
+
+    RtlFillMemory(&IoStatusBlock, sizeof(IoStatusBlock), 0x55);
+    Status = ZwReadFile(PipeHandle,
+                        NULL,
+                        NULL,
+                        NULL,
+                        &IoStatusBlock,
+                        Buffer,
+                        BufferSize,
+                        NULL,
+                        NULL);
+    if (Status == STATUS_PENDING)
+    {
+        Status = ZwWaitForSingleObject(PipeHandle,
+                                       FALSE,
+                                       NULL);
+        ok_eq_hex(Status, STATUS_SUCCESS);
+        Status = IoStatusBlock.Status;
+        PendingReturned = TRUE;
+    }
+    if (NT_SUCCESS(Status))
+    {
+        ok_eq_hex(IoStatusBlock.Status, Status);
+        *BytesRead = IoStatusBlock.Information;
+    }
+    else
+    {
+        if (PendingReturned)
+        {
+            ok_eq_hex(IoStatusBlock.Status, Status);
+            ok_eq_ulongptr(IoStatusBlock.Information, 0);
+        }
+        else
+        {
+            ok_eq_hex(IoStatusBlock.Status, 0x55555555UL);
+            ok_eq_ulongptr(IoStatusBlock.Information, 0x5555555555555555ULL);
+        }
+        *BytesRead = 0;
+    }
+    return Status;
+}
+
+NTSTATUS
+NpWritePipe(
+    IN HANDLE PipeHandle,
+    IN const VOID *Buffer,
+    IN ULONG BufferSize,
+    OUT PULONG_PTR BytesWritten)
+{
+    NTSTATUS Status;
+    IO_STATUS_BLOCK IoStatusBlock;
+
+    RtlFillMemory(&IoStatusBlock, sizeof(IoStatusBlock), 0x55);
+    Status = ZwWriteFile(PipeHandle,
+                         NULL,
+                         NULL,
+                         NULL,
+                         &IoStatusBlock,
+                         (PVOID)Buffer,
+                         BufferSize,
+                         NULL,
+                         NULL);
+    if (Status == STATUS_PENDING)
+    {
+        Status = ZwWaitForSingleObject(PipeHandle,
+                                       FALSE,
+                                       NULL);
+        ok_eq_hex(Status, STATUS_SUCCESS);
+        Status = IoStatusBlock.Status;
+    }
+    if (NT_SUCCESS(Status))
+    {
+        ok_eq_hex(IoStatusBlock.Status, Status);
+        *BytesWritten = IoStatusBlock.Information;
+    }
+    else
+    {
+        ok_eq_hex(IoStatusBlock.Status, 0x55555555UL);
+        ok_eq_ulongptr(IoStatusBlock.Information, 0x5555555555555555ULL);
+        *BytesWritten = 0;
+    }
+    return Status;
+}
+
+static
+BOOLEAN
+CheckBuffer(
+    PVOID Buffer,
+    SIZE_T Size,
+    UCHAR Value)
+{
+    PUCHAR Array = Buffer;
+    SIZE_T i;
+
+    for (i = 0; i < Size; i++)
+        if (Array[i] != Value)
+        {
+            trace("Expected %x, found %x at offset %lu\n", Value, Array[i], (ULONG)i);
+            return FALSE;
+        }
+    return TRUE;
+}
+
+#define ok_eq_print_(value, expected, spec, FileAndLine) \
+                                            KmtOk((value) == (expected), FileAndLine, #value " = " spec ", expected " spec "\n", value, expected)
+#define ok_eq_ulong_(value, expected)       ok_eq_print_(value, expected, "%lu", FileAndLine)
+#define ok_eq_ulonglong_(value, expected)   ok_eq_print_(value, expected, "%I64u", FileAndLine)
+#ifndef _WIN64
+#define ok_eq_ulongptr_(value, expected)    ok_eq_print_(value, (ULONG_PTR)(expected), "%lu", FileAndLine)
+#elif defined _WIN64
+#define ok_eq_ulongptr_(value, expected)    ok_eq_print_(value, (ULONG_PTR)(expected), "%I64u", FileAndLine)
+#endif
+#define ok_eq_hex_(value, expected)         ok_eq_print_(value, expected, "0x%08lx", FileAndLine)
+
+VOID
+NpCheckServerPipe_(
+    IN HANDLE ServerHandle,
+    /* PipeInformation */
+    IN ULONG ReadMode,
+    IN ULONG CompletionMode,
+    /* PipeLocalInformation */
+    IN ULONG NamedPipeType,
+    IN ULONG NamedPipeConfiguration,
+    IN ULONG MaximumInstances,
+    IN ULONG CurrentInstances,
+    IN ULONG InboundQuota,
+    IN ULONG ReadDataAvailable,
+    IN ULONG OutboundQuota,
+    IN ULONG WriteQuotaAvailable,
+    IN ULONG NamedPipeState,
+    /* PipeRemoteInformation */
+    /* */
+    IN PCSTR FileAndLine)
+{
+    NTSTATUS Status;
+    IO_STATUS_BLOCK IoStatusBlock;
+    FILE_PIPE_INFORMATION PipeInfo;
+    FILE_PIPE_LOCAL_INFORMATION PipeLocalInfo;
+    FILE_PIPE_REMOTE_INFORMATION PipeRemoteInfo;
+
+    RtlFillMemory(&IoStatusBlock, sizeof(IoStatusBlock), 0x55);
+    RtlFillMemory(&PipeInfo, sizeof(PipeInfo), 0x55);
+    Status = ZwQueryInformationFile(ServerHandle,
+                                    &IoStatusBlock,
+                                    &PipeInfo,
+                                    sizeof(PipeInfo),
+                                    FilePipeInformation);
+    ok_eq_hex_(Status, STATUS_SUCCESS);
+    ok_eq_hex_(IoStatusBlock.Status, STATUS_SUCCESS);
+    ok_eq_ulongptr_(IoStatusBlock.Information, sizeof(PipeInfo));
+    ok_eq_ulong_(PipeInfo.ReadMode, ReadMode);
+    ok_eq_ulong_(PipeInfo.CompletionMode, CompletionMode);
+
+    RtlFillMemory(&IoStatusBlock, sizeof(IoStatusBlock), 0x55);
+    RtlFillMemory(&PipeLocalInfo, sizeof(PipeLocalInfo), 0x55);
+    Status = ZwQueryInformationFile(ServerHandle,
+                                    &IoStatusBlock,
+                                    &PipeLocalInfo,
+                                    sizeof(PipeLocalInfo),
+                                    FilePipeLocalInformation);
+    ok_eq_hex_(Status, STATUS_SUCCESS);
+    ok_eq_hex_(IoStatusBlock.Status, STATUS_SUCCESS);
+    ok_eq_ulongptr_(IoStatusBlock.Information, sizeof(PipeLocalInfo));
+    ok_eq_ulong_(PipeLocalInfo.NamedPipeType, NamedPipeType);
+    ok_eq_ulong_(PipeLocalInfo.NamedPipeConfiguration, NamedPipeConfiguration);
+    ok_eq_ulong_(PipeLocalInfo.MaximumInstances, MaximumInstances);
+    ok_eq_ulong_(PipeLocalInfo.CurrentInstances, CurrentInstances);
+    ok_eq_ulong_(PipeLocalInfo.InboundQuota, InboundQuota);
+    ok_eq_ulong_(PipeLocalInfo.ReadDataAvailable, ReadDataAvailable);
+    ok_eq_ulong_(PipeLocalInfo.OutboundQuota, OutboundQuota);
+    ok_eq_ulong_(PipeLocalInfo.WriteQuotaAvailable, WriteQuotaAvailable);
+    ok_eq_ulong_(PipeLocalInfo.NamedPipeState, NamedPipeState);
+    ok_eq_ulong_(PipeLocalInfo.NamedPipeEnd, (ULONG)FILE_PIPE_SERVER_END);
+
+    RtlFillMemory(&IoStatusBlock, sizeof(IoStatusBlock), 0x55);
+    RtlFillMemory(&PipeRemoteInfo, sizeof(PipeRemoteInfo), 0x55);
+    Status = ZwQueryInformationFile(ServerHandle,
+                                    &IoStatusBlock,
+                                    &PipeRemoteInfo,
+                                    sizeof(PipeRemoteInfo),
+                                    FilePipeInformation);
+    ok_eq_hex_(Status, STATUS_SUCCESS);
+    ok_eq_hex_(IoStatusBlock.Status, STATUS_SUCCESS);
+    ok_eq_ulongptr_(IoStatusBlock.Information, RTL_SIZEOF_THROUGH_FIELD(FILE_PIPE_REMOTE_INFORMATION, CollectDataTime));
+    ok_eq_ulonglong_(PipeRemoteInfo.CollectDataTime.QuadPart, 0ULL);
+    ok_eq_ulong_(PipeRemoteInfo.MaximumCollectionCount, 0x55555555UL);
+}
+
+VOID
+NpCheckClientPipe_(
+    IN HANDLE ClientHandle,
+    /* PipeInformation */
+    IN ULONG ReadMode,
+    IN ULONG CompletionMode,
+    /* PipeLocalInformation */
+    IN ULONG NamedPipeType,
+    IN ULONG NamedPipeConfiguration,
+    IN ULONG MaximumInstances,
+    IN ULONG CurrentInstances,
+    IN ULONG InboundQuota,
+    IN ULONG ReadDataAvailable,
+    IN ULONG OutboundQuota,
+    IN ULONG WriteQuotaAvailable,
+    IN ULONG NamedPipeState,
+    /* PipeRemoteInformation */
+    /* */
+    IN PCSTR FileAndLine)
+{
+    NTSTATUS Status;
+    IO_STATUS_BLOCK IoStatusBlock;
+    FILE_PIPE_INFORMATION PipeInfo;
+    FILE_PIPE_LOCAL_INFORMATION PipeLocalInfo;
+    FILE_PIPE_REMOTE_INFORMATION PipeRemoteInfo;
+
+    RtlFillMemory(&IoStatusBlock, sizeof(IoStatusBlock), 0x55);
+    RtlFillMemory(&PipeInfo, sizeof(PipeInfo), 0x55);
+    Status = ZwQueryInformationFile(ClientHandle,
+                                    &IoStatusBlock,
+                                    &PipeInfo,
+                                    sizeof(PipeInfo),
+                                    FilePipeInformation);
+    ok_eq_hex_(Status, STATUS_SUCCESS);
+    ok_eq_hex_(IoStatusBlock.Status, STATUS_SUCCESS);
+    ok_eq_ulongptr_(IoStatusBlock.Information, sizeof(PipeInfo));
+    ok_eq_ulong_(PipeInfo.ReadMode, ReadMode);
+    ok_eq_ulong_(PipeInfo.CompletionMode, CompletionMode);
+
+    RtlFillMemory(&IoStatusBlock, sizeof(IoStatusBlock), 0x55);
+    RtlFillMemory(&PipeLocalInfo, sizeof(PipeLocalInfo), 0x55);
+    Status = ZwQueryInformationFile(ClientHandle,
+                                    &IoStatusBlock,
+                                    &PipeLocalInfo,
+                                    sizeof(PipeLocalInfo),
+                                    FilePipeLocalInformation);
+    ok_eq_hex_(Status, STATUS_SUCCESS);
+    ok_eq_hex_(IoStatusBlock.Status, STATUS_SUCCESS);
+    ok_eq_ulongptr_(IoStatusBlock.Information, sizeof(PipeLocalInfo));
+    ok_eq_ulong_(PipeLocalInfo.NamedPipeType, NamedPipeType);
+    ok_eq_ulong_(PipeLocalInfo.NamedPipeConfiguration, NamedPipeConfiguration);
+    ok_eq_ulong_(PipeLocalInfo.MaximumInstances, MaximumInstances);
+    ok_eq_ulong_(PipeLocalInfo.CurrentInstances, CurrentInstances);
+    ok_eq_ulong_(PipeLocalInfo.InboundQuota, InboundQuota);
+    ok_eq_ulong_(PipeLocalInfo.ReadDataAvailable, ReadDataAvailable);
+    ok_eq_ulong_(PipeLocalInfo.OutboundQuota, OutboundQuota);
+    ok_eq_ulong_(PipeLocalInfo.WriteQuotaAvailable, WriteQuotaAvailable);
+    ok_eq_ulong_(PipeLocalInfo.NamedPipeState, NamedPipeState);
+    ok_eq_ulong_(PipeLocalInfo.NamedPipeEnd, (ULONG)FILE_PIPE_CLIENT_END);
+
+    RtlFillMemory(&IoStatusBlock, sizeof(IoStatusBlock), 0x55);
+    RtlFillMemory(&PipeRemoteInfo, sizeof(PipeRemoteInfo), 0x55);
+    Status = ZwQueryInformationFile(ClientHandle,
+                                    &IoStatusBlock,
+                                    &PipeRemoteInfo,
+                                    sizeof(PipeRemoteInfo),
+                                    FilePipeInformation);
+    ok_eq_hex_(Status, STATUS_SUCCESS);
+    ok_eq_hex_(IoStatusBlock.Status, STATUS_SUCCESS);
+    ok_eq_ulongptr_(IoStatusBlock.Information, RTL_SIZEOF_THROUGH_FIELD(FILE_PIPE_REMOTE_INFORMATION, CollectDataTime));
+    ok_eq_ulonglong_(PipeRemoteInfo.CollectDataTime.QuadPart, 0ULL);
+    ok_eq_ulong_(PipeRemoteInfo.MaximumCollectionCount, 0x55555555UL);
+}
+
+VOID
+NpQueryPipe_(
+    IN HANDLE PipeHandle,
+    IN NTSTATUS ExpectedStatus,
+    IN PCSTR FileAndLine)
+{
+    NTSTATUS Status;
+    IO_STATUS_BLOCK IoStatusBlock;
+    FILE_PIPE_INFORMATION PipeInfo;
+    FILE_PIPE_LOCAL_INFORMATION PipeLocalInfo;
+    FILE_PIPE_REMOTE_INFORMATION PipeRemoteInfo;
+
+    ASSERT(!NT_SUCCESS(ExpectedStatus));
+
+    RtlFillMemory(&IoStatusBlock, sizeof(IoStatusBlock), 0x55);
+    RtlFillMemory(&PipeInfo, sizeof(PipeInfo), 0x55);
+    Status = ZwQueryInformationFile(PipeHandle,
+                                    &IoStatusBlock,
+                                    &PipeInfo,
+                                    sizeof(PipeInfo),
+                                    FilePipeInformation);
+    ok_eq_hex_(Status, ExpectedStatus);
+    ok_bool_true(CheckBuffer(&IoStatusBlock, sizeof(IoStatusBlock), 0x55), "CheckBuffer returned");
+    ok_bool_true(CheckBuffer(&PipeInfo, sizeof(PipeInfo), 0x55), "CheckBuffer returned");
+
+    RtlFillMemory(&IoStatusBlock, sizeof(IoStatusBlock), 0x55);
+    RtlFillMemory(&PipeLocalInfo, sizeof(PipeLocalInfo), 0x55);
+    Status = ZwQueryInformationFile(PipeHandle,
+                                    &IoStatusBlock,
+                                    &PipeLocalInfo,
+                                    sizeof(PipeLocalInfo),
+                                    FilePipeLocalInformation);
+    ok_eq_hex_(Status, ExpectedStatus);
+    ok_bool_true(CheckBuffer(&IoStatusBlock, sizeof(IoStatusBlock), 0x55), "CheckBuffer returned");
+    ok_bool_true(CheckBuffer(&PipeLocalInfo, sizeof(PipeLocalInfo), 0x55), "CheckBuffer returned");
+
+    RtlFillMemory(&IoStatusBlock, sizeof(IoStatusBlock), 0x55);
+    RtlFillMemory(&PipeRemoteInfo, sizeof(PipeRemoteInfo), 0x55);
+    Status = ZwQueryInformationFile(PipeHandle,
+                                    &IoStatusBlock,
+                                    &PipeRemoteInfo,
+                                    sizeof(PipeRemoteInfo),
+                                    FilePipeInformation);
+    ok_eq_hex_(Status, ExpectedStatus);
+    ok_bool_true(CheckBuffer(&IoStatusBlock, sizeof(IoStatusBlock), 0x55), "CheckBuffer returned");
+    ok_bool_true(CheckBuffer(&PipeRemoteInfo, sizeof(PipeRemoteInfo), 0x55), "CheckBuffer returned");
+}
+
+static KSTART_ROUTINE PipeWorkerThread;
+static
+VOID
+NTAPI
+PipeWorkerThread(
+    IN PVOID ThreadContext)
+{
+    PTHREAD_CONTEXT Context = ThreadContext;
+    PVOID WaitEvents[2] = { &Context->ThreadDoneEvent,
+                            &Context->StartWorkEvent };
+    NTSTATUS Status;
+
+    while (TRUE)
+    {
+        Status = KeWaitForMultipleObjects(RTL_NUMBER_OF(WaitEvents),
+                                          WaitEvents,
+                                          WaitAny,
+                                          Executive,
+                                          KernelMode,
+                                          FALSE,
+                                          NULL,
+                                          NULL);
+        if (Status == STATUS_WAIT_0)
+            break;
+        ASSERT(Status == STATUS_WAIT_1);
+
+        Context->Work(Context);
+
+        KeSetEvent(&Context->WorkCompleteEvent, IO_NO_INCREMENT, TRUE);
+    }
+}
+
+VOID
+StartWorkerThread(
+    OUT PTHREAD_CONTEXT Context)
+{
+    KeInitializeEvent(&Context->ThreadDoneEvent, NotificationEvent, FALSE);
+    KeInitializeEvent(&Context->StartWorkEvent, SynchronizationEvent, FALSE);
+    KeInitializeEvent(&Context->WorkCompleteEvent, NotificationEvent, TRUE);
+
+    Context->Thread = KmtStartThread(PipeWorkerThread, Context);
+}
+
+VOID
+FinishWorkerThread(
+    IN PTHREAD_CONTEXT Context)
+{
+    KmtFinishThread(Context->Thread, &Context->ThreadDoneEvent);
+}
+
+BOOLEAN
+WaitForWork(
+    IN PTHREAD_CONTEXT Context,
+    IN ULONG MilliSeconds)
+{
+    LARGE_INTEGER Timeout;
+    NTSTATUS Status;
+
+    Timeout.QuadPart = -10 * 1000 * (LONGLONG)MilliSeconds;
+    Status = KeWaitForSingleObject(&Context->WorkCompleteEvent,
+                                   Executive,
+                                   KernelMode,
+                                   FALSE,
+                                   &Timeout);
+    ok(Status == STATUS_SUCCESS || Status == STATUS_TIMEOUT, "Wait status %lx\n", Status);
+    return Status != STATUS_TIMEOUT;
+}
+
+BOOLEAN
+TriggerWork(
+    IN PTHREAD_CONTEXT Context,
+    IN ULONG MilliSeconds)
+{
+    NTSTATUS Status;
+
+    Status = KeWaitForSingleObject(&Context->WorkCompleteEvent,
+                                   Executive,
+                                   KernelMode,
+                                   FALSE,
+                                   NULL);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    KeResetEvent(&Context->WorkCompleteEvent);
+    KeSetEvent(&Context->StartWorkEvent, IO_NO_INCREMENT, TRUE);
+    return WaitForWork(Context, MilliSeconds);
+}
+
+PKTHREAD
+KmtStartThread(
+    IN PKSTART_ROUTINE StartRoutine,
+    IN PVOID StartContext OPTIONAL)
+{
+    NTSTATUS Status;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    HANDLE ThreadHandle;
+    PVOID ThreadObject = NULL;
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               NULL,
+                               OBJ_KERNEL_HANDLE,
+                               NULL,
+                               NULL);
+    ThreadHandle = INVALID_HANDLE_VALUE;
+    Status = PsCreateSystemThread(&ThreadHandle,
+                                  SYNCHRONIZE,
+                                  &ObjectAttributes,
+                                  NULL,
+                                  NULL,
+                                  StartRoutine,
+                                  StartContext);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    if (!skip(NT_SUCCESS(Status) && ThreadHandle != NULL && ThreadHandle != INVALID_HANDLE_VALUE, "No thread\n"))
+    {
+        Status = ObReferenceObjectByHandle(ThreadHandle,
+                                           SYNCHRONIZE,
+                                           PsThreadType,
+                                           KernelMode,
+                                           &ThreadObject,
+                                           NULL);
+        ok_eq_hex(Status, STATUS_SUCCESS);
+        ObCloseHandle(ThreadHandle, KernelMode);
+    }
+    return ThreadObject;
+}
+
+VOID
+KmtFinishThread(
+    IN PKTHREAD Thread OPTIONAL,
+    IN PKEVENT Event OPTIONAL)
+{
+    NTSTATUS Status;
+
+    if (skip(Thread != NULL, "No thread\n"))
+        return;
+
+    if (Event)
+        KeSetEvent(Event, IO_NO_INCREMENT, TRUE);
+    Status = KeWaitForSingleObject(Thread,
+                                   Executive,
+                                   KernelMode,
+                                   FALSE,
+                                   NULL);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    ObDereferenceObject(Thread);
+}
diff --git a/rostests/kmtests/npfs/NpfsReadWrite.c b/rostests/kmtests/npfs/NpfsReadWrite.c
new file mode 100644 (file)
index 0000000..bfbd009
--- /dev/null
@@ -0,0 +1,598 @@
+/*
+ * PROJECT:         ReactOS kernel-mode tests
+ * LICENSE:         LGPLv2+ - See COPYING.LIB in the top level directory
+ * PURPOSE:         Kernel-Mode Test Suite NPFS Read/Write test
+ * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
+ */
+
+#include <kmt_test.h>
+#include "npfs.h"
+
+typedef struct _READ_WRITE_TEST_CONTEXT
+{
+    PCWSTR PipePath;
+    BOOLEAN ServerSynchronous;
+    BOOLEAN ClientSynchronous;
+} READ_WRITE_TEST_CONTEXT, *PREAD_WRITE_TEST_CONTEXT;
+
+#define MAX_INSTANCES   5
+#define IN_QUOTA        4096
+#define OUT_QUOTA       4096
+
+#define MakeServer(ServerHandle, PipePath, ServerSynchronous)           \
+    NpCreatePipeEx(ServerHandle,                                        \
+                   PipePath,                                            \
+                   BYTE_STREAM,                                         \
+                   QUEUE,                                               \
+                   BYTE_STREAM,                                         \
+                   FILE_SHARE_READ | FILE_SHARE_WRITE,                  \
+                   MAX_INSTANCES,                                       \
+                   IN_QUOTA,                                            \
+                   OUT_QUOTA,                                           \
+                   SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,          \
+                   FILE_OPEN_IF,                                        \
+                   (ServerSynchronous) ? FILE_SYNCHRONOUS_IO_NONALERT   \
+                                       : 0,                             \
+                   &DefaultTimeout)
+
+#define CheckServer(ServerHandle, State)                                \
+    NpCheckServerPipe(ServerHandle,                                     \
+                      BYTE_STREAM, QUEUE, BYTE_STREAM, DUPLEX,          \
+                      MAX_INSTANCES, 1,                                 \
+                      IN_QUOTA, 0,                                      \
+                      OUT_QUOTA, OUT_QUOTA,                             \
+                      State)
+
+#define CheckClient(ClientHandle, State)                                \
+    NpCheckClientPipe(ClientHandle,                                     \
+                      BYTE_STREAM, QUEUE, BYTE_STREAM, DUPLEX,          \
+                      MAX_INSTANCES, 1,                                 \
+                      IN_QUOTA, 0,                                      \
+                      OUT_QUOTA, OUT_QUOTA,                             \
+                      State)
+
+#define CheckServerQuota(ServerHandle, InQ, OutQ)                       \
+    NpCheckServerPipe(ServerHandle,                                     \
+                      BYTE_STREAM, QUEUE, BYTE_STREAM, DUPLEX,          \
+                      MAX_INSTANCES, 1,                                 \
+                      IN_QUOTA, InQ,                                    \
+                      OUT_QUOTA, OUT_QUOTA - (OutQ),                    \
+                      FILE_PIPE_CONNECTED_STATE)
+
+#define CheckClientQuota(ClientHandle, InQ, OutQ)                       \
+    NpCheckClientPipe(ClientHandle,                                     \
+                      BYTE_STREAM, QUEUE, BYTE_STREAM, DUPLEX,          \
+                      MAX_INSTANCES, 1,                                 \
+                      IN_QUOTA, InQ,                                    \
+                      OUT_QUOTA, OUT_QUOTA - (OutQ),                    \
+                      FILE_PIPE_CONNECTED_STATE)
+
+#define CheckPipeContext(Context, ExpectedStatus, ExpectedBytes) do             \
+{                                                                           \
+    ok_bool_true(Okay, "CheckPipeContext");                                     \
+    ok_eq_hex((Context)->ReadWrite.Status, ExpectedStatus);                 \
+    ok_eq_ulongptr((Context)->ReadWrite.BytesTransferred, ExpectedBytes);   \
+} while (0)
+
+static
+VOID
+ConnectPipe(
+    IN OUT PTHREAD_CONTEXT Context)
+{
+    HANDLE ClientHandle;
+
+    ClientHandle = NULL;
+    Context->Connect.Status = NpOpenPipeEx(&ClientHandle,
+                                           Context->Connect.PipePath,
+                                           SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
+                                           FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                           FILE_OPEN,
+                                           Context->Connect.ClientSynchronous ? FILE_SYNCHRONOUS_IO_NONALERT
+                                                                              : 0);
+    Context->Connect.ClientHandle = ClientHandle;
+}
+
+static
+VOID
+ListenPipe(
+    IN OUT PTHREAD_CONTEXT Context)
+{
+    Context->Listen.Status = NpListenPipe(Context->Listen.ServerHandle);
+}
+
+static
+VOID
+ReadPipe(
+    IN OUT PTHREAD_CONTEXT Context)
+{
+    Context->ReadWrite.Status = NpReadPipe(Context->ReadWrite.PipeHandle,
+                                           Context->ReadWrite.Buffer,
+                                           Context->ReadWrite.BufferSize,
+                                           (PULONG_PTR)&Context->ReadWrite.BytesTransferred);
+}
+
+static
+VOID
+WritePipe(
+    IN OUT PTHREAD_CONTEXT Context)
+{
+    Context->ReadWrite.Status = NpWritePipe(Context->ReadWrite.PipeHandle,
+                                            Context->ReadWrite.Buffer,
+                                            Context->ReadWrite.BufferSize,
+                                            (PULONG_PTR)&Context->ReadWrite.BytesTransferred);
+}
+
+static
+BOOLEAN
+CheckConnectPipe(
+    IN PTHREAD_CONTEXT Context,
+    IN PCWSTR PipePath,
+    IN BOOLEAN ClientSynchronous,
+    IN ULONG MilliSeconds)
+{
+    Context->Work = ConnectPipe;
+    Context->Connect.PipePath = PipePath;
+    Context->Connect.ClientSynchronous = ClientSynchronous;
+    return TriggerWork(Context, MilliSeconds);
+}
+
+static
+BOOLEAN
+CheckListenPipe(
+    IN PTHREAD_CONTEXT Context,
+    IN HANDLE ServerHandle,
+    IN ULONG MilliSeconds)
+{
+    Context->Work = ListenPipe;
+    Context->Listen.ServerHandle = ServerHandle;
+    return TriggerWork(Context, MilliSeconds);
+}
+
+static
+BOOLEAN
+CheckReadPipe(
+    IN PTHREAD_CONTEXT Context,
+    IN HANDLE PipeHandle,
+    OUT PVOID Buffer,
+    IN ULONG BufferSize,
+    IN ULONG MilliSeconds)
+{
+    Context->Work = ReadPipe;
+    Context->ReadWrite.PipeHandle = PipeHandle;
+    Context->ReadWrite.Buffer = Buffer;
+    Context->ReadWrite.BufferSize = BufferSize;
+    return TriggerWork(Context, MilliSeconds);
+}
+
+static
+BOOLEAN
+CheckWritePipe(
+    IN PTHREAD_CONTEXT Context,
+    IN HANDLE PipeHandle,
+    IN const VOID *Buffer,
+    IN ULONG BufferSize,
+    IN ULONG MilliSeconds)
+{
+    Context->Work = WritePipe;
+    Context->ReadWrite.PipeHandle = PipeHandle;
+    Context->ReadWrite.Buffer = (PVOID)Buffer;
+    Context->ReadWrite.BufferSize = BufferSize;
+    return TriggerWork(Context, MilliSeconds);
+}
+
+static KSTART_ROUTINE TestReadWrite;
+static
+VOID
+NTAPI
+TestReadWrite(
+    IN PVOID Context)
+{
+    PREAD_WRITE_TEST_CONTEXT TestContext = Context;
+    PCWSTR PipePath = TestContext->PipePath;
+    BOOLEAN ServerSynchronous = TestContext->ServerSynchronous;
+    BOOLEAN ClientSynchronous = TestContext->ClientSynchronous;
+    NTSTATUS Status;
+    HANDLE ServerHandle;
+    LARGE_INTEGER DefaultTimeout;
+    THREAD_CONTEXT ConnectContext;
+    THREAD_CONTEXT ListenContext;
+    THREAD_CONTEXT ClientReadContext;
+    THREAD_CONTEXT ClientWriteContext;
+    THREAD_CONTEXT ServerReadContext;
+    THREAD_CONTEXT ServerWriteContext;
+    BOOLEAN Okay;
+    HANDLE ClientHandle;
+    UCHAR ReadBuffer[128];
+    UCHAR WriteBuffer[128];
+
+    StartWorkerThread(&ConnectContext);
+    StartWorkerThread(&ListenContext);
+    StartWorkerThread(&ClientReadContext);
+    StartWorkerThread(&ClientWriteContext);
+    StartWorkerThread(&ServerReadContext);
+    StartWorkerThread(&ServerWriteContext);
+
+    DefaultTimeout.QuadPart = -50 * 1000 * 10;
+
+    /* Server should start out listening */
+    Status = MakeServer(&ServerHandle, PipePath, ServerSynchronous);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    CheckServer(ServerHandle, FILE_PIPE_LISTENING_STATE);
+
+    Okay = CheckWritePipe(&ServerWriteContext, ServerHandle, NULL, 0, 100);
+    ok_bool_true(Okay, "CheckWritePipe returned");
+    ok_eq_ulongptr(ServerWriteContext.ReadWrite.BytesTransferred, 0);
+    ok_eq_hex(ServerWriteContext.ReadWrite.Status, STATUS_PIPE_LISTENING);
+
+    Okay = CheckReadPipe(&ServerReadContext, ServerHandle, NULL, 0, 100);
+    ok_bool_true(Okay, "CheckReadPipe returned");
+    ok_eq_ulongptr(ServerReadContext.ReadWrite.BytesTransferred, 0);
+    ok_eq_hex(ServerReadContext.ReadWrite.Status, STATUS_PIPE_LISTENING);
+
+    /* Connect a client */
+    Okay = CheckConnectPipe(&ConnectContext, PipePath, ClientSynchronous, 100);
+    ok_bool_true(Okay, "CheckConnectPipe returned");
+    ok_eq_hex(ConnectContext.Connect.Status, STATUS_SUCCESS);
+    ClientHandle = ConnectContext.Connect.ClientHandle;
+    CheckClient(ClientHandle, FILE_PIPE_CONNECTED_STATE);
+    CheckServer(ServerHandle, FILE_PIPE_CONNECTED_STATE);
+
+    /** Server to client, write first, 1 byte */
+    WriteBuffer[0] = 'A';
+    ReadBuffer[0] = 'X';
+    Okay = CheckWritePipe(&ServerWriteContext, ServerHandle, WriteBuffer, 1, 100);
+    CheckPipeContext(&ServerWriteContext, STATUS_SUCCESS, 1);
+    CheckServerQuota(ServerHandle, 0, 1); CheckClientQuota(ClientHandle, 1, 0);
+    Okay = CheckReadPipe(&ClientReadContext, ClientHandle, ReadBuffer, 1, 100);
+    CheckPipeContext(&ClientReadContext, STATUS_SUCCESS, 1);
+    ok_eq_uint(ReadBuffer[0], 'A');
+    CheckServerQuota(ServerHandle, 0, 0); CheckClientQuota(ClientHandle, 0, 0);
+
+    /** Server to client, read first, 1 byte */
+    WriteBuffer[0] = 'B';
+    ReadBuffer[0] = 'X';
+    Okay = CheckReadPipe(&ClientReadContext, ClientHandle, ReadBuffer, 1, 100);
+    ok_bool_false(Okay, "CheckReadPipe returned");
+    CheckServerQuota(ServerHandle, 0, 1);
+    Okay = CheckWritePipe(&ServerWriteContext, ServerHandle, WriteBuffer, 1, 100);
+    CheckPipeContext(&ServerWriteContext, STATUS_SUCCESS, 1);
+    Okay = WaitForWork(&ClientReadContext, 100);
+    CheckPipeContext(&ClientReadContext, STATUS_SUCCESS, 1);
+    ok_eq_uint(ReadBuffer[0], 'B');
+    CheckServerQuota(ServerHandle, 0, 0); CheckClientQuota(ClientHandle, 0, 0);
+
+    /** Client to server, write first, 1 byte */
+    WriteBuffer[0] = 'C';
+    ReadBuffer[0] = 'X';
+    Okay = CheckWritePipe(&ClientWriteContext, ClientHandle, WriteBuffer, 1, 100);
+    CheckPipeContext(&ClientWriteContext, STATUS_SUCCESS, 1);
+    CheckClientQuota(ClientHandle, 0, 1); CheckServerQuota(ServerHandle, 1, 0);
+    Okay = CheckReadPipe(&ServerReadContext, ServerHandle, ReadBuffer, 1, 100);
+    CheckPipeContext(&ServerReadContext, STATUS_SUCCESS, 1);
+    ok_eq_uint(ReadBuffer[0], 'C');
+    CheckClientQuota(ClientHandle, 0, 0); CheckServerQuota(ServerHandle, 0, 0);
+
+    /** Client to server, read first, 1 byte */
+    WriteBuffer[0] = 'D';
+    ReadBuffer[0] = 'X';
+    Okay = CheckReadPipe(&ServerReadContext, ServerHandle, ReadBuffer, 1, 100);
+    ok_bool_false(Okay, "CheckReadPipe returned");
+    CheckClientQuota(ClientHandle, 0, 1);
+    Okay = CheckWritePipe(&ClientWriteContext, ClientHandle, WriteBuffer, 1, 100);
+    CheckPipeContext(&ClientWriteContext, STATUS_SUCCESS, 1);
+    Okay = WaitForWork(&ServerReadContext, 100);
+    CheckPipeContext(&ServerReadContext, STATUS_SUCCESS, 1);
+    ok_eq_uint(ReadBuffer[0], 'D');
+    CheckClientQuota(ClientHandle, 0, 0); CheckServerQuota(ServerHandle, 0, 0);
+
+    /** Server to client, write 0 bytes */
+    Okay = CheckWritePipe(&ServerWriteContext, ServerHandle, (PVOID)1, 0, 100);
+    CheckPipeContext(&ServerWriteContext, STATUS_SUCCESS, 0);
+    CheckServerQuota(ServerHandle, 0, 0); CheckClientQuota(ClientHandle, 0, 0);
+
+    /** Client to Server, write 0 bytes */
+    Okay = CheckWritePipe(&ClientWriteContext, ClientHandle, (PVOID)1, 0, 100);
+    CheckPipeContext(&ClientWriteContext, STATUS_SUCCESS, 0);
+    CheckClientQuota(ClientHandle, 0, 0); CheckServerQuota(ServerHandle, 0, 0);
+
+    /** Server to client, read 0 bytes blocks, write 0 bytes does not unblock, write 1 byte unblocks */
+    WriteBuffer[0] = 'E';
+    ReadBuffer[0] = 'X';
+    Okay = CheckReadPipe(&ClientReadContext, ClientHandle, (PVOID)1, 0, 100);
+    ok_bool_false(Okay, "CheckReadPipe returned");
+    CheckServerQuota(ServerHandle, 0, 0);
+    Okay = CheckWritePipe(&ServerWriteContext, ServerHandle, (PVOID)1, 0, 100);
+    CheckPipeContext(&ServerWriteContext, STATUS_SUCCESS, 0);
+    Okay = WaitForWork(&ClientReadContext, 100);
+    ok_bool_false(Okay, "WaitForWork returned");
+    CheckServerQuota(ServerHandle, 0, 0);
+    Okay = CheckWritePipe(&ServerWriteContext, ServerHandle, WriteBuffer, 1, 100);
+    CheckPipeContext(&ServerWriteContext, STATUS_SUCCESS, 1);
+    Okay = WaitForWork(&ClientReadContext, 100);
+    CheckPipeContext(&ClientReadContext, STATUS_SUCCESS, 0);
+    ok_eq_uint(ReadBuffer[0], 'X');
+    CheckServerQuota(ServerHandle, 0, 1); CheckClientQuota(ClientHandle, 1, 0);
+    Okay = CheckReadPipe(&ClientReadContext, ClientHandle, ReadBuffer, 1, 100);
+    CheckPipeContext(&ClientReadContext, STATUS_SUCCESS, 1);
+    ok_eq_uint(ReadBuffer[0], 'E');
+    CheckServerQuota(ServerHandle, 0, 0); CheckClientQuota(ClientHandle, 0, 0);
+
+    /** Client to server, read 0 bytes blocks, write 0 bytes does not unblock, write 1 byte unblocks */
+    WriteBuffer[0] = 'F';
+    ReadBuffer[0] = 'X';
+    Okay = CheckReadPipe(&ServerReadContext, ServerHandle, (PVOID)1, 0, 100);
+    ok_bool_false(Okay, "CheckReadPipe returned");
+    CheckClientQuota(ClientHandle, 0, 0);
+    Okay = CheckWritePipe(&ClientWriteContext, ClientHandle, (PVOID)1, 0, 100);
+    CheckPipeContext(&ClientWriteContext, STATUS_SUCCESS, 0);
+    Okay = WaitForWork(&ServerReadContext, 100);
+    ok_bool_false(Okay, "WaitForWork returned");
+    CheckClientQuota(ClientHandle, 0, 0);
+    Okay = CheckWritePipe(&ClientWriteContext, ClientHandle, WriteBuffer, 1, 100);
+    CheckPipeContext(&ClientWriteContext, STATUS_SUCCESS, 1);
+    Okay = WaitForWork(&ServerReadContext, 100);
+    CheckPipeContext(&ServerReadContext, STATUS_SUCCESS, 0);
+    ok_eq_uint(ReadBuffer[0], 'X');
+    CheckClientQuota(ClientHandle, 0, 1); CheckServerQuota(ServerHandle, 1, 0);
+    Okay = CheckReadPipe(&ServerReadContext, ServerHandle, ReadBuffer, 1, 100);
+    CheckPipeContext(&ServerReadContext, STATUS_SUCCESS, 1);
+    ok_eq_uint(ReadBuffer[0], 'F');
+    CheckClientQuota(ClientHandle, 0, 0); CheckServerQuota(ServerHandle, 0, 0);
+
+    /** Disconnect server with pending read on client */
+    WriteBuffer[0] = 'G';
+    ReadBuffer[0] = 'X';
+    Okay = CheckReadPipe(&ClientReadContext, ClientHandle, ReadBuffer, 1, 100);
+    ok_bool_false(Okay, "CheckReadPipe returned");
+    CheckServerQuota(ServerHandle, 0, 1);
+    Status = NpDisconnectPipe(ServerHandle);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    Okay = WaitForWork(&ClientReadContext, 100);
+    CheckPipeContext(&ClientReadContext, STATUS_PIPE_DISCONNECTED, 0);
+    ok_eq_uint(ReadBuffer[0], 'X');
+
+    /* Read from server when disconnected */
+    Okay = CheckReadPipe(&ServerReadContext, ServerHandle, ReadBuffer, 1, 100);
+    CheckPipeContext(&ServerReadContext, STATUS_PIPE_DISCONNECTED, 0);
+
+    /* Write to server when disconnected */
+    Okay = CheckWritePipe(&ServerWriteContext, ServerHandle, WriteBuffer, 1, 100);
+    CheckPipeContext(&ServerWriteContext, STATUS_PIPE_DISCONNECTED, 0);
+
+    /* Read from client when disconnected */
+    Okay = CheckReadPipe(&ClientReadContext, ClientHandle, ReadBuffer, 1, 100);
+    CheckPipeContext(&ClientReadContext, STATUS_PIPE_DISCONNECTED, 0);
+
+    /* Write to client when disconnected */
+    Okay = CheckWritePipe(&ClientWriteContext, ClientHandle, WriteBuffer, 1, 100);
+    CheckPipeContext(&ClientWriteContext, STATUS_PIPE_DISCONNECTED, 0);
+    Status = ObCloseHandle(ClientHandle, KernelMode);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+
+    /* Restore the connection */
+    Okay = CheckListenPipe(&ListenContext, ServerHandle, 100);
+    ok_bool_false(Okay, "CheckListenPipe returned");
+    Okay = CheckConnectPipe(&ConnectContext, PipePath, ClientSynchronous, 100);
+    ok_bool_true(Okay, "CheckConnectPipe returned");
+    ok_eq_hex(ConnectContext.Connect.Status, STATUS_SUCCESS);
+    Okay = WaitForWork(&ListenContext, 100);
+    ok_bool_true(Okay, "WaitForWork returned");
+    ok_eq_hex(ListenContext.Listen.Status, STATUS_SUCCESS);
+    ClientHandle = ConnectContext.Connect.ClientHandle;
+    CheckClient(ClientHandle, FILE_PIPE_CONNECTED_STATE);
+    CheckServer(ServerHandle, FILE_PIPE_CONNECTED_STATE);
+
+    /** Close server with pending read on client */
+    WriteBuffer[0] = 'H';
+    ReadBuffer[0] = 'X';
+    Okay = CheckReadPipe(&ClientReadContext, ClientHandle, ReadBuffer, 1, 100);
+    ok_bool_false(Okay, "CheckReadPipe returned");
+    Status = ObCloseHandle(ServerHandle, KernelMode);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    Okay = WaitForWork(&ClientReadContext, 100);
+    CheckPipeContext(&ClientReadContext, STATUS_PIPE_BROKEN, 0);
+    ok_eq_uint(ReadBuffer[0], 'X');
+
+    /* Read from client when closed */
+    Okay = CheckReadPipe(&ClientReadContext, ClientHandle, ReadBuffer, 1, 100);
+    CheckPipeContext(&ClientReadContext, STATUS_PIPE_BROKEN, 0);
+
+    /* Write to client when closed */
+    Okay = CheckWritePipe(&ClientWriteContext, ClientHandle, WriteBuffer, 1, 100);
+    CheckPipeContext(&ClientWriteContext, STATUS_PIPE_CLOSING, 0);
+    Status = ObCloseHandle(ClientHandle, KernelMode);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+
+    /* Restore the connection */
+    Status = MakeServer(&ServerHandle, PipePath, ServerSynchronous);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    Okay = CheckConnectPipe(&ConnectContext, PipePath, ClientSynchronous, 100);
+    ok_bool_true(Okay, "CheckConnectPipe returned");
+    ok_eq_hex(ConnectContext.Connect.Status, STATUS_SUCCESS);
+    ClientHandle = ConnectContext.Connect.ClientHandle;
+    CheckClient(ClientHandle, FILE_PIPE_CONNECTED_STATE);
+    CheckServer(ServerHandle, FILE_PIPE_CONNECTED_STATE);
+
+    /** Close client with pending read on server */
+    WriteBuffer[0] = 'I';
+    ReadBuffer[0] = 'X';
+    Okay = CheckReadPipe(&ServerReadContext, ServerHandle, ReadBuffer, 1, 100);
+    ok_bool_false(Okay, "CheckReadPipe returned");
+    Status = ObCloseHandle(ClientHandle, KernelMode);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    Okay = WaitForWork(&ServerReadContext, 100);
+    CheckPipeContext(&ServerReadContext, STATUS_PIPE_BROKEN, 0);
+    ok_eq_uint(ReadBuffer[0], 'X');
+
+    /* Read from server when closed */
+    Okay = CheckReadPipe(&ServerReadContext, ServerHandle, ReadBuffer, 1, 100);
+    CheckPipeContext(&ServerReadContext, STATUS_PIPE_BROKEN, 0);
+
+    /* Write to server when closed */
+    Okay = CheckWritePipe(&ServerWriteContext, ServerHandle, WriteBuffer, 1, 100);
+    CheckPipeContext(&ServerWriteContext, STATUS_PIPE_CLOSING, 0);
+    Status = ObCloseHandle(ServerHandle, KernelMode);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+
+    /* Restore the connection */
+    Status = MakeServer(&ServerHandle, PipePath, ServerSynchronous);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    Okay = CheckConnectPipe(&ConnectContext, PipePath, ClientSynchronous, 100);
+    ok_bool_true(Okay, "CheckConnectPipe returned");
+    ok_eq_hex(ConnectContext.Connect.Status, STATUS_SUCCESS);
+    ClientHandle = ConnectContext.Connect.ClientHandle;
+    CheckClient(ClientHandle, FILE_PIPE_CONNECTED_STATE);
+    CheckServer(ServerHandle, FILE_PIPE_CONNECTED_STATE);
+
+    /** Write to server and disconnect, then read from client */
+    WriteBuffer[0] = 'J';
+    ReadBuffer[0] = 'X';
+    Okay = CheckWritePipe(&ServerWriteContext, ServerHandle, WriteBuffer, 1, 100);
+    CheckPipeContext(&ServerWriteContext, STATUS_SUCCESS, 1);
+    CheckServerQuota(ServerHandle, 0, 1); CheckClientQuota(ClientHandle, 1, 0);
+    Status = NpDisconnectPipe(ServerHandle);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    NpQueryPipe(ClientHandle, STATUS_PIPE_DISCONNECTED);
+    CheckServer(ServerHandle, FILE_PIPE_DISCONNECTED_STATE);
+    Okay = CheckReadPipe(&ClientReadContext, ClientHandle, ReadBuffer, 1, 100);
+    CheckPipeContext(&ClientReadContext, STATUS_PIPE_DISCONNECTED, 0);
+    ok_eq_uint(ReadBuffer[0], 'X');
+    Okay = CheckReadPipe(&ClientReadContext, ClientHandle, ReadBuffer, 1, 100);
+    CheckPipeContext(&ClientReadContext, STATUS_PIPE_DISCONNECTED, 0);
+    Status = ObCloseHandle(ClientHandle, KernelMode);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+
+    /* Restore the connection */
+    Okay = CheckListenPipe(&ListenContext, ServerHandle, 100);
+    ok_bool_false(Okay, "CheckListenPipe returned");
+    Okay = CheckConnectPipe(&ConnectContext, PipePath, ClientSynchronous, 100);
+    ok_bool_true(Okay, "CheckConnectPipe returned");
+    ok_eq_hex(ConnectContext.Connect.Status, STATUS_SUCCESS);
+    Okay = WaitForWork(&ListenContext, 100);
+    ok_bool_true(Okay, "WaitForWork returned");
+    ok_eq_hex(ListenContext.Listen.Status, STATUS_SUCCESS);
+    ClientHandle = ConnectContext.Connect.ClientHandle;
+    CheckClient(ClientHandle, FILE_PIPE_CONNECTED_STATE);
+    CheckServer(ServerHandle, FILE_PIPE_CONNECTED_STATE);
+
+    /** Write to server and close, then read from client */
+    WriteBuffer[0] = 'K';
+    ReadBuffer[0] = 'X';
+    Okay = CheckWritePipe(&ServerWriteContext, ServerHandle, WriteBuffer, 1, 100);
+    CheckPipeContext(&ServerWriteContext, STATUS_SUCCESS, 1);
+    CheckServerQuota(ServerHandle, 0, 1); CheckClientQuota(ClientHandle, 1, 0);
+    Status = ObCloseHandle(ServerHandle, KernelMode);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    NpCheckClientPipe(ClientHandle,
+                      BYTE_STREAM, QUEUE, BYTE_STREAM, DUPLEX,
+                      MAX_INSTANCES, 1,
+                      IN_QUOTA, 1,
+                      OUT_QUOTA, OUT_QUOTA,
+                      FILE_PIPE_CLOSING_STATE);
+    Okay = CheckReadPipe(&ClientReadContext, ClientHandle, ReadBuffer, 1, 100);
+    CheckPipeContext(&ClientReadContext, STATUS_SUCCESS, 1);
+    ok_eq_uint(ReadBuffer[0], 'K');
+    Okay = CheckReadPipe(&ClientReadContext, ClientHandle, ReadBuffer, 1, 100);
+    CheckPipeContext(&ClientReadContext, STATUS_PIPE_BROKEN, 0);
+    Status = ObCloseHandle(ClientHandle, KernelMode);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+
+    /* Restore the connection */
+    Status = MakeServer(&ServerHandle, PipePath, ServerSynchronous);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    Okay = CheckConnectPipe(&ConnectContext, PipePath, ClientSynchronous, 100);
+    ok_bool_true(Okay, "CheckConnectPipe returned");
+    ok_eq_hex(ConnectContext.Connect.Status, STATUS_SUCCESS);
+    ClientHandle = ConnectContext.Connect.ClientHandle;
+    CheckClient(ClientHandle, FILE_PIPE_CONNECTED_STATE);
+    CheckServer(ServerHandle, FILE_PIPE_CONNECTED_STATE);
+
+
+    /** Write to client and close, then read from server */
+    WriteBuffer[0] = 'L';
+    ReadBuffer[0] = 'X';
+    Okay = CheckWritePipe(&ClientWriteContext, ClientHandle, WriteBuffer, 1, 100);
+    CheckPipeContext(&ClientWriteContext, STATUS_SUCCESS, 1);
+    CheckClientQuota(ClientHandle, 0, 1); CheckServerQuota(ServerHandle, 1, 0);
+    Status = ObCloseHandle(ClientHandle, KernelMode);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    NpCheckServerPipe(ServerHandle,
+                      BYTE_STREAM, QUEUE, BYTE_STREAM, DUPLEX,
+                      MAX_INSTANCES, 1,
+                      IN_QUOTA, 1,
+                      OUT_QUOTA, OUT_QUOTA,
+                      FILE_PIPE_CLOSING_STATE);
+    Okay = CheckReadPipe(&ServerReadContext, ServerHandle, ReadBuffer, 1, 100);
+    CheckPipeContext(&ServerReadContext, STATUS_SUCCESS, 1);
+    ok_eq_uint(ReadBuffer[0], 'L');
+    Okay = CheckReadPipe(&ServerReadContext, ServerHandle, ReadBuffer, 1, 100);
+    CheckPipeContext(&ServerReadContext, STATUS_PIPE_BROKEN, 0);
+    Status = ObCloseHandle(ServerHandle, KernelMode);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+
+    /* Restore the connection */
+    Status = MakeServer(&ServerHandle, PipePath, ServerSynchronous);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    Okay = CheckConnectPipe(&ConnectContext, PipePath, ClientSynchronous, 100);
+    ok_bool_true(Okay, "CheckConnectPipe returned");
+    ok_eq_hex(ConnectContext.Connect.Status, STATUS_SUCCESS);
+    ClientHandle = ConnectContext.Connect.ClientHandle;
+    CheckClient(ClientHandle, FILE_PIPE_CONNECTED_STATE);
+    CheckServer(ServerHandle, FILE_PIPE_CONNECTED_STATE);
+
+    /** Write to client and disconnect server, then read from server */
+    WriteBuffer[0] = 'M';
+    ReadBuffer[0] = 'X';
+    Okay = CheckWritePipe(&ClientWriteContext, ClientHandle, WriteBuffer, 1, 100);
+    CheckPipeContext(&ClientWriteContext, STATUS_SUCCESS, 1);
+    CheckClientQuota(ClientHandle, 0, 1); CheckServerQuota(ServerHandle, 1, 0);
+    Status = NpDisconnectPipe(ServerHandle);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    NpQueryPipe(ClientHandle, STATUS_PIPE_DISCONNECTED);
+    CheckServer(ServerHandle, FILE_PIPE_DISCONNECTED_STATE);
+    Okay = CheckReadPipe(&ServerReadContext, ServerHandle, ReadBuffer, 1, 100);
+    CheckPipeContext(&ServerReadContext, STATUS_PIPE_DISCONNECTED, 0);
+    ok_eq_uint(ReadBuffer[0], 'X');
+    Okay = CheckReadPipe(&ServerReadContext, ServerHandle, ReadBuffer, 1, 100);
+    CheckPipeContext(&ServerReadContext, STATUS_PIPE_DISCONNECTED, 0);
+    Status = ObCloseHandle(ClientHandle, KernelMode);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+
+    Status = ObCloseHandle(ServerHandle, KernelMode);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+
+    FinishWorkerThread(&ServerWriteContext);
+    FinishWorkerThread(&ServerReadContext);
+    FinishWorkerThread(&ClientWriteContext);
+    FinishWorkerThread(&ClientReadContext);
+    FinishWorkerThread(&ListenContext);
+    FinishWorkerThread(&ConnectContext);
+}
+
+START_TEST(NpfsReadWrite)
+{
+    PKTHREAD Thread;
+    READ_WRITE_TEST_CONTEXT TestContext;
+
+    TestContext.PipePath = DEVICE_NAMED_PIPE L"\\KmtestNpfsReadWriteTestPipe";
+
+    TestContext.ServerSynchronous = TRUE;
+    TestContext.ClientSynchronous = TRUE;
+    Thread = KmtStartThread(TestReadWrite, &TestContext);
+    KmtFinishThread(Thread, NULL);
+
+    TestContext.ServerSynchronous = FALSE;
+    TestContext.ClientSynchronous = TRUE;
+    Thread = KmtStartThread(TestReadWrite, &TestContext);
+    KmtFinishThread(Thread, NULL);
+
+    TestContext.ServerSynchronous = TRUE;
+    TestContext.ClientSynchronous = FALSE;
+    Thread = KmtStartThread(TestReadWrite, &TestContext);
+    KmtFinishThread(Thread, NULL);
+
+    TestContext.ServerSynchronous = FALSE;
+    TestContext.ClientSynchronous = FALSE;
+    Thread = KmtStartThread(TestReadWrite, &TestContext);
+    KmtFinishThread(Thread, NULL);
+}
diff --git a/rostests/kmtests/npfs/npfs.h b/rostests/kmtests/npfs/npfs.h
new file mode 100644 (file)
index 0000000..dd28c83
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * PROJECT:         ReactOS kernel-mode tests
+ * LICENSE:         LGPLv2+ - See COPYING.LIB in the top level directory
+ * PURPOSE:         Kernel-Mode Test Suite NPFS helper declarations
+ * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
+ */
+
+#ifndef _KMTEST_NPFS_H_
+#define _KMTEST_NPFS_H_
+
+#define DEVICE_NAMED_PIPE L"\\Device\\NamedPipe"
+
+#define BYTE_STREAM FILE_PIPE_BYTE_STREAM_MODE
+C_ASSERT(FILE_PIPE_BYTE_STREAM_MODE == FILE_PIPE_BYTE_STREAM_TYPE);
+#define MESSAGE     FILE_PIPE_MESSAGE_MODE
+C_ASSERT(FILE_PIPE_MESSAGE_MODE == FILE_PIPE_MESSAGE_TYPE);
+#define QUEUE       FILE_PIPE_QUEUE_OPERATION
+#define COMPLETE    FILE_PIPE_COMPLETE_OPERATION
+#define INBOUND     FILE_PIPE_INBOUND
+#define OUTBOUND    FILE_PIPE_OUTBOUND
+#define DUPLEX      FILE_PIPE_FULL_DUPLEX
+
+NTSTATUS
+NpCreatePipeEx(
+    OUT PHANDLE ServerHandle,
+    IN PCWSTR PipePath,
+    IN ULONG ReadMode,
+    IN ULONG CompletionMode,
+    IN ULONG NamedPipeType,
+    IN ULONG ShareAccess,
+    IN ULONG MaximumInstances,
+    IN ULONG InboundQuota,
+    IN ULONG OutboundQuota,
+    IN ACCESS_MASK DesiredAccess,
+    IN ULONG Disposition,
+    IN ULONG CreateOptions,
+    IN PLARGE_INTEGER DefaultTimeout OPTIONAL);
+
+NTSTATUS
+NpCreatePipe(
+    OUT PHANDLE ServerHandle,
+    IN PCWSTR PipePath,
+    IN ULONG ReadMode,
+    IN ULONG CompletionMode,
+    IN ULONG NamedPipeType,
+    IN ULONG NamedPipeConfiguration,
+    IN ULONG MaximumInstances,
+    IN ULONG InboundQuota,
+    IN ULONG OutboundQuota);
+
+NTSTATUS
+NpOpenPipeEx(
+    OUT PHANDLE ClientHandle,
+    IN PCWSTR PipePath,
+    IN ACCESS_MASK DesiredAccess,
+    IN ULONG ShareAccess,
+    IN ULONG Disposition,
+    IN ULONG CreateOptions);
+
+NTSTATUS
+NpOpenPipe(
+    OUT PHANDLE ClientHandle,
+    IN PCWSTR PipePath,
+    IN ULONG NamedPipeConfiguration);
+
+NTSTATUS
+NpControlPipe(
+    IN HANDLE PipeHandle,
+    IN ULONG FsControlCode,
+    IN PVOID InputBuffer,
+    IN ULONG InputBufferLength);
+
+#define NpListenPipe(ServerHandle)      NpControlPipe(ServerHandle, FSCTL_PIPE_LISTEN, NULL, 0)
+#define NpDisconnectPipe(ServerHandle)  NpControlPipe(ServerHandle, FSCTL_PIPE_DISCONNECT, NULL, 0)
+
+NTSTATUS
+NpWaitPipe(
+    IN PCWSTR PipeName,
+    IN PLARGE_INTEGER Timeout);
+
+NTSTATUS
+NpReadPipe(
+    IN HANDLE PipeHandle,
+    OUT PVOID Buffer,
+    IN ULONG BufferSize,
+    OUT PULONG_PTR BytesRead);
+
+NTSTATUS
+NpWritePipe(
+    IN HANDLE PipeHandle,
+    IN const VOID *Buffer,
+    IN ULONG BufferSize,
+    OUT PULONG_PTR BytesWritten);
+
+#define NpCheckServerPipe(h, rm, cm, npt, npc, mi, ci, iq, rsa, oq, wqa, nps) \
+    NpCheckServerPipe__(h, rm, cm, npt, npc, mi, ci, iq, rsa, oq, wqa, nps, __FILE__, __LINE__)
+
+#define NpCheckServerPipe__(h, rm, cm, npt, npc, mi, ci, iq, rsa, oq, wqa, nps, file, line) \
+    NpCheckServerPipe_(h, rm, cm, npt, npc, mi, ci, iq, rsa, oq, wqa, nps, file ":" KMT_STRINGIZE(line))
+
+VOID
+NpCheckServerPipe_(
+    IN HANDLE ServerHandle,
+    /* PipeInformation */
+    IN ULONG ReadMode,
+    IN ULONG CompletionMode,
+    /* PipeLocalInformation */
+    IN ULONG NamedPipeType,
+    IN ULONG NamedPipeConfiguration,
+    IN ULONG MaximumInstances,
+    IN ULONG CurrentInstances,
+    IN ULONG InboundQuota,
+    IN ULONG ReadDataAvailable,
+    IN ULONG OutboundQuota,
+    IN ULONG WriteQuotaAvailable,
+    IN ULONG NamedPipeState,
+    /* PipeRemoteInformation */
+    /* */
+    IN PCSTR FileAndLine);
+
+#define NpCheckClientPipe(h, rm, cm, npt, npc, mi, ci, iq, rsa, oq, wqa, nps) \
+    NpCheckClientPipe__(h, rm, cm, npt, npc, mi, ci, iq, rsa, oq, wqa, nps, __FILE__, __LINE__)
+
+#define NpCheckClientPipe__(h, rm, cm, npt, npc, mi, ci, iq, rsa, oq, wqa, nps, file, line) \
+    NpCheckClientPipe_(h, rm, cm, npt, npc, mi, ci, iq, rsa, oq, wqa, nps, file ":" KMT_STRINGIZE(line))
+
+VOID
+NpCheckClientPipe_(
+    IN HANDLE ClientHandle,
+    /* PipeInformation */
+    IN ULONG ReadMode,
+    IN ULONG CompletionMode,
+    /* PipeLocalInformation */
+    IN ULONG NamedPipeType,
+    IN ULONG NamedPipeConfiguration,
+    IN ULONG MaximumInstances,
+    IN ULONG CurrentInstances,
+    IN ULONG InboundQuota,
+    IN ULONG ReadDataAvailable,
+    IN ULONG OutboundQuota,
+    IN ULONG WriteQuotaAvailable,
+    IN ULONG NamedPipeState,
+    /* PipeRemoteInformation */
+    /* */
+    IN PCSTR FileAndLine);
+
+#define NpQueryPipe(h, es) \
+    NpQueryPipe__(h, es, __FILE__, __LINE__)
+
+#define NpQueryPipe__(h, es, file, line) \
+    NpQueryPipe_(h, es, file ":" KMT_STRINGIZE(line))
+
+VOID
+NpQueryPipe_(
+    IN HANDLE Handle,
+    IN NTSTATUS ExpectedStatus,
+    IN PCSTR FileAndLine);
+
+
+struct _THREAD_CONTEXT;
+typedef VOID (WORK_FUNCTION)(IN OUT struct _THREAD_CONTEXT *);
+typedef WORK_FUNCTION *PWORK_FUNCTION;
+
+typedef struct _THREAD_CONTEXT
+{
+    volatile PWORK_FUNCTION Work;
+    volatile union
+    {
+        struct
+        {
+            PCWSTR PipePath;
+            BOOLEAN ClientSynchronous;
+            HANDLE ClientHandle;
+            NTSTATUS Status;
+        } Connect;
+        struct
+        {
+            HANDLE ServerHandle;
+            NTSTATUS Status;
+        } Listen;
+        struct
+        {
+            HANDLE PipeHandle;
+            PVOID Buffer;
+            ULONG BufferSize;
+            ULONG_PTR BytesTransferred;
+            NTSTATUS Status;
+        } ReadWrite;
+    };
+    KEVENT ThreadDoneEvent;
+    KEVENT StartWorkEvent;
+    KEVENT WorkCompleteEvent;
+    PKTHREAD Thread;
+} THREAD_CONTEXT, *PTHREAD_CONTEXT;
+
+VOID
+StartWorkerThread(
+    OUT PTHREAD_CONTEXT Context);
+
+VOID
+FinishWorkerThread(
+    IN PTHREAD_CONTEXT Context);
+
+BOOLEAN
+WaitForWork(
+    IN PTHREAD_CONTEXT Context,
+    IN ULONG MilliSeconds);
+
+BOOLEAN
+TriggerWork(
+    IN PTHREAD_CONTEXT Context,
+    IN ULONG MilliSeconds);
+
+
+PKTHREAD
+KmtStartThread(
+    IN PKSTART_ROUTINE StartRoutine,
+    IN PVOID StartContext OPTIONAL);
+
+VOID
+KmtFinishThread(
+    IN PKTHREAD Thread OPTIONAL,
+    IN PKEVENT Event OPTIONAL);
+
+#endif /* !defined _KMTEST_NPFS_H_ */