1180 lines
28 KiB
C
1180 lines
28 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
StateSup.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements the Named Pipe State Support routines
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Gary Kimura [GaryKi] 30-Aug-1990
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "NpProcs.h"
|
|||
|
|
|||
|
//
|
|||
|
// The Bug check file id for this module
|
|||
|
//
|
|||
|
|
|||
|
#define BugCheckFileId (NPFS_BUG_CHECK_STATESUP)
|
|||
|
|
|||
|
//
|
|||
|
// The debug trace level
|
|||
|
//
|
|||
|
|
|||
|
#define Dbg (DEBUG_TRACE_STATESUP)
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, NpInitializePipeState)
|
|||
|
#pragma alloc_text(PAGE, NpUninitializePipeState)
|
|||
|
#endif
|
|||
|
|
|||
|
VOID
|
|||
|
NpCancelListeningQueueIrp (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
NpInitializePipeState (
|
|||
|
IN PCCB Ccb,
|
|||
|
IN PFILE_OBJECT ServerFileObject
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine initialize a named pipe instance to the disconnected state.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Ccb - Supplies a pointer to the Ccb representing the pipe state
|
|||
|
|
|||
|
ServerFileObject - Supplies a pointer to the server file object
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
DebugTrace(+1, Dbg, "NpInitializePipeState, Ccb = %08lx\n", Ccb);
|
|||
|
|
|||
|
//
|
|||
|
// Set the ccb and nonpaged ccb fields
|
|||
|
//
|
|||
|
|
|||
|
Ccb->FileObject[ FILE_PIPE_SERVER_END ] = ServerFileObject;
|
|||
|
Ccb->NamedPipeState = FILE_PIPE_DISCONNECTED_STATE;
|
|||
|
|
|||
|
//
|
|||
|
// The file object contexts pointers.
|
|||
|
//
|
|||
|
|
|||
|
NpSetFileObject( ServerFileObject,
|
|||
|
Ccb,
|
|||
|
Ccb->NonpagedCcb,
|
|||
|
FILE_PIPE_SERVER_END );
|
|||
|
|
|||
|
//
|
|||
|
// and return to our caller
|
|||
|
//
|
|||
|
|
|||
|
DebugTrace(-1, Dbg, "NpInitializePipeState -> VOID\n", 0);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
NpUninitializePipeState (
|
|||
|
IN PCCB Ccb
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine initialize a named pipe instance to the disconnected state.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Ccb - Supplies a pointer to the Ccb representing the pipe state
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
DebugTrace(+1, Dbg, "NpUninitializePipeState, Ccb = %08lx\n", Ccb);
|
|||
|
|
|||
|
//
|
|||
|
// The file object contexts pointers for our server to null
|
|||
|
//
|
|||
|
|
|||
|
NpSetFileObject( Ccb->FileObject[ FILE_PIPE_SERVER_END ],
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FILE_PIPE_SERVER_END );
|
|||
|
Ccb->FileObject[ FILE_PIPE_SERVER_END ] = NULL;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// The file object contexts pointers for our client to null
|
|||
|
//
|
|||
|
|
|||
|
NpSetFileObject( Ccb->FileObject[ FILE_PIPE_CLIENT_END ],
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FILE_PIPE_CLIENT_END );
|
|||
|
Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Set to null both pointers to file object.
|
|||
|
//
|
|||
|
|
|||
|
Ccb->FileObject[ FILE_PIPE_SERVER_END ] = NULL;
|
|||
|
Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// and return to our caller
|
|||
|
//
|
|||
|
|
|||
|
DebugTrace(-1, Dbg, "NpUninitializePipeState -> VOID\n", 0);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NpSetListeningPipeState (
|
|||
|
IN PCCB Ccb,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sets a named pipe to the listening state. This routine
|
|||
|
will either complete the IRP right away or put in the listening queue
|
|||
|
to be completed later.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Ccb - Supplies a pointer to the Ccb representing the pipe state
|
|||
|
|
|||
|
Irp - Supplies the Irp doing the listening operation
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
DebugTrace(+1, Dbg, "NpSetListeningPipeState, Ccb = %08lx\n", Ccb);
|
|||
|
|
|||
|
//
|
|||
|
// Case on the current state of the named pipe
|
|||
|
//
|
|||
|
|
|||
|
switch (Ccb->NamedPipeState) {
|
|||
|
|
|||
|
case FILE_PIPE_DISCONNECTED_STATE:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe was disconnected\n", 0);
|
|||
|
|
|||
|
//
|
|||
|
// Set the state to listening and check for any wait for named
|
|||
|
// pipe requests.
|
|||
|
//
|
|||
|
|
|||
|
Ccb->NamedPipeState = FILE_PIPE_LISTENING_STATE;
|
|||
|
|
|||
|
NpCancelWaiter( &NpVcb->WaitQueue,
|
|||
|
&Ccb->Fcb->FullFileName );
|
|||
|
|
|||
|
//
|
|||
|
// If the completion mode is complete operation then we can
|
|||
|
// complete this irp otherwise we need to enqueue the irp
|
|||
|
// into the listening queue, and mark it pending.
|
|||
|
//
|
|||
|
|
|||
|
if (Ccb->CompletionMode[ FILE_PIPE_SERVER_END ] == FILE_PIPE_COMPLETE_OPERATION) {
|
|||
|
|
|||
|
NpCompleteRequest( Irp, STATUS_PIPE_LISTENING );
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
IoMarkIrpPending( Irp );
|
|||
|
|
|||
|
InsertTailList( &Ccb->NonpagedCcb->ListeningQueue, &Irp->Tail.Overlay.ListEntry );
|
|||
|
|
|||
|
//
|
|||
|
// Set the cancel routine and also check if the irp is already cancelled
|
|||
|
//
|
|||
|
|
|||
|
IoAcquireCancelSpinLock( &Irp->CancelIrql );
|
|||
|
Irp->IoStatus.Status = (ULONG)Ccb;
|
|||
|
|
|||
|
if (Irp->Cancel) {
|
|||
|
|
|||
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|||
|
RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
|
|||
|
NpCompleteRequest( Irp, STATUS_CANCELLED );
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
IoSetCancelRoutine( Irp, NpCancelListeningQueueIrp );
|
|||
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|||
|
Status = STATUS_PENDING;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case FILE_PIPE_LISTENING_STATE:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe was listening\n", 0);
|
|||
|
|
|||
|
//
|
|||
|
// If the completion mode is complete operation then we can
|
|||
|
// complete this irp otherwise we need to enqueue the irp
|
|||
|
// into the listening queue, and mark it pending.
|
|||
|
//
|
|||
|
|
|||
|
if (Ccb->CompletionMode[ FILE_PIPE_SERVER_END ] == FILE_PIPE_COMPLETE_OPERATION) {
|
|||
|
|
|||
|
NpCompleteRequest( Irp, STATUS_PIPE_LISTENING );
|
|||
|
Status = STATUS_PIPE_LISTENING;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
IoMarkIrpPending( Irp );
|
|||
|
|
|||
|
InsertTailList( &Ccb->NonpagedCcb->ListeningQueue, &Irp->Tail.Overlay.ListEntry );
|
|||
|
|
|||
|
//
|
|||
|
// Set the cancel routine and also check if the irp is already cancelled
|
|||
|
//
|
|||
|
|
|||
|
IoAcquireCancelSpinLock( &Irp->CancelIrql );
|
|||
|
Irp->IoStatus.Status = (ULONG)Ccb;
|
|||
|
|
|||
|
if (Irp->Cancel) {
|
|||
|
|
|||
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|||
|
RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
|
|||
|
NpCompleteRequest( Irp, STATUS_CANCELLED );
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
IoSetCancelRoutine( Irp, NpCancelListeningQueueIrp );
|
|||
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|||
|
Status = STATUS_PENDING;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case FILE_PIPE_CONNECTED_STATE:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe was connected\n", 0);
|
|||
|
|
|||
|
NpCompleteRequest( Irp, STATUS_PIPE_CONNECTED );
|
|||
|
Status = STATUS_PIPE_CONNECTED;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case FILE_PIPE_CLOSING_STATE:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe was closing\n", 0);
|
|||
|
|
|||
|
NpCompleteRequest( Irp, STATUS_PIPE_CLOSING );
|
|||
|
Status = STATUS_PIPE_CLOSING;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
NpBugCheck( Ccb->NamedPipeState, 0, 0 );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// and return to our caller
|
|||
|
//
|
|||
|
|
|||
|
DebugTrace(-1, Dbg, "NpSetListeningPipeState -> %08lx\n", Status);
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NpSetConnectedPipeState (
|
|||
|
IN PCCB Ccb,
|
|||
|
IN PFILE_OBJECT ClientFileObject
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sets the state of a named pipe to connected.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Ccb - Supplies a pointer to the Ccb representing the pipe state
|
|||
|
|
|||
|
ClientFileObject - Supplies the file object for the client that is
|
|||
|
doing the connect.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
PNONPAGED_CCB NonpagedCcb;
|
|||
|
PIRP LocalIrp;
|
|||
|
KIRQL CancelIrql;
|
|||
|
|
|||
|
DebugTrace(+1, Dbg, "NpSetConnectedPipeState, Ccb = %08lx\n", Ccb);
|
|||
|
|
|||
|
//
|
|||
|
// Save a pointer to the nonpaged ccb, we really need to do this now so when we
|
|||
|
// complete our listening waiters we won't touch paged pool
|
|||
|
//
|
|||
|
|
|||
|
NonpagedCcb = Ccb->NonpagedCcb;
|
|||
|
|
|||
|
//
|
|||
|
// Case on the current state of the named pipe
|
|||
|
//
|
|||
|
|
|||
|
switch (Ccb->NamedPipeState) {
|
|||
|
|
|||
|
case FILE_PIPE_DISCONNECTED_STATE:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe was disconnected\n", 0);
|
|||
|
|
|||
|
NpBugCheck( 0, 0, 0 );
|
|||
|
|
|||
|
case FILE_PIPE_LISTENING_STATE:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe was listening\n", 0);
|
|||
|
|
|||
|
//
|
|||
|
// Set the state of the pipe to connected and adjust the
|
|||
|
// appropriate read mode and completion mode values
|
|||
|
//
|
|||
|
|
|||
|
Ccb->NamedPipeState = FILE_PIPE_CONNECTED_STATE;
|
|||
|
Ccb->ReadMode[ FILE_PIPE_CLIENT_END ] = FILE_PIPE_BYTE_STREAM_MODE;
|
|||
|
Ccb->CompletionMode[ FILE_PIPE_CLIENT_END ] = FILE_PIPE_QUEUE_OPERATION;
|
|||
|
|
|||
|
//
|
|||
|
// Set our back pointer to the client file object and set the
|
|||
|
// client file object context pointers
|
|||
|
//
|
|||
|
|
|||
|
Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = ClientFileObject;
|
|||
|
|
|||
|
NpSetFileObject( ClientFileObject,
|
|||
|
Ccb,
|
|||
|
NonpagedCcb,
|
|||
|
FILE_PIPE_CLIENT_END );
|
|||
|
|
|||
|
//
|
|||
|
// And complete any listening waiters
|
|||
|
//
|
|||
|
|
|||
|
IoAcquireCancelSpinLock( &CancelIrql );
|
|||
|
|
|||
|
while (!IsListEmpty( &NonpagedCcb->ListeningQueue )) {
|
|||
|
PLIST_ENTRY Links;
|
|||
|
|
|||
|
Links = RemoveHeadList( &NonpagedCcb->ListeningQueue );
|
|||
|
|
|||
|
LocalIrp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry );
|
|||
|
|
|||
|
//
|
|||
|
// Disable the cancel routine in the irp before completing the request
|
|||
|
//
|
|||
|
|
|||
|
LocalIrp->CancelIrql = CancelIrql;
|
|||
|
|
|||
|
IoSetCancelRoutine( LocalIrp, NULL );
|
|||
|
|
|||
|
IoReleaseCancelSpinLock( CancelIrql );
|
|||
|
|
|||
|
NpCompleteRequest( LocalIrp, STATUS_SUCCESS );
|
|||
|
|
|||
|
IoAcquireCancelSpinLock( &CancelIrql );
|
|||
|
}
|
|||
|
|
|||
|
IoReleaseCancelSpinLock( CancelIrql );
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case FILE_PIPE_CONNECTED_STATE:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe was connected\n", 0);
|
|||
|
|
|||
|
NpBugCheck( 0, 0, 0 );
|
|||
|
|
|||
|
case FILE_PIPE_CLOSING_STATE:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe was closing\n", 0);
|
|||
|
|
|||
|
NpBugCheck( 0, 0, 0 );
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
NpBugCheck( Ccb->NamedPipeState, 0, 0 );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// and return to our caller
|
|||
|
//
|
|||
|
|
|||
|
DebugTrace(-1, Dbg, "NpSetConnectedPipeState -> %08lx\n", Status);
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NpSetClosingPipeState (
|
|||
|
IN PCCB Ccb,
|
|||
|
IN PIRP Irp,
|
|||
|
IN NAMED_PIPE_END NamedPipeEnd
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sets a pipe state to closing. This routine will
|
|||
|
either complete the irp right away or put in on the data queue
|
|||
|
to be completed later.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Ccb - Supplies a pointer to the Ccb representing the pipe state
|
|||
|
|
|||
|
Irp - Supplies the Irp trying to do the close operation
|
|||
|
|
|||
|
NamedPipeEnd - Indicates if the server or client is doing the transition
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS -
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
PNONPAGED_CCB NonpagedCcb;
|
|||
|
|
|||
|
PFCB Fcb;
|
|||
|
PIRP LocalIrp;
|
|||
|
KIRQL CancelIrql;
|
|||
|
|
|||
|
PDATA_QUEUE ReadQueue;
|
|||
|
PDATA_QUEUE WriteQueue;
|
|||
|
|
|||
|
PEVENT_TABLE_ENTRY Event;
|
|||
|
|
|||
|
DebugTrace(+1, Dbg, "NpSetClosingPipeState, Ccb = %08lx\n", Ccb);
|
|||
|
|
|||
|
Fcb = Ccb->Fcb;
|
|||
|
|
|||
|
//
|
|||
|
// Save a pointer to the nonpaged ccb, we really need to do this now so when we
|
|||
|
// complete our listening waiters we won't touch paged pool
|
|||
|
//
|
|||
|
|
|||
|
NonpagedCcb = Ccb->NonpagedCcb;
|
|||
|
|
|||
|
//
|
|||
|
// Case on the current state of the named pipe
|
|||
|
//
|
|||
|
|
|||
|
switch (Ccb->NamedPipeState) {
|
|||
|
|
|||
|
case FILE_PIPE_DISCONNECTED_STATE:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe was disconnected\n", 0);
|
|||
|
|
|||
|
ASSERT( NamedPipeEnd == FILE_PIPE_SERVER_END );
|
|||
|
|
|||
|
//
|
|||
|
// Pipe is disconnected, for safety sake we'll zero out the
|
|||
|
// file objects context pointers to use
|
|||
|
//
|
|||
|
|
|||
|
NpSetFileObject( Ccb->FileObject[ FILE_PIPE_SERVER_END ],
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FILE_PIPE_SERVER_END );
|
|||
|
Ccb->FileObject[ FILE_PIPE_SERVER_END ] = NULL;
|
|||
|
|
|||
|
NpSetFileObject( Ccb->FileObject[ FILE_PIPE_CLIENT_END ],
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FILE_PIPE_CLIENT_END );
|
|||
|
Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Close it we delete the instance, and possibly the Fcb if its
|
|||
|
// open count is now zero.
|
|||
|
//
|
|||
|
|
|||
|
NpDeleteCcb( Ccb );
|
|||
|
if (Fcb->OpenCount == 0) {
|
|||
|
|
|||
|
NpDeleteFcb( Fcb );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// And now complete the irp
|
|||
|
//
|
|||
|
|
|||
|
NpCompleteRequest( Irp, STATUS_SUCCESS );
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case FILE_PIPE_LISTENING_STATE:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe was listening\n", 0);
|
|||
|
|
|||
|
ASSERT( NamedPipeEnd == FILE_PIPE_SERVER_END );
|
|||
|
|
|||
|
//
|
|||
|
// Pipe in listening state, so complete all IRPs that are in the
|
|||
|
// listening queue with a closing status, and then delete the
|
|||
|
// instance and possibly the Fcb if its open count is now zero
|
|||
|
//
|
|||
|
|
|||
|
IoAcquireCancelSpinLock( &CancelIrql );
|
|||
|
|
|||
|
while (!IsListEmpty( &NonpagedCcb->ListeningQueue )) {
|
|||
|
PLIST_ENTRY Links;
|
|||
|
|
|||
|
Links = RemoveHeadList( &NonpagedCcb->ListeningQueue );
|
|||
|
|
|||
|
LocalIrp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry );
|
|||
|
|
|||
|
//
|
|||
|
// Disable the cancel routine in the irp before completing the request
|
|||
|
//
|
|||
|
|
|||
|
LocalIrp->CancelIrql = CancelIrql;
|
|||
|
|
|||
|
IoSetCancelRoutine( LocalIrp, NULL );
|
|||
|
|
|||
|
IoReleaseCancelSpinLock( CancelIrql );
|
|||
|
|
|||
|
NpCompleteRequest( LocalIrp, STATUS_PIPE_BROKEN );
|
|||
|
|
|||
|
IoAcquireCancelSpinLock( &CancelIrql );
|
|||
|
}
|
|||
|
|
|||
|
IoReleaseCancelSpinLock( CancelIrql );
|
|||
|
|
|||
|
//
|
|||
|
// For safety sake we'll zero out the file objects context
|
|||
|
// pointers to use
|
|||
|
//
|
|||
|
|
|||
|
NpSetFileObject( Ccb->FileObject[ FILE_PIPE_SERVER_END ],
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FILE_PIPE_SERVER_END );
|
|||
|
Ccb->FileObject[ FILE_PIPE_SERVER_END ] = NULL;
|
|||
|
|
|||
|
NpSetFileObject( Ccb->FileObject[ FILE_PIPE_CLIENT_END ],
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FILE_PIPE_CLIENT_END );
|
|||
|
Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Remove the ccb and possibly the Fcb
|
|||
|
//
|
|||
|
|
|||
|
NpDeleteCcb( Ccb );
|
|||
|
if (Fcb->OpenCount == 0) {
|
|||
|
|
|||
|
NpDeleteFcb( Fcb );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// And now complete the irp
|
|||
|
//
|
|||
|
|
|||
|
NpCompleteRequest( Irp, STATUS_SUCCESS );
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case FILE_PIPE_CONNECTED_STATE:
|
|||
|
|
|||
|
//
|
|||
|
// The pipe is connected so decide who is trying to do the close
|
|||
|
// and then fall into common code
|
|||
|
//
|
|||
|
|
|||
|
if (NamedPipeEnd == FILE_PIPE_SERVER_END) {
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe was connected, server doing close\n", 0);
|
|||
|
|
|||
|
ReadQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ];
|
|||
|
WriteQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ];
|
|||
|
|
|||
|
Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ];
|
|||
|
|
|||
|
//
|
|||
|
// For safety sake we'll zero out the file objects context
|
|||
|
// pointers to use
|
|||
|
//
|
|||
|
|
|||
|
NpSetFileObject( Ccb->FileObject[ FILE_PIPE_SERVER_END ],
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FILE_PIPE_SERVER_END );
|
|||
|
Ccb->FileObject[ FILE_PIPE_SERVER_END ] = NULL;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe was connected, client doing close\n", 0);
|
|||
|
|
|||
|
ReadQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ];
|
|||
|
WriteQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ];
|
|||
|
|
|||
|
Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_SERVER_END ];
|
|||
|
|
|||
|
//
|
|||
|
// For safety sake we'll zero out the file objects context
|
|||
|
// pointers to use
|
|||
|
//
|
|||
|
|
|||
|
NpSetFileObject( Ccb->FileObject[ FILE_PIPE_CLIENT_END ],
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FILE_PIPE_CLIENT_END );
|
|||
|
Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// To do a close on a connected pipe we set its state to closing
|
|||
|
// drain the read queue and drain reads on the write queue.
|
|||
|
//
|
|||
|
//
|
|||
|
// Closing <---ReadQueue---- [ Remove all entries ]
|
|||
|
// End
|
|||
|
// ---WriteQueue---> [ Remove only read entries ]
|
|||
|
//
|
|||
|
|
|||
|
Ccb->NamedPipeState = FILE_PIPE_CLOSING_STATE;
|
|||
|
|
|||
|
while (!NpIsDataQueueEmpty( ReadQueue )) {
|
|||
|
|
|||
|
if ((LocalIrp = NpRemoveDataQueueEntry( ReadQueue )) != NULL) {
|
|||
|
|
|||
|
NpCompleteRequest( LocalIrp, STATUS_PIPE_BROKEN );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
while (!NpIsDataQueueEmpty( WriteQueue ) &&
|
|||
|
(WriteQueue->QueueState == ReadEntries)) {
|
|||
|
|
|||
|
if ((LocalIrp = NpRemoveDataQueueEntry( WriteQueue )) != NULL) {
|
|||
|
|
|||
|
NpCompleteRequest( LocalIrp, STATUS_PIPE_BROKEN );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now if the write queue is empty then we complete this
|
|||
|
// close request with success otherwise we need to enqueue
|
|||
|
// the irp to the write queue and set our return status to pending
|
|||
|
// also the mark the irp pending
|
|||
|
//
|
|||
|
|
|||
|
if ( TRUE || NpIsDataQueueEmpty( WriteQueue ) ) {
|
|||
|
|
|||
|
NpCompleteRequest( Irp, STATUS_SUCCESS );
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
IoMarkIrpPending( Irp );
|
|||
|
|
|||
|
(VOID)NpAddDataQueueEntry( WriteQueue, WriteEntries, Close, 0, Irp, NULL );
|
|||
|
Status = STATUS_PENDING;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now signal the other sides event to show that something has
|
|||
|
// happened
|
|||
|
//
|
|||
|
|
|||
|
NpSignalEventTableEntry( Event );
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case FILE_PIPE_CLOSING_STATE:
|
|||
|
|
|||
|
//
|
|||
|
// The pipe is closing so decide who is trying to complete the close
|
|||
|
// and then fall into common code
|
|||
|
//
|
|||
|
|
|||
|
if (NamedPipeEnd == FILE_PIPE_SERVER_END) {
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe was closing, server doing close\n", 0);
|
|||
|
|
|||
|
ReadQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ];
|
|||
|
|
|||
|
//
|
|||
|
// For safety sake we'll zero out the file objects context
|
|||
|
// pointers to use
|
|||
|
//
|
|||
|
|
|||
|
NpSetFileObject( Ccb->FileObject[ FILE_PIPE_SERVER_END ],
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FILE_PIPE_SERVER_END );
|
|||
|
Ccb->FileObject[ FILE_PIPE_SERVER_END ] = NULL;
|
|||
|
|
|||
|
NpSetFileObject( Ccb->FileObject[ FILE_PIPE_CLIENT_END ],
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FILE_PIPE_CLIENT_END );
|
|||
|
Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe was closing, client doing close\n", 0);
|
|||
|
|
|||
|
ReadQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ];
|
|||
|
|
|||
|
//
|
|||
|
// For safety sake we'll zero out the file objects context
|
|||
|
// pointers to use
|
|||
|
//
|
|||
|
|
|||
|
NpSetFileObject( Ccb->FileObject[ FILE_PIPE_SERVER_END ],
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FILE_PIPE_SERVER_END );
|
|||
|
Ccb->FileObject[ FILE_PIPE_SERVER_END ] = NULL;
|
|||
|
|
|||
|
NpSetFileObject( Ccb->FileObject[ FILE_PIPE_CLIENT_END ],
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FILE_PIPE_CLIENT_END );
|
|||
|
Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// To do a close on a closing pipe we drain the read queue of
|
|||
|
// all its entries, delete the instance, and possibly delete the
|
|||
|
// Fcb if its open count is now zero.
|
|||
|
//
|
|||
|
//
|
|||
|
// Previously <-----Closed----- Closing
|
|||
|
// Closed End
|
|||
|
// End ----ReadQueue--->
|
|||
|
//
|
|||
|
|
|||
|
while (!NpIsDataQueueEmpty( ReadQueue )) {
|
|||
|
|
|||
|
if ((LocalIrp = NpRemoveDataQueueEntry( ReadQueue )) != NULL) {
|
|||
|
|
|||
|
NpCompleteRequest( LocalIrp, STATUS_PIPE_BROKEN );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
NpDeleteCcb( Ccb );
|
|||
|
if (Fcb->OpenCount == 0) {
|
|||
|
|
|||
|
NpDeleteFcb( Fcb );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// And now complete the irp
|
|||
|
//
|
|||
|
|
|||
|
NpCompleteRequest( Irp, STATUS_SUCCESS );
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
NpBugCheck( Ccb->NamedPipeState, 0, 0 );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// and return to our caller
|
|||
|
//
|
|||
|
|
|||
|
DebugTrace(-1, Dbg, "NpSetClosingPipeState -> %08lx\n", Status);
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NpSetDisconnectedPipeState (
|
|||
|
IN PCCB Ccb
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sets a pipe state to disconnected, only the server is
|
|||
|
allowed to do this transition
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Ccb - Supplies a pointer to the Ccb representing the pipe instance
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
PNONPAGED_CCB NonpagedCcb;
|
|||
|
|
|||
|
PIRP Irp;
|
|||
|
|
|||
|
PDATA_QUEUE Inbound;
|
|||
|
PDATA_QUEUE Outbound;
|
|||
|
PEVENT_TABLE_ENTRY ClientEvent;
|
|||
|
|
|||
|
DebugTrace(+1, Dbg, "NpSetDisconnectedPipeState, Ccb = %08lx\n", Ccb);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Save a pointer to the nonpaged ccb, we really need to do this now so when we
|
|||
|
// complete our listening waiters we won't touch paged pool
|
|||
|
//
|
|||
|
|
|||
|
NonpagedCcb = Ccb->NonpagedCcb;
|
|||
|
|
|||
|
//
|
|||
|
// Case on the current state of the named pipe
|
|||
|
//
|
|||
|
|
|||
|
switch (Ccb->NamedPipeState) {
|
|||
|
|
|||
|
case FILE_PIPE_DISCONNECTED_STATE:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe already disconnected\n", 0);
|
|||
|
|
|||
|
//
|
|||
|
// pipe already disconnected so there is no work for us to do
|
|||
|
//
|
|||
|
|
|||
|
Status = STATUS_PIPE_DISCONNECTED;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case FILE_PIPE_LISTENING_STATE:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe was listening\n", 0);
|
|||
|
|
|||
|
//
|
|||
|
// Pipe in listening state, so complete all IRPs that are in the
|
|||
|
// listening queue with a disconnected status
|
|||
|
//
|
|||
|
|
|||
|
while (!IsListEmpty( &NonpagedCcb->ListeningQueue )) {
|
|||
|
PLIST_ENTRY Links;
|
|||
|
|
|||
|
Links = RemoveHeadList( &NonpagedCcb->ListeningQueue );
|
|||
|
|
|||
|
Irp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry );
|
|||
|
|
|||
|
//
|
|||
|
// Disable the cancel routine in the irp before completing the request
|
|||
|
//
|
|||
|
|
|||
|
IoAcquireCancelSpinLock( &Irp->CancelIrql );
|
|||
|
IoSetCancelRoutine( Irp, NULL );
|
|||
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|||
|
|
|||
|
NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED );
|
|||
|
}
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case FILE_PIPE_CONNECTED_STATE:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe was connected\n", 0);
|
|||
|
|
|||
|
Inbound = &Ccb->DataQueue[ FILE_PIPE_INBOUND ];
|
|||
|
Outbound = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ];
|
|||
|
ClientEvent = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ];
|
|||
|
|
|||
|
//
|
|||
|
// Pipe is connected so we need to discard all of the data queues
|
|||
|
// and complete any of their IRPs with status disconnected.
|
|||
|
//
|
|||
|
|
|||
|
while (!NpIsDataQueueEmpty( Inbound )) {
|
|||
|
|
|||
|
if ((Irp = NpRemoveDataQueueEntry( Inbound )) != NULL) {
|
|||
|
|
|||
|
NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
while (!NpIsDataQueueEmpty( Outbound )) {
|
|||
|
|
|||
|
if ((Irp = NpRemoveDataQueueEntry( Outbound )) != NULL) {
|
|||
|
|
|||
|
NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Signal the client event and then remove it from the pipe
|
|||
|
//
|
|||
|
|
|||
|
NpSignalEventTableEntry( ClientEvent );
|
|||
|
|
|||
|
NpDeleteEventTableEntry( &NpVcb->EventTable, ClientEvent );
|
|||
|
NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ] = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Disable the client's file object
|
|||
|
//
|
|||
|
|
|||
|
NpSetFileObject( Ccb->FileObject[ FILE_PIPE_CLIENT_END ],
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FILE_PIPE_CLIENT_END );
|
|||
|
Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL;
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case FILE_PIPE_CLOSING_STATE:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Pipe was closing\n", 0);
|
|||
|
|
|||
|
Inbound = &Ccb->DataQueue[ FILE_PIPE_INBOUND ];
|
|||
|
Outbound = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ];
|
|||
|
ClientEvent = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ];
|
|||
|
|
|||
|
//
|
|||
|
// Pipe is closing (this had to have been done by the client) we
|
|||
|
// need to discard all of the data queues (only the inbound can have
|
|||
|
// entries) and complete any of their IRPs with status disconnected.
|
|||
|
//
|
|||
|
//
|
|||
|
// Server <----Inbound---- Client
|
|||
|
// End End
|
|||
|
// ----Closed----->
|
|||
|
//
|
|||
|
|
|||
|
while (!NpIsDataQueueEmpty( Inbound )) {
|
|||
|
|
|||
|
if ((Irp = NpRemoveDataQueueEntry( Inbound )) != NULL) {
|
|||
|
|
|||
|
NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ASSERT( NpIsDataQueueEmpty( Outbound ) );
|
|||
|
|
|||
|
//
|
|||
|
// The client event should already be gone but for safety sake
|
|||
|
// we'll make sure its gone.
|
|||
|
//
|
|||
|
|
|||
|
NpDeleteEventTableEntry( &NpVcb->EventTable, ClientEvent );
|
|||
|
NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ] = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Also if it's still connected, disable the client's file object
|
|||
|
//
|
|||
|
|
|||
|
NpSetFileObject( Ccb->FileObject[ FILE_PIPE_CLIENT_END ],
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FILE_PIPE_CLIENT_END );
|
|||
|
Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL;
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
NpBugCheck( Ccb->NamedPipeState, 0, 0 );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the state to disconnected
|
|||
|
//
|
|||
|
|
|||
|
Ccb->NamedPipeState = FILE_PIPE_DISCONNECTED_STATE;
|
|||
|
|
|||
|
//
|
|||
|
// and return to our caller
|
|||
|
//
|
|||
|
|
|||
|
DebugTrace(-1, Dbg, "NpSetDisconnectedPipeState -> %08lx\n", Status);
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Local support routine
|
|||
|
//
|
|||
|
|
|||
|
VOID
|
|||
|
NpCancelListeningQueueIrp (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine implements the cancel function for an IRP saved in a listening
|
|||
|
queue
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - ignored
|
|||
|
|
|||
|
Irp - Supplies the Irp being cancelled. A pointer to the ccb
|
|||
|
structure is stored in the information field of the Irp Iosb
|
|||
|
field.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PCCB Ccb;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|||
|
|
|||
|
//
|
|||
|
// The status field is used to store a pointer to the Ccb
|
|||
|
// containing this irp
|
|||
|
//
|
|||
|
|
|||
|
Ccb = (PCCB)Irp->IoStatus.Status;
|
|||
|
|
|||
|
//
|
|||
|
// We now need to void the cancel routine and release the io cancel spinlock
|
|||
|
//
|
|||
|
|
|||
|
IoSetCancelRoutine( Irp, NULL );
|
|||
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|||
|
|
|||
|
//
|
|||
|
// Get exclusive access to the named pipe vcb so we can now do our work
|
|||
|
//
|
|||
|
|
|||
|
NpAcquireExclusiveVcb();
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
PNONPAGED_CCB NonPagedCcb = Ccb->NonpagedCcb;
|
|||
|
PLIST_ENTRY Links;
|
|||
|
BOOLEAN RemoveEntry = FALSE;
|
|||
|
|
|||
|
if (NodeType( NonPagedCcb ) == NPFS_NTC_NONPAGED_CCB) {
|
|||
|
|
|||
|
//
|
|||
|
// We need to check that the Irp is still in the listening queue.
|
|||
|
//
|
|||
|
|
|||
|
for (Links = NonPagedCcb->ListeningQueue.Flink;
|
|||
|
Links != &NonPagedCcb->ListeningQueue;
|
|||
|
Links = Links->Flink) {
|
|||
|
|
|||
|
if (CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry ) == Irp) {
|
|||
|
|
|||
|
RemoveEntry = TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (RemoveEntry) {
|
|||
|
|
|||
|
RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
|
|||
|
NpCompleteRequest( Irp, STATUS_CANCELLED );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Catch all exceptions. It's possible that the Ccb has gone away in
|
|||
|
// the meantime and there is now no Irp to complete.
|
|||
|
//
|
|||
|
|
|||
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NOTHING;
|
|||
|
}
|
|||
|
|
|||
|
NpReleaseVcb();
|
|||
|
|
|||
|
//
|
|||
|
// And return to our caller
|
|||
|
//
|
|||
|
|
|||
|
return;
|
|||
|
}
|