Index: CMakeLists.txt =================================================================== --- modules/rostests/kmtests/CMakeLists.txt (revision 59995) +++ modules/rostests/kmtests/CMakeLists.txt (working copy) @@ -26,6 +26,12 @@ example/Example.c example/KernelType.c + npfs/NpfsConnect.c + npfs/NpfsCreate.c + npfs/NpfsHelpers.c + npfs/NpfsReadWrite.c + npfs/NpfsReadWriteAsync.c + npfs/NpfsWait.c ntos_ex/ExCallback.c ntos_ex/ExDoubleList.c ntos_ex/ExFastMutex.c Index: kmtest_drv/testlist.c =================================================================== --- modules/rostests/kmtests/kmtest_drv/testlist.c (revision 59995) +++ modules/rostests/kmtests/kmtest_drv/testlist.c (working copy) @@ -38,6 +38,11 @@ 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_NpfsReadWriteAsync; +KMT_TESTFUNC Test_NpfsWait; KMT_TESTFUNC Test_ObReference; KMT_TESTFUNC Test_ObType; KMT_TESTFUNC Test_ObTypeClean; @@ -88,6 +93,11 @@ { "KeTimer", Test_KeTimer }, { "-KernelType", Test_KernelType }, { "MmSection", Test_MmSection }, + { "NpfsConnect", Test_NpfsConnect }, + { "NpfsCreate", Test_NpfsCreate }, + { "NpfsReadWrite", Test_NpfsReadWrite }, + { "NpfsReadWriteAsync", Test_NpfsReadWriteAsync }, + { "-NpfsWait", Test_NpfsWait }, { "ObReference", Test_ObReference }, { "ObType", Test_ObType }, { "-ObTypeClean", Test_ObTypeClean }, Index: npfs =================================================================== --- modules/rostests/kmtests/npfs (revision 0) +++ modules/rostests/kmtests/npfs (working copy) Property changes on: npfs ___________________________________________________________________ Added: bugtraq:logregex ## -0,0 +1,2 ## +([Ii]ssue|[Bb]ug)s? #?(\d+)(,? ?#?(\d+))*(,? ?(and |or )?#?(\d+))? +(\d+) \ No newline at end of property Added: bugtraq:url ## -0,0 +1 ## +http://www.reactos.org/bugzilla/show_bug.cgi?id=%BUGID% \ No newline at end of property Added: bugtraq:message ## -0,0 +1 ## +See issue #%BUGID% for more details. \ No newline at end of property Added: tsvn:logminsize ## -0,0 +1 ## +10 \ No newline at end of property Index: npfs/npfs.h =================================================================== --- modules/rostests/kmtests/npfs/npfs.h (revision 0) +++ modules/rostests/kmtests/npfs/npfs.h (working copy) @@ -0,0 +1,225 @@ +/* + * PROJECT: ReactOS kernel-mode tests + * LICENSE: GPLv2+ - See COPYING in the top level directory + * PURPOSE: Kernel-Mode Test Suite NPFS helper declarations + * PROGRAMMER: Thomas Faber + */ + +#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_ */ Index: npfs/NpfsConnect.c =================================================================== --- modules/rostests/kmtests/npfs/NpfsConnect.c (revision 0) +++ modules/rostests/kmtests/npfs/NpfsConnect.c (working copy) @@ -0,0 +1,272 @@ +/* + * PROJECT: ReactOS kernel-mode tests + * LICENSE: GPLv2+ - See COPYING in the top level directory + * PURPOSE: Kernel-Mode Test Suite NPFS Connect test + * PROGRAMMER: Thomas Faber + */ + +#include +#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); +} Index: npfs/NpfsCreate.c =================================================================== --- modules/rostests/kmtests/npfs/NpfsCreate.c (revision 0) +++ modules/rostests/kmtests/npfs/NpfsCreate.c (working copy) @@ -0,0 +1,78 @@ +/* + * PROJECT: ReactOS kernel-mode tests + * LICENSE: GPLv2+ - See COPYING in the top level directory + * PURPOSE: Kernel-Mode Test Suite NPFS Create test + * PROGRAMMER: Thomas Faber + */ + +#include +#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; + + /* 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); +} Index: npfs/NpfsHelpers.c =================================================================== --- modules/rostests/kmtests/npfs/NpfsHelpers.c (revision 0) +++ modules/rostests/kmtests/npfs/NpfsHelpers.c (working copy) @@ -0,0 +1,768 @@ +/* + * PROJECT: ReactOS kernel-mode tests + * LICENSE: GPLv2+ - See COPYING in the top level directory + * PURPOSE: Kernel-Mode Test Suite Helper functions for NPFS tests + * PROGRAMMER: Thomas Faber + */ + +#include +#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; + } + + /* TODO: might want to go even lower and use ObOpenObjectByName */ + 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); + ok_eq_hex(IoStatusBlock.Status, Status); + ok_eq_ulongptr(IoStatusBlock.Information, FILE_CREATED); + 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); + + /* TODO: might want to go even lower and use ObOpenObjectByName */ + 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, 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, 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\n"); + ok_bool_true(CheckBuffer(&PipeInfo, sizeof(PipeInfo), 0x55), "CheckBuffer returned\n"); + + 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\n"); + ok_bool_true(CheckBuffer(&PipeLocalInfo, sizeof(PipeLocalInfo), 0x55), "CheckBuffer returned\n"); + + 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\n"); + ok_bool_true(CheckBuffer(&PipeRemoteInfo, sizeof(PipeRemoteInfo), 0x55), "CheckBuffer returned\n"); +} + +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); +} Index: npfs/NpfsReadWrite.c =================================================================== --- modules/rostests/kmtests/npfs/NpfsReadWrite.c (revision 0) +++ modules/rostests/kmtests/npfs/NpfsReadWrite.c (working copy) @@ -0,0 +1,598 @@ +/* + * PROJECT: ReactOS kernel-mode tests + * LICENSE: GPLv2+ - See COPYING in the top level directory + * PURPOSE: Kernel-Mode Test Suite NPFS Read/Write test + * PROGRAMMER: Thomas Faber + */ + +#include +#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); +} Index: npfs/NpfsReadWriteAsync.c =================================================================== --- modules/rostests/kmtests/npfs/NpfsReadWriteAsync.c (revision 0) +++ modules/rostests/kmtests/npfs/NpfsReadWriteAsync.c (working copy) @@ -0,0 +1,490 @@ +/* + * PROJECT: ReactOS kernel-mode tests + * LICENSE: GPLv2+ - See COPYING in the top level directory + * PURPOSE: Kernel-Mode Test Suite NPFS Asynchronous Read/Write test + * PROGRAMMER: Thomas Faber + */ + +#include +#include "npfs.h" + +#define MAX_INSTANCES 5 +#define IN_QUOTA 4096 +#define OUT_QUOTA 4096 + +#define MakeServer(ServerHandle, PipePath) \ + 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, \ + 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) + +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, + 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 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 +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) +{ + PCWSTR PipePath = Context; + 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); + 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, 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); + ok_bool_true(Okay, "CheckWritePipe returned"); + ok_eq_ulongptr(ServerWriteContext.ReadWrite.BytesTransferred, 1); + ok_eq_hex(ServerWriteContext.ReadWrite.Status, STATUS_SUCCESS); + CheckServerQuota(ServerHandle, 0, 1); CheckClientQuota(ClientHandle, 1, 0); + Okay = CheckReadPipe(&ClientReadContext, ClientHandle, ReadBuffer, 1, 100); + ok_bool_true(Okay, "CheckReadPipe returned"); + ok_eq_ulongptr(ClientReadContext.ReadWrite.BytesTransferred, 1); + ok_eq_hex(ClientReadContext.ReadWrite.Status, STATUS_SUCCESS); + 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); + ok_bool_true(Okay, "CheckWritePipe returned"); + ok_eq_ulongptr(ServerWriteContext.ReadWrite.BytesTransferred, 1); + ok_eq_hex(ServerWriteContext.ReadWrite.Status, STATUS_SUCCESS); + Okay = WaitForWork(&ClientReadContext, 100); + ok_bool_true(Okay, "WaitForWork returned"); + ok_eq_ulongptr(ClientReadContext.ReadWrite.BytesTransferred, 1); + ok_eq_hex(ClientReadContext.ReadWrite.Status, STATUS_SUCCESS); + 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); + ok_bool_true(Okay, "CheckWritePipe returned"); + ok_eq_ulongptr(ClientWriteContext.ReadWrite.BytesTransferred, 1); + ok_eq_hex(ClientWriteContext.ReadWrite.Status, STATUS_SUCCESS); + CheckClientQuota(ClientHandle, 0, 1); CheckServerQuota(ServerHandle, 1, 0); + Okay = CheckReadPipe(&ServerReadContext, ServerHandle, ReadBuffer, 1, 100); + ok_bool_true(Okay, "CheckReadPipe returned"); + ok_eq_ulongptr(ServerReadContext.ReadWrite.BytesTransferred, 1); + ok_eq_hex(ServerReadContext.ReadWrite.Status, STATUS_SUCCESS); + 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); + ok_bool_true(Okay, "CheckWritePipe returned"); + ok_eq_ulongptr(ClientWriteContext.ReadWrite.BytesTransferred, 1); + ok_eq_hex(ClientWriteContext.ReadWrite.Status, STATUS_SUCCESS); + Okay = WaitForWork(&ServerReadContext, 100); + ok_bool_true(Okay, "WaitForWork returned"); + ok_eq_ulongptr(ServerReadContext.ReadWrite.BytesTransferred, 1); + ok_eq_hex(ServerReadContext.ReadWrite.Status, STATUS_SUCCESS); + 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); + ok_bool_true(Okay, "CheckWritePipe returned"); + ok_eq_ulongptr(ServerWriteContext.ReadWrite.BytesTransferred, 0); + ok_eq_hex(ServerWriteContext.ReadWrite.Status, STATUS_SUCCESS); + CheckServerQuota(ServerHandle, 0, 0); CheckClientQuota(ClientHandle, 0, 0); + + /* Client to Server, write 0 bytes */ + Okay = CheckWritePipe(&ClientWriteContext, ClientHandle, (PVOID)1, 0, 100); + ok_bool_true(Okay, "CheckWritePipe returned"); + ok_eq_ulongptr(ClientWriteContext.ReadWrite.BytesTransferred, 0); + ok_eq_hex(ClientWriteContext.ReadWrite.Status, STATUS_SUCCESS); + 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); + ok_bool_true(Okay, "CheckWritePipe returned"); + ok_eq_ulongptr(ServerWriteContext.ReadWrite.BytesTransferred, 0); + ok_eq_hex(ServerWriteContext.ReadWrite.Status, STATUS_SUCCESS); + Okay = WaitForWork(&ClientReadContext, 100); + ok_bool_false(Okay, "WaitForWork returned"); + CheckServerQuota(ServerHandle, 0, 0); + Okay = CheckWritePipe(&ServerWriteContext, ServerHandle, WriteBuffer, 1, 100); + ok_bool_true(Okay, "CheckWritePipe returned"); + ok_eq_ulongptr(ServerWriteContext.ReadWrite.BytesTransferred, 1); + ok_eq_hex(ServerWriteContext.ReadWrite.Status, STATUS_SUCCESS); + Okay = WaitForWork(&ClientReadContext, 100); + ok_bool_true(Okay, "WaitForWork returned"); + ok_eq_ulongptr(ClientReadContext.ReadWrite.BytesTransferred, 0); + ok_eq_hex(ClientReadContext.ReadWrite.Status, STATUS_SUCCESS); + ok_eq_uint(ReadBuffer[0], 'X'); + CheckServerQuota(ServerHandle, 0, 1); CheckClientQuota(ClientHandle, 1, 0); + Okay = CheckReadPipe(&ClientReadContext, ClientHandle, ReadBuffer, 1, 100); + ok_bool_true(Okay, "CheckReadPipe returned"); + ok_eq_ulongptr(ClientReadContext.ReadWrite.BytesTransferred, 1); + ok_eq_hex(ClientReadContext.ReadWrite.Status, STATUS_SUCCESS); + 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); + ok_bool_true(Okay, "CheckWritePipe returned"); + ok_eq_ulongptr(ClientWriteContext.ReadWrite.BytesTransferred, 0); + ok_eq_hex(ClientWriteContext.ReadWrite.Status, STATUS_SUCCESS); + Okay = WaitForWork(&ServerReadContext, 100); + ok_bool_false(Okay, "WaitForWork returned"); + CheckClientQuota(ClientHandle, 0, 0); + Okay = CheckWritePipe(&ClientWriteContext, ClientHandle, WriteBuffer, 1, 100); + ok_bool_true(Okay, "CheckWritePipe returned"); + ok_eq_ulongptr(ClientWriteContext.ReadWrite.BytesTransferred, 1); + ok_eq_hex(ClientWriteContext.ReadWrite.Status, STATUS_SUCCESS); + Okay = WaitForWork(&ServerReadContext, 100); + ok_bool_true(Okay, "WaitForWork returned"); + ok_eq_ulongptr(ServerReadContext.ReadWrite.BytesTransferred, 0); + ok_eq_hex(ServerReadContext.ReadWrite.Status, STATUS_SUCCESS); + ok_eq_uint(ReadBuffer[0], 'X'); + CheckClientQuota(ClientHandle, 0, 1); CheckServerQuota(ServerHandle, 1, 0); + Okay = CheckReadPipe(&ServerReadContext, ServerHandle, ReadBuffer, 1, 100); + ok_bool_true(Okay, "CheckReadPipe returned"); + ok_eq_ulongptr(ServerReadContext.ReadWrite.BytesTransferred, 1); + ok_eq_hex(ServerReadContext.ReadWrite.Status, STATUS_SUCCESS); + 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); + Okay = WaitForWork(&ClientReadContext, 100); + ok_bool_true(Okay, "WaitForWork returned"); + ok_eq_ulongptr(ClientReadContext.ReadWrite.BytesTransferred, 0); + ok_eq_hex(ClientReadContext.ReadWrite.Status, STATUS_PIPE_DISCONNECTED); + ok_eq_uint(ReadBuffer[0], 'X'); + + /* Read from server when disconnected */ + Okay = CheckReadPipe(&ServerReadContext, ServerHandle, ReadBuffer, 1, 100); + ok_bool_true(Okay, "CheckReadPipe returned"); + ok_eq_ulongptr(ServerReadContext.ReadWrite.BytesTransferred, 0); + ok_eq_hex(ServerReadContext.ReadWrite.Status, STATUS_PIPE_DISCONNECTED); + + /* Write to server when disconnected */ + Okay = CheckWritePipe(&ServerWriteContext, ServerHandle, WriteBuffer, 1, 100); + ok_bool_true(Okay, "CheckWritePipe returned"); + ok_eq_ulongptr(ServerWriteContext.ReadWrite.BytesTransferred, 0); + ok_eq_hex(ServerWriteContext.ReadWrite.Status, STATUS_PIPE_DISCONNECTED); + + /* Read from client when disconnected */ + Okay = CheckReadPipe(&ClientReadContext, ClientHandle, ReadBuffer, 1, 100); + ok_bool_true(Okay, "CheckReadPipe returned"); + ok_eq_ulongptr(ClientReadContext.ReadWrite.BytesTransferred, 0); + ok_eq_hex(ClientReadContext.ReadWrite.Status, STATUS_PIPE_DISCONNECTED); + + /* Write to client when disconnected */ + Okay = CheckWritePipe(&ClientWriteContext, ClientHandle, WriteBuffer, 1, 100); + ok_bool_true(Okay, "CheckWritePipe returned"); + ok_eq_ulongptr(ClientWriteContext.ReadWrite.BytesTransferred, 0); + ok_eq_hex(ClientWriteContext.ReadWrite.Status, STATUS_PIPE_DISCONNECTED); + 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, 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); + ok_bool_true(Okay, "WaitForWork returned"); + ok_eq_ulongptr(ClientReadContext.ReadWrite.BytesTransferred, 0); + ok_eq_hex(ClientReadContext.ReadWrite.Status, STATUS_PIPE_BROKEN); + ok_eq_uint(ReadBuffer[0], 'X'); + + /* Read from client when closed */ + Okay = CheckReadPipe(&ClientReadContext, ClientHandle, ReadBuffer, 1, 100); + ok_bool_true(Okay, "CheckReadPipe returned"); + ok_eq_ulongptr(ClientReadContext.ReadWrite.BytesTransferred, 0); + ok_eq_hex(ClientReadContext.ReadWrite.Status, STATUS_PIPE_BROKEN); + + /* Write to client when closed */ + Okay = CheckWritePipe(&ClientWriteContext, ClientHandle, WriteBuffer, 1, 100); + ok_bool_true(Okay, "CheckReadPipe returned"); + ok_eq_ulongptr(ClientWriteContext.ReadWrite.BytesTransferred, 0); + ok_eq_hex(ClientWriteContext.ReadWrite.Status, STATUS_PIPE_CLOSING); + Status = ObCloseHandle(ClientHandle, KernelMode); + ok_eq_hex(Status, STATUS_SUCCESS); + + /* Restore the connection */ + Status = MakeServer(&ServerHandle, PipePath); + ok_eq_hex(Status, STATUS_SUCCESS); + Okay = CheckConnectPipe(&ConnectContext, PipePath, 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 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); + ok_bool_true(Okay, "WaitForWork returned"); + ok_eq_ulongptr(ServerReadContext.ReadWrite.BytesTransferred, 0); + ok_eq_hex(ServerReadContext.ReadWrite.Status, STATUS_PIPE_BROKEN); + ok_eq_uint(ReadBuffer[0], 'X'); + + /* Read from server when closed */ + Okay = CheckReadPipe(&ServerReadContext, ServerHandle, ReadBuffer, 1, 100); + ok_bool_true(Okay, "CheckReadPipe returned"); + ok_eq_ulongptr(ServerReadContext.ReadWrite.BytesTransferred, 0); + ok_eq_hex(ServerReadContext.ReadWrite.Status, STATUS_PIPE_BROKEN); + + /* Write to server when closed */ + Okay = CheckWritePipe(&ServerWriteContext, ServerHandle, WriteBuffer, 1, 100); + ok_bool_true(Okay, "CheckWritePipe returned"); + ok_eq_ulongptr(ServerWriteContext.ReadWrite.BytesTransferred, 0); + ok_eq_hex(ServerWriteContext.ReadWrite.Status, STATUS_PIPE_CLOSING); + Status = ObCloseHandle(ServerHandle, KernelMode); + ok_eq_hex(Status, STATUS_SUCCESS); + + FinishWorkerThread(&ServerWriteContext); + FinishWorkerThread(&ServerReadContext); + FinishWorkerThread(&ClientWriteContext); + FinishWorkerThread(&ClientReadContext); + FinishWorkerThread(&ListenContext); + FinishWorkerThread(&ConnectContext); +} + +START_TEST(NpfsReadWriteAsync) +{ + PKTHREAD Thread; + + Thread = KmtStartThread(TestReadWrite, DEVICE_NAMED_PIPE L"\\KmtestNpfsReadWriteTestPipe"); + KmtFinishThread(Thread, NULL); +} Index: npfs/NpfsWait.c =================================================================== --- modules/rostests/kmtests/npfs/NpfsWait.c (revision 0) +++ modules/rostests/kmtests/npfs/NpfsWait.c (working copy) @@ -0,0 +1,179 @@ +/* + * PROJECT: ReactOS kernel-mode tests + * LICENSE: GPLv2+ - See COPYING in the top level directory + * PURPOSE: Kernel-Mode Test Suite NPFS Connect test + * PROGRAMMER: Thomas Faber + */ + +#include +#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) + +typedef struct _WAIT_FOR_PIPE_CONTEXT +{ + volatile PCWSTR PipeName; + volatile PLARGE_INTEGER Timeout; + volatile LARGE_INTEGER TimeoutBuffer; + volatile NTSTATUS Status; + KEVENT ThreadDoneEvent; + KEVENT StartWaitEvent; + KEVENT WaitCompleteEvent; + PKTHREAD Thread; +} WAIT_FOR_PIPE_CONTEXT, *PWAIT_FOR_PIPE_CONTEXT; +static KSTART_ROUTINE WaitForPipeThread; +static +VOID +NTAPI +WaitForPipeThread( + IN PVOID Context) +{ + PWAIT_FOR_PIPE_CONTEXT WaitContext = Context; + PVOID WaitEvents[2] = { &WaitContext->ThreadDoneEvent, + &WaitContext->StartWaitEvent }; + 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); + + WaitContext->Status = NpWaitPipe(WaitContext->PipeName, + WaitContext->Timeout); + KeSetEvent(&WaitContext->WaitCompleteEvent, IO_NO_INCREMENT, TRUE); + } +} + +static +VOID +StartWaitThread( + OUT PWAIT_FOR_PIPE_CONTEXT WaitContext) +{ + KeInitializeEvent(&WaitContext->ThreadDoneEvent, NotificationEvent, FALSE); + KeInitializeEvent(&WaitContext->StartWaitEvent, NotificationEvent, FALSE); + KeInitializeEvent(&WaitContext->WaitCompleteEvent, NotificationEvent, FALSE); + + WaitContext->Thread = KmtStartThread(WaitForPipeThread, WaitContext); +} + +static +VOID +FinishWaitThread( + IN PWAIT_FOR_PIPE_CONTEXT WaitContext) +{ + KmtFinishThread(WaitContext->Thread, &WaitContext->ThreadDoneEvent); +} + + +static +BOOLEAN +CheckWaitPipe( + IN PWAIT_FOR_PIPE_CONTEXT WaitContext, + IN PCWSTR PipeName, + IN ULONG MilliSeconds, + OUT NTSTATUS *Status) +{ + WaitContext->PipeName = PipeName; + //WaitContext-> + return KeReadStateEvent(&WaitContext->WaitCompleteEvent) != 0; +} + +static +VOID +TestWait( + IN HANDLE ServerHandle) +{ + NTSTATUS Status; + + // NOTE for Query test: Querying a blocked listening server also blocks + + CheckServer(ServerHandle, FILE_PIPE_LISTENING_STATE); + + /* Waiting on this is invalid */ + Status = NpControlPipe(ServerHandle, FSCTL_PIPE_WAIT, NULL, 0); + ok_eq_hex(Status, STATUS_ILLEGAL_FUNCTION); + CheckServer(ServerHandle, FILE_PIPE_LISTENING_STATE); + + Status = NpDisconnectPipe(ServerHandle); + ok_eq_hex(Status, STATUS_SUCCESS); + CheckServer(ServerHandle, FILE_PIPE_DISCONNECTED_STATE); + + //Status = NpWaitPipe(ServerHandle); + //ok_eq_hex(Status, STATUS_ILLEGAL_FUNCTION); + //CheckServer(ServerHandle, FILE_PIPE_DISCONNECTED_STATE); + + Status = NpDisconnectPipe(ServerHandle); + ok_eq_hex(Status, STATUS_PIPE_DISCONNECTED); + CheckServer(ServerHandle, FILE_PIPE_DISCONNECTED_STATE); + + Status = NpListenPipe(ServerHandle); + ok_eq_hex(Status, STATUS_SUCCESS); + CheckServer(ServerHandle, FILE_PIPE_CONNECTED_STATE); + + //Status = NpWaitPipe(ServerHandle); + //ok_eq_hex(Status, STATUS_ILLEGAL_FUNCTION); + //CheckServer(ServerHandle, FILE_PIPE_CONNECTED_STATE); + + Status = NpListenPipe(ServerHandle); + ok_eq_hex(Status, STATUS_PIPE_CONNECTED); + CheckServer(ServerHandle, FILE_PIPE_CONNECTED_STATE); +} + +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, TRUE, 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); + TestWait(ServerHandle); + ObCloseHandle(ServerHandle, KernelMode); + } +} + +START_TEST(NpfsWait) +{ + PKTHREAD Thread; + + Thread = KmtStartThread(RunTest, NULL); + KmtFinishThread(Thread, NULL); + (VOID)StartWaitThread; + (VOID)FinishWaitThread; + (VOID)CheckWaitPipe; +}