summaryrefslogtreecommitdiffstats
path: root/private/ntos/npfs/waitsup.c
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/npfs/waitsup.c
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/ntos/npfs/waitsup.c')
-rw-r--r--private/ntos/npfs/waitsup.c626
1 files changed, 626 insertions, 0 deletions
diff --git a/private/ntos/npfs/waitsup.c b/private/ntos/npfs/waitsup.c
new file mode 100644
index 000000000..fa03790e2
--- /dev/null
+++ b/private/ntos/npfs/waitsup.c
@@ -0,0 +1,626 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ WaitSup.c
+
+Abstract:
+
+ This module implements the Wait for Named Pipe support routines.
+
+Author:
+
+ Gary Kimura [GaryKi] 30-Aug-1990
+
+Revision History:
+
+--*/
+
+#include "NpProcs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_WAITSUP)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, NpInitializeWaitQueue)
+#pragma alloc_text(PAGE, NpUninitializeWaitQueue)
+#endif
+
+
+//
+// Local procedures and structures
+//
+
+typedef struct _WAIT_CONTEXT {
+ KDPC Dpc;
+ KTIMER Timer;
+ PWAIT_QUEUE WaitQueue;
+} WAIT_CONTEXT;
+typedef WAIT_CONTEXT *PWAIT_CONTEXT;
+
+VOID
+NpTimerDispatch(
+ IN PKDPC Dpc,
+ IN PVOID Contxt,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2
+ );
+
+VOID
+NpCancelWaitQueueIrp(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+
+VOID
+NpInitializeWaitQueue (
+ IN PWAIT_QUEUE WaitQueue
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initializes the wait for named pipe queue.
+
+Arguments:
+
+ WaitQueue - Supplies a pointer to the list head being initialized
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NpInitializeWaitQueue, WaitQueue = %08lx\n", WaitQueue);
+
+ //
+ // Initialize the List head
+ //
+
+ InitializeListHead( &WaitQueue->Queue );
+
+ //
+ // Initialize the Wait Queue's spinlock
+ //
+
+ KeInitializeSpinLock( &WaitQueue->SpinLock );
+
+ //
+ // and return to our caller
+ //
+
+ DebugTrace(-1, Dbg, "NpInitializeWaitQueue -> VOID\n", 0);
+
+ return;
+}
+
+
+VOID
+NpUninitializeWaitQueue (
+ IN PWAIT_QUEUE WaitQueue
+ )
+
+/*++
+
+Routine Description:
+
+ This routine uninitializes the wait for named pipe queue.
+
+Arguments:
+
+ WaitQueue - Supplies a pointer to the list head being uninitialized
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NpInitializeWaitQueue, WaitQueue = %08lx\n", WaitQueue);
+
+ //
+ // And return to our caller
+ //
+
+ DebugTrace(-1, Dbg, "NpInitializeWaitQueue -> VOID\n", 0);
+
+ return;
+}
+
+
+VOID
+NpAddWaiter (
+ IN PWAIT_QUEUE WaitQueue,
+ IN LARGE_INTEGER DefaultTimeOut,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine adds a new "wait for named pipe" IRP to the wait queue.
+ After calling this function the caller nolonger can access the IRP
+
+Arguments:
+
+ WaitQueue - Supplies the wait queue being used
+
+ DefaultTimeOut - Supplies the default time out to use if one is
+ not supplied in the Irp
+
+ Irp - Supplies a pointer to the wait Irp
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ KIRQL OldIrql;
+ PWAIT_CONTEXT Context;
+ PFILE_PIPE_WAIT_FOR_BUFFER WaitForBuffer;
+ LARGE_INTEGER Timeout;
+ ULONG i;
+
+ DebugTrace(+1, Dbg, "NpAddWaiter, WaitQueue = %08lx\n", WaitQueue);
+
+ //
+ // Allocate a dpc and timer structure and initialize them
+ //
+
+ Context = FsRtlAllocatePool( NonPagedPool, sizeof(WAIT_CONTEXT) );
+
+ KeInitializeDpc( &Context->Dpc, NpTimerDispatch, Irp );
+
+ KeInitializeTimer( &Context->Timer );
+
+ Context->WaitQueue = WaitQueue;
+
+ //
+ // Have the information of the irp point to the context buffer
+ //
+
+ Irp->IoStatus.Information = (ULONG)Context;
+
+ //
+ // Figure out our timeout value
+ //
+
+ WaitForBuffer = (PFILE_PIPE_WAIT_FOR_BUFFER)Irp->AssociatedIrp.SystemBuffer;
+
+ if (WaitForBuffer->TimeoutSpecified) {
+
+ Timeout = WaitForBuffer->Timeout;
+
+ } else {
+
+ Timeout = DefaultTimeOut;
+ }
+
+ //
+ // Upcase the name of the pipe we are waiting for
+ //
+
+ for (i = 0; i < WaitForBuffer->NameLength/sizeof(WCHAR); i += 1) {
+
+ WaitForBuffer->Name[i] = RtlUpcaseUnicodeChar(WaitForBuffer->Name[i]);
+ }
+
+ //
+ // Acquire the spinlock
+ //
+
+ KeAcquireSpinLock( &WaitQueue->SpinLock, &OldIrql );
+
+ try {
+
+ //
+ // Now insert this new entry into the Wait Queue
+ //
+
+ InsertTailList( &WaitQueue->Queue, &Irp->Tail.Overlay.ListEntry );
+
+ //
+ // And set the timer to go off
+ //
+
+ (VOID)KeSetTimer( &Context->Timer, Timeout, &Context->Dpc );
+
+ //
+ // Now set the cancel routine for the irp and check if it has been cancelled.
+ //
+
+ IoAcquireCancelSpinLock( &Irp->CancelIrql );
+ Irp->IoStatus.Status = (ULONG)WaitQueue;
+
+ if (Irp->Cancel) {
+
+ NpCancelWaitQueueIrp( ((PVOID)0x1), Irp );
+
+ } else {
+
+ IoSetCancelRoutine( Irp, NpCancelWaitQueueIrp );
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+ }
+
+ } finally {
+
+ //
+ // Release the spinlock
+ //
+
+ KeReleaseSpinLock( &WaitQueue->SpinLock, OldIrql );
+ }
+
+ //
+ // And now return to our caller
+ //
+
+ DebugTrace(-1, Dbg, "NpAddWaiter -> VOID\n", 0);
+
+ return;
+}
+
+
+VOID
+NpCancelWaiter (
+ IN PWAIT_QUEUE WaitQueue,
+ IN PUNICODE_STRING NameOfPipe
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure cancels all waiters that are waiting for the named
+ pipe to reach the listening state. The corresponding IRPs are completed
+ with STATUS_SUCCESS.
+
+Arguments:
+
+ WaitQueue - Supplies the wait queue being modified
+
+ NameOfPipe - Supplies the name of the named pipe (device relative)
+ that has just reached the listening state.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY Links;
+ PIRP Irp;
+ PFILE_PIPE_WAIT_FOR_BUFFER WaitForBuffer;
+ PWAIT_CONTEXT Context;
+ ULONG i;
+
+ UNICODE_STRING NonPagedNameOfPipe;
+
+ DebugTrace(+1, Dbg, "NpCancelWaiter, WaitQueue = %08lx\n", WaitQueue);
+
+ //
+ // Capture the name of pipe before we grab the spinlock, and upcase it
+ //
+
+ NonPagedNameOfPipe.Buffer = FsRtlAllocatePool( NonPagedPool, NameOfPipe->Length );
+ NonPagedNameOfPipe.Length = 0;
+ NonPagedNameOfPipe.MaximumLength = NameOfPipe->Length;
+
+ (VOID) RtlUpcaseUnicodeString( &NonPagedNameOfPipe, NameOfPipe, FALSE );
+
+ //
+ // Acquire the spinlock
+ //
+
+ KeAcquireSpinLock( &WaitQueue->SpinLock, &OldIrql );
+
+ try {
+
+ //
+ // For each waiting irp check if the name matches
+ //
+
+ for (Links = WaitQueue->Queue.Flink;
+ Links != &WaitQueue->Queue;
+ Links = Links->Flink) {
+
+ Irp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry );
+ WaitForBuffer = (PFILE_PIPE_WAIT_FOR_BUFFER)Irp->AssociatedIrp.SystemBuffer;
+ Context = (PWAIT_CONTEXT)Irp->IoStatus.Information;
+
+ //
+ // Check if this Irp matches the one we've been waiting for
+ // First check the lengths for equality, and then compare
+ // the strings. They match if we exit the inner loop with
+ // i >= name length.
+ //
+
+ if (((USHORT)(WaitForBuffer->NameLength + sizeof(WCHAR))) == NonPagedNameOfPipe.Length) {
+
+ for (i = 0; i < WaitForBuffer->NameLength/sizeof(WCHAR); i += 1) {
+
+ if (WaitForBuffer->Name[i] != NonPagedNameOfPipe.Buffer[i+1]) {
+
+ break;
+ }
+ }
+
+ if (i >= WaitForBuffer->NameLength/sizeof(WCHAR)) {
+
+ //
+ // We need to complete this irp so we first
+ // stop the timer, dequeue it from the wait queue
+ // (be sure to keep links correct), disable the cancel routine
+ // and then complete the Irp.
+ //
+
+ if (KeCancelTimer( &Context->Timer )) {
+
+ Links = Links->Blink;
+
+ RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
+
+ IoAcquireCancelSpinLock( &Irp->CancelIrql );
+ IoSetCancelRoutine( Irp, NULL );
+ Irp->IoStatus.Information = 0;
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+
+ NpCompleteRequest( Irp, STATUS_SUCCESS );
+
+ ExFreePool( Context );
+ }
+ }
+ }
+ }
+
+ } finally {
+
+ //
+ // Release the spinlock
+ //
+
+ KeReleaseSpinLock( &WaitQueue->SpinLock, OldIrql );
+
+ ExFreePool( NonPagedNameOfPipe.Buffer );
+
+ DebugTrace(-1, Dbg, "NpCancelWaiter -> VOID\n", 0);
+ }
+
+ //
+ // And now return to our caller
+ //
+
+ return;
+}
+
+
+//
+// Local support routine
+//
+
+VOID
+NpTimerDispatch(
+ IN PKDPC Dpc,
+ IN PVOID Contxt,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called whenever a timer on a wait queue Irp goes off
+
+Arguments:
+
+ Dpc - Ignored
+
+ Contxt - Supplies a pointer to the irp whose timer went off
+
+ SystemArgument1 - Ignored
+
+ SystemArgument2 - Ignored
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ PIRP Irp = Contxt;
+ KIRQL OldIrql;
+ PLIST_ENTRY Links;
+ PWAIT_CONTEXT Context;
+ PWAIT_QUEUE WaitQueue;
+
+ UNREFERENCED_PARAMETER( Dpc );
+ UNREFERENCED_PARAMETER( SystemArgument1 );
+ UNREFERENCED_PARAMETER( SystemArgument2 );
+
+ Context = (PWAIT_CONTEXT)Irp->IoStatus.Information;
+ WaitQueue = Context->WaitQueue;
+
+ KeAcquireSpinLock( &WaitQueue->SpinLock, &OldIrql );
+
+ try {
+
+ //
+ // Check if the Irp is still in the waiting queue. We need to do
+ // this because we might be in the middle of canceling the entry
+ // when the timer went off.
+ //
+
+ for (Links = WaitQueue->Queue.Flink;
+ Links != &WaitQueue->Queue;
+ Links = Links->Flink) {
+
+ if (Irp == CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry )) {
+
+ //
+ // Remove the IRP, and complete it with a result of timeout
+ //
+
+ RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
+
+ NpCompleteRequest( Irp, STATUS_IO_TIMEOUT );
+
+ //
+ // Deallocate the context
+ //
+
+ ExFreePool( Context );
+
+ //
+ // And exit from the loop because we found our match
+ //
+
+ break;
+ }
+ }
+
+ } finally {
+
+ //
+ // Release the spinlock
+ //
+
+ KeReleaseSpinLock( &WaitQueue->SpinLock, OldIrql );
+ }
+
+ //
+ // And now return to our caller
+ //
+
+ return;
+}
+
+
+//
+// Local Support routine
+//
+
+VOID
+NpCancelWaitQueueIrp(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to cancel a wait queue irp
+
+Arguments:
+
+ DeviceObject - Ignored
+
+ Irp - Supplies the Irp being cancelled. The Iosb.Status field in the irp
+ points to the wait queue
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ PWAIT_QUEUE WaitQueue;
+ KIRQL OldIrql;
+ PLIST_ENTRY Links;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+
+ //
+ // The status field is used to store a pointer to the wait queue
+ // containing this irp
+ //
+
+ WaitQueue = (PWAIT_QUEUE)Irp->IoStatus.Status;
+
+ //
+ // We now need to void the cancel routine and release the io cancel spinlock
+ //
+
+ IoSetCancelRoutine( Irp, NULL );
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+
+ //
+ // Get the spinlock proctecting the wait queue
+ //
+
+ if (DeviceObject != (PVOID)0x1) { KeAcquireSpinLock( &WaitQueue->SpinLock, &OldIrql ); }
+
+ try {
+
+ //
+ // For each waiting irp check if it has been cancelled
+ //
+
+ for (Links = WaitQueue->Queue.Flink;
+ Links != &WaitQueue->Queue;
+ Links = Links->Flink) {
+
+ PIRP LocalIrp;
+ PWAIT_CONTEXT Context;
+
+ LocalIrp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry );
+ Context = (PWAIT_CONTEXT)LocalIrp->IoStatus.Information;
+
+ if (LocalIrp->Cancel) {
+
+ //
+ // We need to complete this irp so we first
+ // stop the timer, dequeue it from the wait queue
+ // (be sure to keep links correct), and then complete the Irp.
+ //
+
+ if (KeCancelTimer( &Context->Timer )) {
+
+ Links = Links->Blink;
+
+ RemoveEntryList( &LocalIrp->Tail.Overlay.ListEntry );
+
+ LocalIrp->IoStatus.Information = 0;
+
+ NpCompleteRequest( LocalIrp, STATUS_CANCELLED );
+ ExFreePool( Context );
+ }
+ }
+ }
+
+ } finally {
+
+ if (DeviceObject != (PVOID)0x1) { KeReleaseSpinLock( &WaitQueue->SpinLock, OldIrql ); }
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return;
+
+
+
+}