627 lines
12 KiB
C
627 lines
12 KiB
C
|
/*++
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
}
|