NT4/private/ntos/npfs/waitsup.c

627 lines
12 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*++
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;
}