NT4/private/ntos/npfs/fsctrl.c

3297 lines
82 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
FsContrl.c
Abstract:
This module implements the File System Control routine for NPFS called by
the dispatch driver.
Author:
Gary Kimura [GaryKi] 21-Aug-1990
Revision History:
--*/
#include "NpProcs.h"
//
// The Bug check file id for this module
//
#define BugCheckFileId (NPFS_BUG_CHECK_FSCTRL)
//
// The debug trace level
//
#define Dbg (DEBUG_TRACE_FSCONTRL)
//
// Local procedure prototypes
//
NTSTATUS
NpCommonFileSystemControl (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
);
NTSTATUS
NpAssignEvent (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
);
NTSTATUS
NpDisconnect (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
);
NTSTATUS
NpListen (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
);
NTSTATUS
NpPeek (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
);
NTSTATUS
NpQueryEvent (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
);
NTSTATUS
NpTransceive (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
);
NTSTATUS
NpWaitForNamedPipe (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
);
NTSTATUS
NpImpersonate (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
);
NTSTATUS
NpInternalRead (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp,
IN BOOLEAN ReadOverflowOperation
);
NTSTATUS
NpInternalWrite (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
);
NTSTATUS
NpInternalTransceive (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
);
NTSTATUS
NpQueryClientProcess (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
);
NTSTATUS
NpSetClientProcess (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
);
NTSTATUS
NpCompleteTransceiveIrp (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NpAssignEvent)
#pragma alloc_text(PAGE, NpCommonFileSystemControl)
#pragma alloc_text(PAGE, NpCompleteTransceiveIrp)
#pragma alloc_text(PAGE, NpDisconnect)
#pragma alloc_text(PAGE, NpFsdFileSystemControl)
#pragma alloc_text(PAGE, NpImpersonate)
#pragma alloc_text(PAGE, NpInternalRead)
#pragma alloc_text(PAGE, NpInternalTransceive)
#pragma alloc_text(PAGE, NpInternalWrite)
#pragma alloc_text(PAGE, NpListen)
#pragma alloc_text(PAGE, NpPeek)
#pragma alloc_text(PAGE, NpQueryClientProcess)
#pragma alloc_text(PAGE, NpQueryEvent)
#pragma alloc_text(PAGE, NpSetClientProcess)
#pragma alloc_text(PAGE, NpTransceive)
#pragma alloc_text(PAGE, NpWaitForNamedPipe)
#endif
//
// Define a structure used for posting DPC requests to an ExWorkerThread.
//
typedef struct _FSP_CONTEXT {
WORK_QUEUE_ITEM Item;
PNPFS_DEVICE_OBJECT NpfsDeviceObject;
PIRP Irp;
} FSP_CONTEXT;
typedef FSP_CONTEXT *PFSP_CONTEXT;
NTSTATUS
NpFsdFileSystemControl (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine implements the FSD part of the NtFsControlFile API calls.
Arguments
NpfsDeviceObject - Supplies the device object to use.
Irp - Supplies the Irp being processed
Return Value:
NTSTATUS - The Fsd status for the Irp
--*/
{
NTSTATUS Status;
PAGED_CODE();
DebugTrace(+1, Dbg, "NpFsdFileSystemControl\n", 0);
//
// Call the common FsControl routine.
//
FsRtlEnterFileSystem();
try {
Status = NpCommonFileSystemControl( NpfsDeviceObject,
Irp );
} except(NpExceptionFilter( GetExceptionCode() )) {
//
// We had some trouble trying to perform the requested
// operation, so we'll abort the I/O request with
// the error status that we get back from the
// execption code
//
Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() );
}
NpReleaseVcb();
FsRtlExitFileSystem();
//
// And return to our caller
//
DebugTrace(-1, Dbg, "NpFsdFileSystemControl -> %08lx\n", Status );
return Status;
}
//
// Internal Support Routine
//
NTSTATUS
NpCommonFileSystemControl (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine does the common code for handling/dispatching an fsctl
function.
Arguments:
NpfsDeviceObject - Supplies the named pipe device object
Irp - Supplies the Irp being processed
Return Value:
NTSTATUS - The return status for the operation
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
BOOLEAN ReadOverflowOperation;
PAGED_CODE();
//
// Reference our input parameters to make things easier
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "NpCommonFileSystemControl\n", 0);
DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp);
DebugTrace( 0, Dbg, "OutputBufferLength = %08lx\n", IrpSp->Parameters.FileSystemControl.OutputBufferLength);
DebugTrace( 0, Dbg, "InputBufferLength = %08lx\n", IrpSp->Parameters.FileSystemControl.InputBufferLength);
DebugTrace( 0, Dbg, "FsControlCode = %08lx\n", IrpSp->Parameters.FileSystemControl.FsControlCode);
//
// Case on the type of function we're trying to do. In each case
// we'll call a local work routine to do the actual work.
//
ReadOverflowOperation = FALSE;
switch (IrpSp->Parameters.FileSystemControl.FsControlCode) {
case FSCTL_PIPE_ASSIGN_EVENT:
NpAcquireExclusiveVcb();
Status = NpAssignEvent( NpfsDeviceObject, Irp );
break;
case FSCTL_PIPE_DISCONNECT:
NpAcquireExclusiveVcb();
Status = NpDisconnect( NpfsDeviceObject, Irp );
break;
case FSCTL_PIPE_LISTEN:
NpAcquireSharedVcb();
Status = NpListen( NpfsDeviceObject, Irp );
break;
case FSCTL_PIPE_PEEK:
NpAcquireExclusiveVcb();
Status = NpPeek( NpfsDeviceObject, Irp );
break;
case FSCTL_PIPE_QUERY_EVENT:
NpAcquireExclusiveVcb();
Status = NpQueryEvent( NpfsDeviceObject, Irp );
break;
case FSCTL_PIPE_TRANSCEIVE:
NpAcquireSharedVcb();
Status = NpTransceive( NpfsDeviceObject, Irp );
break;
case FSCTL_PIPE_WAIT:
NpAcquireExclusiveVcb();
Status = NpWaitForNamedPipe( NpfsDeviceObject, Irp );
break;
case FSCTL_PIPE_IMPERSONATE:
NpAcquireExclusiveVcb();
Status = NpImpersonate( NpfsDeviceObject, Irp );
break;
case FSCTL_PIPE_INTERNAL_READ_OVFLOW:
ReadOverflowOperation = TRUE;
case FSCTL_PIPE_INTERNAL_READ:
NpAcquireSharedVcb();
Status = NpInternalRead( NpfsDeviceObject, Irp, ReadOverflowOperation );
break;
case FSCTL_PIPE_INTERNAL_WRITE:
NpAcquireSharedVcb();
Status = NpInternalWrite( NpfsDeviceObject, Irp );
break;
case FSCTL_PIPE_INTERNAL_TRANSCEIVE:
NpAcquireSharedVcb();
Status = NpInternalTransceive( NpfsDeviceObject, Irp );
break;
case FSCTL_PIPE_QUERY_CLIENT_PROCESS:
NpAcquireSharedVcb();
Status = NpQueryClientProcess( NpfsDeviceObject, Irp );
break;
case FSCTL_PIPE_SET_CLIENT_PROCESS:
NpAcquireExclusiveVcb();
Status = NpSetClientProcess( NpfsDeviceObject, Irp );
break;
default:
NpAcquireExclusiveVcb();
NpCompleteRequest( Irp, Status = STATUS_NOT_SUPPORTED );
break;
}
//
// And return to our caller
//
DebugTrace(-1, Dbg, "NpCommonFileSystemControl -> %08lx\n", Status);
return Status;
}
//
// Local support routine
//
NTSTATUS
NpAssignEvent (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine does the assign event control function
Arguments:
NpfsDeviceObject - Supplies our device object
Irp - Supplies the Irp specifying the function
Return Value:
NTSTATUS - An appropriate return status
--*/
{
PIO_STACK_LOCATION IrpSp;
ULONG InputBufferLength;
ULONG FsControlCode;
PCCB Ccb;
PNONPAGED_CCB NonpagedCcb;
NAMED_PIPE_END NamedPipeEnd;
PFILE_PIPE_ASSIGN_EVENT_BUFFER EventBuffer;
PAGED_CODE();
//
// Get the current stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "NpAssignEvent...\n", 0);
InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
//
// Decode the file object to figure out who we are. If the result
// is not a ccb then the pipe has been disconnected.
//
if (NpDecodeFileObject( IrpSp->FileObject,
NULL,
&Ccb,
&NamedPipeEnd ) != NPFS_NTC_CCB) {
DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0);
NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED );
return STATUS_PIPE_DISCONNECTED;
}
NonpagedCcb = Ccb->NonpagedCcb;
//
// Reference the system buffer as an assign event buffer and make
// sure it's large enough
//
if (InputBufferLength < sizeof(FILE_PIPE_ASSIGN_EVENT_BUFFER)) {
DebugTrace(0, Dbg, "System buffer size is too small\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
EventBuffer = Irp->AssociatedIrp.SystemBuffer;
//
// First thing we do is delete the old event if there is one
// for this end of the pipe
//
NpDeleteEventTableEntry( &NpVcb->EventTable,
NonpagedCcb->EventTableEntry[ NamedPipeEnd ] );
NonpagedCcb->EventTableEntry[ NamedPipeEnd ] = NULL;
//
// Now if the new event handle is not null then we'll add the new
// event to the event table
//
if (EventBuffer->EventHandle != NULL) {
NonpagedCcb->EventTableEntry[ NamedPipeEnd ] =
NpAddEventTableEntry( &NpVcb->EventTable,
Ccb,
NamedPipeEnd,
EventBuffer->EventHandle,
EventBuffer->KeyValue,
PsGetCurrentProcess(),
Irp->RequestorMode );
}
//
// Complete the Irp with success
//
NpCompleteRequest( Irp, STATUS_SUCCESS );
DebugTrace(-1, Dbg, "NpAssignEvent -> STATUS_SUCCESS\n", 0);
return STATUS_SUCCESS;
}
//
// Local Support Routine
//
NTSTATUS
NpDisconnect (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine does the disconnect control function
Arguments:
NpfsDeviceObject - Supplies our device object
Irp - Supplies the being processed
Return Value:
NTSTATUS - An apprropriate return status
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
ULONG FsControlCode;
PCCB Ccb;
NAMED_PIPE_END NamedPipeEnd;
PAGED_CODE();
//
// Get the current stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "NpDisconnect...\n", 0);
FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
//
// Decode the file object to figure out who we are. If the result
// is not a ccb then the pipe has been disconnected.
//
if (NpDecodeFileObject( IrpSp->FileObject,
NULL,
&Ccb,
&NamedPipeEnd ) != NPFS_NTC_CCB) {
DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0);
NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED );
return STATUS_PIPE_DISCONNECTED;
}
//
// Make sure that this is only the server that is doing this
// action.
//
if (NamedPipeEnd != FILE_PIPE_SERVER_END) {
DebugTrace(0, Dbg, "Not the server end\n", 0);
NpCompleteRequest( Irp, STATUS_ILLEGAL_FUNCTION );
return STATUS_ILLEGAL_FUNCTION;
}
NpAcquireExclusiveCcb(Ccb);
//
// Now call the state support routine to set the ccb to
// a disconnected state and remove the client's cached security
// context.
//
Status = NpSetDisconnectedPipeState( Ccb );
NpUninitializeSecurity( Ccb );
NpReleaseCcb(Ccb);
//
// Complete the Irp with our returned status
//
NpCompleteRequest( Irp, Status );
DebugTrace(-1, Dbg, "NpDisconnect -> %08lx\n", Status);
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NpListen (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine does the listen control function
Arguments:
NpfsDeviceObject - Supplies our device object
Irp - Supplies the being processed
Return Value:
NTSTATUS - An apprropriate return status
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
ULONG FsControlCode;
PCCB Ccb;
NAMED_PIPE_END NamedPipeEnd;
PAGED_CODE();
//
// Get the current stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "NpListen...\n", 0);
FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
//
// Decode the file object to figure out who we are. If the result
// is not a ccb then the pipe has been disconnected.
//
if (NpDecodeFileObject( IrpSp->FileObject,
NULL,
&Ccb,
&NamedPipeEnd ) != NPFS_NTC_CCB) {
DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0);
NpCompleteRequest( Irp, STATUS_ILLEGAL_FUNCTION );
DebugTrace(-1, Dbg, "NpListen -> STATUS_ILLEGAL_FUNCTION\n", 0 );
return STATUS_ILLEGAL_FUNCTION;
}
//
// Make sure that this is only the server that is doing this
// action.
//
if (NamedPipeEnd != FILE_PIPE_SERVER_END) {
DebugTrace(0, Dbg, "Not the server end\n", 0);
NpCompleteRequest( Irp, STATUS_ILLEGAL_FUNCTION );
DebugTrace(-1, Dbg, "NpListen -> STATUS_ILLEGAL_FUNCTION\n", 0 );
return STATUS_ILLEGAL_FUNCTION;
}
NpAcquireExclusiveCcb(Ccb);
//
// Now call the state support routine to set the ccb to
// a listening state. This routine will complete the Irp
// for us.
//
Status = NpSetListeningPipeState( Ccb, Irp );
NpReleaseCcb(Ccb);
DebugTrace(-1, Dbg, "NpListen -> %08lx\n", Status);
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NpPeek (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine does the peek control function
Arguments:
NpfsDeviceObject - Supplies our device object
Irp - Supplies the being processed
Return Value:
NTSTATUS - An apprropriate return status
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
ULONG OutputBufferLength;
ULONG FsControlCode;
NODE_TYPE_CODE NodeTypeCode;
PCCB Ccb;
PNONPAGED_CCB NonpagedCcb;
NAMED_PIPE_END NamedPipeEnd;
PFILE_PIPE_PEEK_BUFFER PeekBuffer;
PDATA_QUEUE ReadQueue;
READ_MODE ReadMode;
ULONG LengthWritten;
PDATA_ENTRY DataEntry;
PUCHAR ReadBuffer;
ULONG ReadLength;
ULONG ReadRemaining;
PAGED_CODE();
//
// Get the current Irp stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "NpPeek...\n", 0);
//
// Extract the important fields from the IrpSp
//
OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
DebugTrace( 0, Dbg, "OutputBufferLength = %08lx\n", OutputBufferLength);
DebugTrace( 0, Dbg, "FsControlCode = %08lx\n", FsControlCode);
//
// Decode the file object to figure out who we are. The results
// have a disconnected pipe if we get back an undefined ntc
//
if ((NodeTypeCode = NpDecodeFileObject( IrpSp->FileObject,
NULL,
&Ccb,
&NamedPipeEnd )) == NTC_UNDEFINED) {
DebugTrace(0, Dbg, "FileObject has been disconnected\n", 0);
NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED );
DebugTrace(-1, Dbg, "NpPeek -> STATUS_PIPE_DISCONNECTED\n", 0 );
return STATUS_PIPE_DISCONNECTED;
}
//
// Now make sure the node type code is for a ccb otherwise it is an
// invalid parameter
//
if (NodeTypeCode != NPFS_NTC_CCB) {
DebugTrace(0, Dbg, "FileObject is not for a ccb\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
DebugTrace(-1, Dbg, "NpPeek -> STATUS_INVALID_PARAMETER\n", 0 );
return STATUS_INVALID_PARAMETER;
}
NonpagedCcb = Ccb->NonpagedCcb;
//
// Reference the system buffer as a peek buffer and make sure it's
// large enough
//
if (OutputBufferLength < (ULONG)FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0])) {
DebugTrace(0, Dbg, "Output buffer is too small\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
DebugTrace(-1, Dbg, "NpPeek -> STATUS_INVALID_PARAMETER\n", 0 );
return STATUS_INVALID_PARAMETER;
}
PeekBuffer = Irp->AssociatedIrp.SystemBuffer;
//
// Now the data queue that we read from is based on the named pipe
// end. The server reads from the inbound queue and the client reads
// from the outbound queue
//
switch (NamedPipeEnd) {
case FILE_PIPE_SERVER_END:
ReadQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ];
//ReadMode = Ccb->ReadMode[ FILE_PIPE_SERVER_END ];
break;
case FILE_PIPE_CLIENT_END:
ReadQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ];
//ReadMode = Ccb->ReadMode[ FILE_PIPE_CLIENT_END ];
break;
default:
NpBugCheck( NamedPipeEnd, 0, 0 );
}
//
// Our read mode is really based upon the pipe type and not the set
// read mode for the pipe end.
//
if (Ccb->Fcb->Specific.Fcb.NamedPipeType == FILE_PIPE_MESSAGE_TYPE) {
ReadMode = FILE_PIPE_MESSAGE_MODE;
} else {
ReadMode = FILE_PIPE_BYTE_STREAM_MODE;
}
DebugTrace(0, Dbg, "ReadQueue = %08lx\n", ReadQueue);
DebugTrace(0, Dbg, "ReadMode = %08lx\n", ReadMode);
//
// If the state of the pipe is not in the connected or closing
// state then it is an invalid pipe state
//
if ((Ccb->NamedPipeState != FILE_PIPE_CONNECTED_STATE) &&
(Ccb->NamedPipeState != FILE_PIPE_CLOSING_STATE)) {
DebugTrace(0, Dbg, "pipe not connected or closing\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_PIPE_STATE );
return STATUS_INVALID_PIPE_STATE;
}
//
// If the state of the pipe is closing and the queue does
// not contain any writers then we return eof
//
if ((Ccb->NamedPipeState == FILE_PIPE_CLOSING_STATE) &&
(!NpIsDataQueueWriters( ReadQueue ))) {
DebugTrace(0, Dbg, "pipe closing and is empty\n", 0);
NpCompleteRequest( Irp, STATUS_PIPE_BROKEN );
return STATUS_PIPE_BROKEN;
}
//
// Zero out the standard header part of the peek buffer and
// set the length written to the amount we've just zeroed out
//
RtlZeroMemory( PeekBuffer, FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]) );
LengthWritten = FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]);
//
// Set the named pipe state
//
PeekBuffer->NamedPipeState = Ccb->NamedPipeState;
//
// There is only data available if the read queue contains
// write entries otherwise the rest of record is all zero.
//
if (NpIsDataQueueWriters( ReadQueue )) {
//
// Now find the first real entry in the read queue. The
// first entry actually better be a real one.
//
DataEntry = NpGetNextDataQueueEntry( ReadQueue, NULL );
ASSERT( (DataEntry->DataEntryType == Buffered) ||
(DataEntry->DataEntryType == Unbuffered) );
//
// Indicate how many bytes are available to read
//
PeekBuffer->ReadDataAvailable = ReadQueue->BytesInQueue;
//
// The number of messages a message length is only filled
// in for a message mode pipe
//
if (ReadMode == FILE_PIPE_MESSAGE_MODE) {
PeekBuffer->NumberOfMessages = ReadQueue->EntriesInQueue;
PeekBuffer->MessageLength = DataEntry->DataSize - ReadQueue->NextByteOffset;
}
//
// Now we are ready to copy over the data from the read queue
// into the peek buffer. First establish how much room we
// have in the peek buffer and who much is remaining.
//
ReadBuffer = &PeekBuffer->Data[0];
ReadLength = OutputBufferLength - FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]);
ReadRemaining = ReadLength;
DebugTrace(0, Dbg, "ReadBuffer = %08lx\n", ReadBuffer);
DebugTrace(0, Dbg, "ReadLength = %08lx\n", ReadLength);
//
// Now read the data queue.
//
if ( ReadLength != 0 ) {
IO_STATUS_BLOCK Iosb;
Iosb = NpReadDataQueue( ReadQueue,
TRUE,
FALSE,
ReadBuffer,
ReadLength,
ReadMode,
Ccb );
Status = Iosb.Status;
LengthWritten += Iosb.Information;
} else {
if ( PeekBuffer->ReadDataAvailable == 0) {
Status = STATUS_SUCCESS;
} else {
Status = STATUS_BUFFER_OVERFLOW;
}
}
} else {
Status = STATUS_SUCCESS;
}
//
// Complete the request. The amount of information copied
// is stored in length written
//
Irp->IoStatus.Information = LengthWritten;
NpCompleteRequest(Irp, Status);
DebugTrace(-1, Dbg, "NpPeek -> %08lx\n", Status);
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NpQueryEvent (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine does the query event control function
Arguments:
NpfsDeviceObject - Supplies our device object
Irp - Supplies the Irp specifying the function
Return Value:
NTSTATUS - An appropriate return status
--*/
{
PIO_STACK_LOCATION IrpSp;
ULONG InputBufferLength;
ULONG OutputBufferLength;
ULONG FsControlCode;
PCCB Ccb;
NAMED_PIPE_END NamedPipeEnd;
HANDLE EventHandle;
PFILE_PIPE_EVENT_BUFFER EventArray;
PFILE_PIPE_EVENT_BUFFER EventBuffer;
ULONG EventArrayMaximumCount;
ULONG EventCount;
PEPROCESS Process;
PEVENT_TABLE_ENTRY Ete;
PDATA_QUEUE ReadQueue;
PDATA_QUEUE WriteQueue;
PVOID RestartKey;
PAGED_CODE();
//
// Get the current stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "NpQueryEvent...\n", 0);
InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
//
// Decode the file object to figure out who we are. If the result
// is not a Vcb then its an invalid parameter
//
if (NpDecodeFileObject( IrpSp->FileObject,
NULL,
&Ccb,
&NamedPipeEnd ) != NPFS_NTC_VCB) {
DebugTrace(0, Dbg, "FileObject is not the named pipe driver\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
//
// Reference the system buffer as a handle and make sure it's large
// enough
//
if (InputBufferLength < sizeof(HANDLE)) {
DebugTrace(0, Dbg, "Input System buffer size is too small\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
EventHandle = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
//
// Reference the system buffer as an output event buffer, and compute
// how many event buffer records we can put in the buffer.
//
EventArray = Irp->AssociatedIrp.SystemBuffer;
EventArrayMaximumCount = OutputBufferLength / sizeof(FILE_PIPE_EVENT_BUFFER);
EventCount = 0;
//
// Get our current process pointer that we'll need for our search
//
Process = PsGetCurrentProcess();
//
// Now enumerate the event table entries in the event table
//
RestartKey = NULL;
for (Ete = NpGetNextEventTableEntry( &NpVcb->EventTable, &RestartKey);
Ete != NULL;
Ete = NpGetNextEventTableEntry( &NpVcb->EventTable, &RestartKey)) {
//
// Check if the event table entry matches the event handle
// and the process
//
if ((Ete->EventHandle == EventHandle) &&
(Ete->Process == Process)) {
//
// Now based on the named pipe end we treat the inbound/
// outbound as a read/write queue.
//
NpAcquireExclusiveCcb(Ete->Ccb);
switch (Ete->NamedPipeEnd) {
case FILE_PIPE_CLIENT_END:
ReadQueue = &Ete->Ccb->DataQueue[ FILE_PIPE_OUTBOUND ];
WriteQueue = &Ete->Ccb->DataQueue[ FILE_PIPE_INBOUND ];
break;
case FILE_PIPE_SERVER_END:
ReadQueue = &Ete->Ccb->DataQueue[ FILE_PIPE_INBOUND ];
WriteQueue = &Ete->Ccb->DataQueue[ FILE_PIPE_OUTBOUND ];
break;
default:
NpBugCheck( Ete->NamedPipeEnd, 0, 0 );
}
//
// Now if there is any data in the read queue to be read
// we fill in the buffer
//
if (NpIsDataQueueWriters(ReadQueue)) {
//
// First make sure there is enough room in the
// EventBuffer to hold another entry
//
if (EventCount >= EventArrayMaximumCount) {
DebugTrace(0, Dbg, "The event buffer is full\n", 0);
NpReleaseCcb(Ete->Ccb);
break;
}
//
// Reference the event buffer and increment the
// counter
//
EventBuffer = &EventArray[EventCount];
EventCount += 1;
//
// Fill in the event buffer entry
//
EventBuffer->NamedPipeState = Ete->Ccb->NamedPipeState;
EventBuffer->EntryType = FILE_PIPE_READ_DATA;
EventBuffer->ByteCount = ReadQueue->BytesInQueue;
EventBuffer->KeyValue = Ete->KeyValue;
EventBuffer->NumberRequests = ReadQueue->EntriesInQueue;
}
//
// We'll always fill in a write space buffer. The amount
// will either be bytes of write space available or
// the quota of write space that we can use.
//
//
// First make sure there is enough room in the
// EventBuffer to hold another entry
//
if (EventCount >= EventArrayMaximumCount) {
DebugTrace(0, Dbg, "The event buffer is full\n", 0);
NpReleaseCcb(Ete->Ccb);
break;
}
//
// Reference the event buffer and increment the
// counter
//
EventBuffer = &EventArray[EventCount];
EventCount += 1;
//
// Fill in the event buffer entry
//
EventBuffer->NamedPipeState = Ete->Ccb->NamedPipeState;
EventBuffer->EntryType = FILE_PIPE_WRITE_SPACE;
EventBuffer->KeyValue = Ete->KeyValue;
//
// Now either we put in the write space available or
// we put in the quota available
//
if (NpIsDataQueueReaders(WriteQueue)) {
EventBuffer->ByteCount = WriteQueue->BytesInQueue;
EventBuffer->NumberRequests = WriteQueue->EntriesInQueue;
} else {
EventBuffer->ByteCount = WriteQueue->Quota - WriteQueue->QuotaUsed;
EventBuffer->NumberRequests = 0;
}
NpReleaseCcb(Ete->Ccb);
}
}
//
// Set the information field to be the number of bytes of output
// data we've fill into the system buffer
//
Irp->IoStatus.Information = EventCount * sizeof(FILE_PIPE_EVENT_BUFFER);
//
// And complete the Irp with success
//
NpCompleteRequest( Irp, STATUS_SUCCESS );
DebugTrace(-1, Dbg, "NpQueryEvent -> STATUS_SUCCESS\n", 0);
return STATUS_SUCCESS;
}
//
// Local Support Routine
//
NTSTATUS
NpTransceive (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine does the transceive named pipe control function
Arguments:
NpfsDeviceObject - Supplies our device object
Irp - Supplies the being processed
Return Value:
NTSTATUS - An apprropriate return status
--*/
{
static IO_STATUS_BLOCK Iosb;
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
PETHREAD UserThread;
PUCHAR WriteBuffer;
ULONG WriteLength;
PUCHAR ReadBuffer;
ULONG ReadLength;
NODE_TYPE_CODE NodeTypeCode;
PCCB Ccb;
PNONPAGED_CCB NonpagedCcb;
NAMED_PIPE_END NamedPipeEnd;
PDATA_QUEUE ReadQueue;
PDATA_QUEUE WriteQueue;
PEVENT_TABLE_ENTRY Event;
READ_MODE ReadMode;
NAMED_PIPE_CONFIGURATION NamedPipeConfiguration;
ULONG WriteRemaining;
PIRP WriteIrp;
PDATA_ENTRY DataEntry;
//
// The following variable is used during abnormal unwind
//
PVOID UnwindStorage = NULL;
PAGED_CODE();
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "NpTransceive\n", 0);
DebugTrace( 0, Dbg, "NpfsDeviceObject = %08lx\n", NpfsDeviceObject);
DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp);
DebugTrace( 0, Dbg, "FileObject = %08lx\n", IrpSp->FileObject);
WriteLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
WriteBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
ReadLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
ReadBuffer = Irp->UserBuffer;
//
// Now if the requestor mode is user mode we need to probe the buffers
// We do now need to have an exception handler here because our top
// level caller already has one that will complete the Irp with
// the appropriate status if we access violate.
//
if (Irp->RequestorMode != KernelMode) {
try {
ProbeForRead( WriteBuffer, WriteLength, sizeof(UCHAR) );
ProbeForWrite( ReadBuffer, ReadLength, sizeof(UCHAR) );
} except(EXCEPTION_EXECUTE_HANDLER) {
ExRaiseStatus( STATUS_INVALID_USER_BUFFER );
}
}
//
// Get the Ccb and figure out who we are, and make sure we're not
// disconnected
//
if ((NodeTypeCode = NpDecodeFileObject( IrpSp->FileObject,
NULL,
&Ccb,
&NamedPipeEnd )) == NTC_UNDEFINED) {
DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0);
NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED );
DebugTrace(-1, Dbg, "NpTransceive -> STATUS_PIPE_DISCONNECTED\n", 0 );
return STATUS_PIPE_DISCONNECTED;
}
//
// Now we only will allow transceive operations on the pipe and not a
// directory or the device
//
if (NodeTypeCode != NPFS_NTC_CCB) {
DebugTrace(0, Dbg, "FileObject is not for a named pipe\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
DebugTrace(-1, Dbg, "NpTransceive -> STATUS_PIPE_DISCONNECTED\n", 0 );
return STATUS_PIPE_DISCONNECTED;
}
NonpagedCcb = Ccb->NonpagedCcb;
NpAcquireExclusiveCcb(Ccb);
WriteIrp = NULL;
try {
//
// Check that the pipe is in the connected state
//
if (Ccb->NamedPipeState != FILE_PIPE_CONNECTED_STATE) {
DebugTrace(0, Dbg, "Pipe not connected\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_PIPE_STATE );
try_return( Status = STATUS_INVALID_PIPE_STATE );
}
//
// Figure out the read/write queue, read mode, and event based
// on the end of the named pipe doing the transceive.
//
switch (NamedPipeEnd) {
case FILE_PIPE_SERVER_END:
ReadQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ];
WriteQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ];
Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ];
ReadMode = Ccb->ReadMode[ FILE_PIPE_SERVER_END ];
break;
case FILE_PIPE_CLIENT_END:
ReadQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ];
WriteQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ];
Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_SERVER_END ];
ReadMode = Ccb->ReadMode[ FILE_PIPE_CLIENT_END ];
break;
default:
NpBugCheck( NamedPipeEnd, 0, 0 );
}
//
// We only allow a transceive on a message mode, full duplex pipe.
//
NamedPipeConfiguration = Ccb->Fcb->Specific.Fcb.NamedPipeConfiguration;
if ((NamedPipeConfiguration != FILE_PIPE_FULL_DUPLEX) ||
(ReadMode != FILE_PIPE_MESSAGE_MODE)) {
DebugTrace(0, Dbg, "Bad pipe configuration or read mode\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_READ_MODE );
try_return( Status = STATUS_INVALID_READ_MODE );
}
//
// Check that the read queue is empty.
//
if (!NpIsDataQueueEmpty( ReadQueue )) {
DebugTrace(0, Dbg, "Read queue is not empty\n", 0);
NpCompleteRequest( Irp, STATUS_PIPE_BUSY );
try_return( Status = STATUS_PIPE_BUSY );
}
//
// Do the transceive write operation. We first try and push the data
// from the write buffer into any waiting readers in the write queue
// and if that succeeds then we can go on and do the read operation
// otherwise we need to make a copy of irp and to enqueue as
// a data entry into the write queue.
//
// Now we'll call our common write data queue routine to
// transfer data out of our write buffer into the data queue.
// If the result of the call is FALSE then we still have some
// write data to put into the write queue.
//
UserThread = Irp->Tail.Overlay.Thread;
if (!NpWriteDataQueue( WriteQueue,
ReadMode,
WriteBuffer,
WriteLength,
Ccb->Fcb->Specific.Fcb.NamedPipeType,
&WriteRemaining,
Ccb,
NamedPipeEnd,
UserThread )) {
PIO_STACK_LOCATION WriteIrpSp;
ASSERT( !NpIsDataQueueReaders( WriteQueue ));
DebugTrace(0, Dbg, "Add write to data queue\n", 0);
//
// We need to do some more write processing. So to handle
// this case we'll allocate a new irp and set its system
// buffer to be the remaining part of the write buffer
//
if ((WriteIrp = IoAllocateIrp( NpfsDeviceObject->DeviceObject.StackSize, FALSE )) == NULL) {
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
}
IoSetCompletionRoutine( WriteIrp, NpCompleteTransceiveIrp, NULL, TRUE, TRUE, TRUE );
WriteIrpSp = IoGetNextIrpStackLocation( WriteIrp );
if (WriteRemaining > 0) {
WriteIrp->AssociatedIrp.SystemBuffer = UnwindStorage = FsRtlAllocatePoolWithQuota( NonPagedPool,
WriteRemaining );
//
// Safely do the copy
//
try {
RtlCopyMemory( WriteIrp->AssociatedIrp.SystemBuffer,
&WriteBuffer[ WriteLength - WriteRemaining ],
WriteRemaining );
} except(EXCEPTION_EXECUTE_HANDLER) {
ExRaiseStatus( STATUS_INVALID_USER_BUFFER );
}
} else {
WriteIrp->AssociatedIrp.SystemBuffer = UnwindStorage = NULL;
}
//
// Set the current stack location, and set in the amount we are
// try to write.
//
WriteIrp->CurrentLocation -= 1;
WriteIrp->Tail.Overlay.CurrentStackLocation = WriteIrpSp;
WriteIrpSp->Parameters.Write.Length = WriteRemaining;
WriteIrp->IoStatus.Information = WriteRemaining;
//
// Set it up to do buffered I/O and deallocate the buffer
// on completion.
if (WriteRemaining > 0) {
WriteIrp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
}
WriteIrp->UserIosb = &Iosb;
//
// Add this write request to the write queue
//
DataEntry = NpAddDataQueueEntry( WriteQueue,
WriteEntries,
Unbuffered,
WriteRemaining,
WriteIrp,
NULL );
//
// And set the security part of the data entry
//
NpSetDataEntryClientContext( NamedPipeEnd,
Ccb,
DataEntry,
UserThread );
//
// Now null out the write irp variable so that we know not
// to deallocate it on an error
//
WriteIrp = NULL;
UnwindStorage = NULL;
}
//
// Now we need to advance the write queue to the next read irp to
// skip over flushes and closes
//
//****NpWriteDataQueue does this for us**** (VOID)NpGetNextRealDataQueueEntry( WriteQueue );
//
// And because we've done something we need to signal the
// other ends event
//
NpSignalEventTableEntry( Event );
//
// Do the transceive read operation. This is just like a
// buffered read.
//
// Now we know that the read queue is empty so we'll enqueue this
// Irp to the read queue and return status pending, also mark the
// irp pending
//
ASSERT( NpIsDataQueueEmpty( ReadQueue ));
(VOID)NpAddDataQueueEntry( ReadQueue,
ReadEntries,
Buffered,
ReadLength,
Irp,
NULL );
IoMarkIrpPending( Irp );
(VOID)NpGetNextRealDataQueueEntry( ReadQueue );
//
// And because we've done something we need to signal the
// other ends event
//
NpSignalEventTableEntry( Event );
Status = STATUS_PENDING;
try_exit: NOTHING;
} finally {
NpReleaseCcb(Ccb);
if (WriteIrp != NULL) { IoFreeIrp( WriteIrp ); }
if (UnwindStorage != NULL) { ExFreePool( UnwindStorage ); }
}
DebugTrace(-1, Dbg, "NpTransceive -> %08lx\n", Status);
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NpWaitForNamedPipe (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine does the wait for named pipe control function
Arguments:
NpfsDeviceObject - Supplies our device object
Irp - Supplies the being processed
Return Value:
NTSTATUS - An apprropriate return status
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
ULONG InputBufferLength;
ULONG FsControlCode;
PFCB Fcb;
PCCB Ccb;
PFILE_PIPE_WAIT_FOR_BUFFER WaitBuffer;
UNICODE_STRING Name;
PVOID LocalBuffer;
PLIST_ENTRY Links;
BOOLEAN CaseInsensitive = TRUE; //**** Make all searches case insensitive
PAGED_CODE();
//
// Get the current stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "NpWaitForNamedPipe...\n", 0);
//
// Extract the important fields from the IrpSp
//
InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
Name.Buffer = NULL;
LocalBuffer = NULL;
try {
//
// Decode the file object to figure out who we are. If the result
// is an error if the we weren't given a Vcb.
//
{
PCCB Ccb;
NAMED_PIPE_END NamedPipeEnd;
if (NpDecodeFileObject( IrpSp->FileObject,
NULL,
&Ccb,
&NamedPipeEnd ) != NPFS_NTC_ROOT_DCB) {
DebugTrace(0, Dbg, "File Object is not for the named pipe root directory\n", 0);
NpCompleteRequest( Irp, STATUS_ILLEGAL_FUNCTION );
try_return( Status = STATUS_ILLEGAL_FUNCTION );
}
}
//
// Reference the system buffer as a wait for buffer and make
// sure it's large enough
//
if (InputBufferLength < sizeof(FILE_PIPE_WAIT_FOR_BUFFER)) {
DebugTrace(0, Dbg, "System buffer size is too small\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
try_return( Status = STATUS_INVALID_PARAMETER );
}
WaitBuffer = Irp->AssociatedIrp.SystemBuffer;
//
// Check for an invalid buffer
//
if (FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name[0]) + WaitBuffer->NameLength >
InputBufferLength) {
DebugTrace(0, Dbg, "System buffer size is too small\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
try_return( Status = STATUS_INVALID_PARAMETER );
}
//
// Set up the local variable Name to be the name we're looking
// for
//
Name.Length = (USHORT)(WaitBuffer->NameLength + 2);
Name.Buffer = LocalBuffer = FsRtlAllocatePool( PagedPool, Name.Length );
Name.Buffer[0] = L'\\';
RtlCopyMemory( &Name.Buffer[1],
&WaitBuffer->Name[0],
WaitBuffer->NameLength );
//
// If the name is an alias, translate it.
//
Status = NpTranslateAlias( &Name );
if ( !NT_SUCCESS(Status) ) {
NpCompleteRequest( Irp, Status );
try_return( NOTHING );
}
//
// Now check to see if we can find a named pipe with the right
// name
//
Fcb = NpFindPrefix( &Name, CaseInsensitive, &Name );
//
// If the Fcb is null then we can't wait for it, Also if the
// Fcb is not an Fcb then we also have nothing to wait for
//
if (NodeType(Fcb) != NPFS_NTC_FCB) {
DebugTrace(0, Dbg, "Bad nonexistent named pipe name", 0);
NpCompleteRequest( Irp, STATUS_OBJECT_NAME_NOT_FOUND );
try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND );
}
//
// Now we need to search to see if we find a ccb already in the
// listening state
// First try and find a ccb that is in the listening state
// If we exit the loop with ccb null then we haven't found
// one
//
Ccb = NULL;
for (Links = Fcb->Specific.Fcb.CcbQueue.Flink;
Links != &Fcb->Specific.Fcb.CcbQueue;
Links = Links->Flink) {
Ccb = CONTAINING_RECORD( Links, CCB, CcbLinks );
if (Ccb->NamedPipeState == FILE_PIPE_LISTENING_STATE) {
break;
}
Ccb = NULL;
}
//
// Check if we found one
//
if (Ccb != NULL) {
DebugTrace(0, Dbg, "Found a ccb in listening state\n", 0);
NpCompleteRequest( Irp, STATUS_SUCCESS );
try_return( Status = STATUS_SUCCESS );
}
//
// We weren't able to find one so we need to add a new waiter
// Mark the irp pending and add this to the wait queue
//
IoMarkIrpPending( Irp );
try {
NpAddWaiter( &NpVcb->WaitQueue,
Fcb->Specific.Fcb.DefaultTimeOut,
Irp );
} finally {
//
// If we bomb out trying to add the waiter then we better
// not mark this irp as pending. We have to do it after the
// fact because once the irp is successfully in the wait queue
// it is out of our hands.
//
if (AbnormalTermination()) {
IoGetCurrentIrpStackLocation((Irp))->Control &= ~SL_PENDING_RETURNED;
}
}
Status = STATUS_PENDING;
try_exit: NOTHING;
} finally {
if (LocalBuffer != NULL) { ExFreePool( LocalBuffer ); }
}
DebugTrace(-1, Dbg, "NpWaitForNamedPipe -> %08lx\n", Status);
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NpImpersonate (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine does the impersonate of the named pipe
Arguments:
NpfsDeviceObject - Supplies our device object
Irp - Supplies the being processed
Return Value:
NTSTATUS - An apprropriate return status
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
PCCB Ccb;
NAMED_PIPE_END NamedPipeEnd;
UNREFERENCED_PARAMETER( NpfsDeviceObject );
PAGED_CODE();
//
// Get the current stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "NpImpersonate...\n", 0);
//
// Decode the file object to figure out who we are. If the result
// is an error if the we weren't given a Vcb.
//
if (NpDecodeFileObject( IrpSp->FileObject,
NULL,
&Ccb,
&NamedPipeEnd ) != NPFS_NTC_CCB) {
DebugTrace(0, Dbg, "File Object is not a named pipe\n", 0);
NpCompleteRequest( Irp, STATUS_ILLEGAL_FUNCTION );
DebugTrace(-1, Dbg, "NpImpersonate -> STATUS_ILLEGAL_FUNCTION\n", 0 );
return STATUS_ILLEGAL_FUNCTION;
}
//
// Make sure that we are the server end and not the client end
//
if (NamedPipeEnd != FILE_PIPE_SERVER_END) {
DebugTrace(0, Dbg, "Not the server end\n", 0);
NpCompleteRequest( Irp, STATUS_ILLEGAL_FUNCTION );
DebugTrace(-1, Dbg, "NpImpersonate -> STATUS_ILLEGAL_FUNCTION\n", 0 );
return STATUS_ILLEGAL_FUNCTION;
}
//
// set up the impersonation
//
Status = NpImpersonateClientContext( Ccb );
NpCompleteRequest( Irp, STATUS_SUCCESS );
DebugTrace(-1, Dbg, "NpImpersonate -> %08lx\n", Status);
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NpInternalRead (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp,
IN BOOLEAN ReadOverflowOperation
)
/*++
Routine Description:
This routine does the unbuffered read named pipe control function
Arguments:
NpfsDeviceObject - Supplies our device object
Irp - Supplies the being processed
ReadOverflowOperation - Used to indicate if the read being processed is a read overflow
operation.
Return Value:
NTSTATUS - An apprropriate return status
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
NODE_TYPE_CODE NodeTypeCode;
PCCB Ccb;
PNONPAGED_CCB NonpagedCcb;
NAMED_PIPE_END NamedPipeEnd;
NAMED_PIPE_CONFIGURATION NamedPipeConfiguration;
PIRP ReadIrp;
PUCHAR ReadBuffer;
ULONG ReadLength;
ULONG ReadRemaining;
READ_MODE ReadMode;
COMPLETION_MODE CompletionMode;
PDATA_QUEUE ReadQueue;
PEVENT_TABLE_ENTRY Event;
PAGED_CODE();
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "NpInternalRead\n", 0);
DebugTrace( 0, Dbg, "NpfsDeviceObject = %08lx\n", NpfsDeviceObject);
DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp);
DebugTrace( 0, Dbg, "FileObject = %08lx\n", IrpSp->FileObject);
//
// Get the Ccb and figure out who we are, and make sure we're not
// disconnected
//
if ((NodeTypeCode = NpDecodeFileObject( IrpSp->FileObject,
NULL,
&Ccb,
&NamedPipeEnd )) == NTC_UNDEFINED) {
DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0);
NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED );
DebugTrace(-1, Dbg, "NpInternalRead -> STATUS_PIPE_DISCONNECTED\n", 0 );
return STATUS_PIPE_DISCONNECTED;
}
//
// Now we only will allow Read operations on the pipe and not a directory
// or the device
//
if (NodeTypeCode != NPFS_NTC_CCB) {
DebugTrace(0, Dbg, "FileObject is not for a named pipe\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
DebugTrace(-1, Dbg, "NpInternalRead -> STATUS_INVALID_PARAMETER\n", 0 );
return STATUS_INVALID_PARAMETER;
}
NonpagedCcb = Ccb->NonpagedCcb;
NpAcquireExclusiveCcb(Ccb);
//
// Check if the pipe is not in the connected state.
//
switch (Ccb->NamedPipeState) {
case FILE_PIPE_DISCONNECTED_STATE:
DebugTrace(0, Dbg, "Pipe in disconnected state\n", 0);
NpReleaseCcb(Ccb);
NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED );
DebugTrace(-1, Dbg, "NpInternalRead -> STATUS_PIPE_DISCONNECTED\n", 0 );
return STATUS_PIPE_DISCONNECTED;
case FILE_PIPE_LISTENING_STATE:
DebugTrace(0, Dbg, "Pipe in listening state\n", 0);
NpReleaseCcb(Ccb);
NpCompleteRequest( Irp, STATUS_PIPE_LISTENING );
DebugTrace(-1, Dbg, "NpInternalRead -> STATUS_PIPE_LISTENING\n", 0 );
return STATUS_PIPE_LISTENING;
case FILE_PIPE_CONNECTED_STATE:
case FILE_PIPE_CLOSING_STATE:
break;
default:
DebugTrace(0, Dbg, "Illegal pipe state = %08lx\n", Ccb->NamedPipeState);
NpBugCheck( Ccb->NamedPipeState, 0, 0 );
}
//
// We only allow a read by the server on a non outbound only pipe
// and by the client on a non inbound only pipe
//
NamedPipeConfiguration = Ccb->Fcb->Specific.Fcb.NamedPipeConfiguration;
if (((NamedPipeEnd == FILE_PIPE_SERVER_END) &&
(NamedPipeConfiguration == FILE_PIPE_OUTBOUND))
||
((NamedPipeEnd == FILE_PIPE_CLIENT_END) &&
(NamedPipeConfiguration == FILE_PIPE_INBOUND))) {
DebugTrace(0, Dbg, "Trying to read to the wrong pipe configuration\n", 0);
NpReleaseCcb(Ccb);
NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
DebugTrace(-1, Dbg, "NpInternalRead -> STATUS_INVALID_PARAMETER\n", 0 );
return STATUS_INVALID_PARAMETER;
}
//
// Reference our input parameters to make things easier, and
// initialize our main variables that describe the Read command
//
ReadIrp = Irp;
ReadBuffer = Irp->AssociatedIrp.SystemBuffer;
ReadLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
ReadRemaining = ReadLength;
ReadMode = Ccb->ReadMode[ NamedPipeEnd ];
CompletionMode = Ccb->CompletionMode[ NamedPipeEnd ];
//
// Now the data queue that we read from into and the event that we signal
// are based on the named pipe end. The server read from the inbound
// queue and signals the client event. The client does just the
// opposite.
//
switch (NamedPipeEnd) {
case FILE_PIPE_SERVER_END:
ReadQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ];
Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ];
break;
case FILE_PIPE_CLIENT_END:
ReadQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ];
Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_SERVER_END ];
break;
default:
NpBugCheck( NamedPipeEnd, 0, 0 );
}
DebugTrace(0, Dbg, "ReadBuffer = %08lx\n", ReadBuffer);
DebugTrace(0, Dbg, "ReadLength = %08lx\n", ReadLength);
DebugTrace(0, Dbg, "ReadMode = %08lx\n", ReadMode);
DebugTrace(0, Dbg, "CompletionMode = %08lx\n", CompletionMode);
DebugTrace(0, Dbg, "ReadQueue = %08lx\n", ReadQueue);
DebugTrace(0, Dbg, "Event = %08lx\n", Event);
//
// if the read queue does not contain any write entries
// then we either need to enqueue this operation or
// fail immediately
//
if (!NpIsDataQueueWriters( ReadQueue )) {
//
// Check if the other end of the pipe is closing, and if
// so then we complete it with end of file.
// Otherwise check to see if we should enqueue the irp
// or complete the operation and tell the user the pipe is empty.
//
if (Ccb->NamedPipeState == FILE_PIPE_CLOSING_STATE) {
DebugTrace(0, Dbg, "Complete the irp with eof\n", 0);
NpCompleteRequest( ReadIrp, STATUS_PIPE_BROKEN );
Status = STATUS_PIPE_BROKEN;
} else if (CompletionMode == FILE_PIPE_QUEUE_OPERATION) {
DebugTrace(0, Dbg, "Put the irp into the read queue\n", 0);
(VOID)NpAddDataQueueEntry( ReadQueue,
ReadEntries,
Unbuffered,
ReadLength,
ReadIrp,
NULL );
IoMarkIrpPending( Irp );
Status = STATUS_PENDING;
} else {
DebugTrace(0, Dbg, "Complete the irp with pipe empty\n", 0);
NpCompleteRequest( ReadIrp, STATUS_PIPE_EMPTY );
Status = STATUS_PIPE_EMPTY;
}
} else {
//
// otherwise there we have a read irp against a read queue
// that contains one or more write entries.
//
ReadIrp->IoStatus = NpReadDataQueue( ReadQueue,
FALSE,
ReadOverflowOperation,
ReadBuffer,
ReadLength,
ReadMode,
Ccb );
Status = ReadIrp->IoStatus.Status;
//
// Now set the remaining byte count in the allocation size of
// the Irp.
//
ReadIrp->Overlay.AllocationSize.QuadPart = ReadQueue->BytesInQueue;
//
// Finish up the read irp.
//
NpCompleteRequest( ReadIrp, Status );
}
//
// Now we need to advance the read queue to the next write irp to
// skip over flushes and closes
//
(VOID)NpGetNextRealDataQueueEntry( ReadQueue );
//
// And because we've done something we need to signal the
// other ends event
//
NpSignalEventTableEntry( Event );
NpReleaseCcb(Ccb);
DebugTrace(-1, Dbg, "NpInternalRead -> %08lx\n", Status);
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NpInternalWrite (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine does the unbuffered write named pipe control function
Arguments:
NpfsDeviceObject - Supplies our device object
Irp - Supplies the being processed
Return Value:
NTSTATUS - An apprropriate return status
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
PETHREAD UserThread;
NODE_TYPE_CODE NodeTypeCode;
PCCB Ccb;
PNONPAGED_CCB NonpagedCcb;
NAMED_PIPE_END NamedPipeEnd;
NAMED_PIPE_CONFIGURATION NamedPipeConfiguration;
PIRP WriteIrp;
PUCHAR WriteBuffer;
ULONG WriteLength;
ULONG WriteRemaining;
PDATA_QUEUE WriteQueue;
PEVENT_TABLE_ENTRY Event;
READ_MODE ReadMode;
PDATA_ENTRY DataEntry;
PAGED_CODE();
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "NpInternalWrite\n", 0);
DebugTrace( 0, Dbg, "NpfsDeviceObject = %08lx\n", NpfsDeviceObject);
DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp);
DebugTrace( 0, Dbg, "FileObject = %08lx\n", IrpSp->FileObject);
//
// Get the Ccb and figure out who we are, and make sure we're not
// disconnected
//
if ((NodeTypeCode = NpDecodeFileObject( IrpSp->FileObject,
NULL,
&Ccb,
&NamedPipeEnd )) == NTC_UNDEFINED) {
DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0);
NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED );
DebugTrace(-1, Dbg, "NpInternalWrite -> STATUS_PIPE_DISCONNECTED\n", 0 );
return STATUS_PIPE_DISCONNECTED;
}
//
// Now we only will allow write operations on the pipe and not a directory
// or the device
//
if (NodeTypeCode != NPFS_NTC_CCB) {
DebugTrace(0, Dbg, "FileObject is not for a named pipe\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
DebugTrace(-1, Dbg, "NpInternalWrite -> STATUS_PIPE_DISCONNECTED\n", 0);
return STATUS_PIPE_DISCONNECTED;
}
NonpagedCcb = Ccb->NonpagedCcb;
NpAcquireExclusiveCcb(Ccb);
//
// We only allow a write by the server on a non inbound only pipe
// and by the client on a non outbound only pipe
//
NamedPipeConfiguration = Ccb->Fcb->Specific.Fcb.NamedPipeConfiguration;
if (((NamedPipeEnd == FILE_PIPE_SERVER_END) &&
(NamedPipeConfiguration == FILE_PIPE_INBOUND))
||
((NamedPipeEnd == FILE_PIPE_CLIENT_END) &&
(NamedPipeConfiguration == FILE_PIPE_OUTBOUND))) {
DebugTrace(0, Dbg, "Trying to write to the wrong pipe configuration\n", 0);
NpReleaseCcb(Ccb);
NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
DebugTrace(-1, Dbg, "NpInternalWrite -> STATUS_PIPE_DISCONNECTED\n", 0);
return STATUS_PIPE_DISCONNECTED;
}
//
// Reference our input parameters to make things easier, and
// initialize our main variables that describe the write command
//
WriteIrp = Irp;
WriteBuffer = Irp->AssociatedIrp.SystemBuffer;
WriteLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
//
// Set up the amount of data we will have written by the time this
// irp gets completed
//
WriteIrp->IoStatus.Information = WriteLength;
//
// Now the data queue that we write into and the event that we signal
// are based on the named pipe end. The server writes to the outbound
// queue and signals the client event. The client does just the
// opposite. We also need to figure out the read mode for the opposite
// end of the pipe.
//
switch (NamedPipeEnd) {
case FILE_PIPE_SERVER_END:
WriteQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ];
Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ];
ReadMode = Ccb->ReadMode[ FILE_PIPE_CLIENT_END ];
break;
case FILE_PIPE_CLIENT_END:
WriteQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ];
Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_SERVER_END ];
ReadMode = Ccb->ReadMode[ FILE_PIPE_SERVER_END ];
break;
default:
NpBugCheck( NamedPipeEnd, 0, 0 );
}
//
// Check if the pipe is not in the connected state.
//
switch (Ccb->NamedPipeState) {
case FILE_PIPE_DISCONNECTED_STATE:
DebugTrace(0, Dbg, "Pipe in disconnected state\n", 0);
NpReleaseCcb(Ccb);
NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED );
return STATUS_PIPE_DISCONNECTED;
case FILE_PIPE_LISTENING_STATE:
DebugTrace(0, Dbg, "Pipe in listening state\n", 0);
NpReleaseCcb(Ccb);
NpCompleteRequest( Irp, STATUS_PIPE_LISTENING );
return STATUS_PIPE_LISTENING;
case FILE_PIPE_CONNECTED_STATE:
break;
case FILE_PIPE_CLOSING_STATE:
DebugTrace(0, Dbg, "Pipe in closing state\n", 0);
NpReleaseCcb(Ccb);
NpCompleteRequest( Irp, STATUS_PIPE_CLOSING );
return STATUS_PIPE_CLOSING;
default:
DebugTrace(0, Dbg, "Illegal pipe state = %08lx\n", Ccb->NamedPipeState);
NpBugCheck( Ccb->NamedPipeState, 0, 0 );
}
//
// Check if this is a message type pipe and the operation type is complete
// operation, If so then we also check that the queued reads is enough to
// complete the message otherwise we need to abort the write irp immediately.
//
if ((Ccb->Fcb->Specific.Fcb.NamedPipeType == FILE_PIPE_MESSAGE_TYPE) &&
(Ccb->CompletionMode[NamedPipeEnd] == FILE_PIPE_COMPLETE_OPERATION)) {
//
// If the pipe contains readers and amount to read is less than the write
// length then we cannot do it the write.
// Or if pipe does not contain reads then we also cannot do the write.
//
if ((NpIsDataQueueReaders( WriteQueue ) &&
(WriteQueue->BytesInQueue < (WriteLength + sizeof(DATA_ENTRY))))
||
(!NpIsDataQueueReaders( WriteQueue ))) {
DebugTrace(0, Dbg, "Cannot complete the message without blocking\n", 0);
NpReleaseCcb(Ccb);
Irp->IoStatus.Information = 0;
NpCompleteRequest( Irp, STATUS_SUCCESS );
return STATUS_SUCCESS;
}
}
//
// Now we'll call our common write data queue routine to
// transfer data out of our write buffer into the data queue.
// If the result of the call is FALSE then we still have some
// write data to put into the write queue.
//
UserThread = Irp->Tail.Overlay.Thread;
if (!NpWriteDataQueue( WriteQueue,
ReadMode,
WriteBuffer,
WriteLength,
Ccb->Fcb->Specific.Fcb.NamedPipeType,
&WriteRemaining,
Ccb,
NamedPipeEnd,
UserThread )) {
ASSERT( !NpIsDataQueueReaders( WriteQueue ));
ASSERT((Ccb->Fcb->Specific.Fcb.NamedPipeType == FILE_PIPE_BYTE_STREAM_TYPE) ||
(Ccb->CompletionMode[NamedPipeEnd] == FILE_PIPE_QUEUE_OPERATION) ||
(WriteRemaining <= (WriteQueue->Quota - WriteQueue->QuotaUsed)));
//
// Check if the operation is not to block and if so then we
// will complete the operation now with what we're written, if what is
// left will not fit in the quota for the file
//
if (Ccb->CompletionMode[NamedPipeEnd] == FILE_PIPE_COMPLETE_OPERATION) {
DebugTrace(0, Dbg, "Complete the byte stream write immediately\n", 0);
Irp->IoStatus.Information = WriteLength - WriteRemaining;
NpCompleteRequest( Irp, Status = STATUS_SUCCESS );
} else {
DebugTrace(0, Dbg, "Add write to data queue\n", 0);
IoMarkIrpPending( Irp );
//
// Add this write request to the write queue
//
try {
DataEntry = NpAddDataQueueEntry( WriteQueue,
WriteEntries,
Unbuffered,
WriteLength,
Irp,
NULL );
//
// And set the security part of the data entry
//
NpSetDataEntryClientContext( NamedPipeEnd,
Ccb,
DataEntry,
UserThread );
} finally {
if (AbnormalTermination()) {
IoGetCurrentIrpStackLocation((Irp))->Control &= ~SL_PENDING_RETURNED;
}
}
//
// Now if the remaining length is not equal to the original
// write length then this must have been the first write entry
// into the data queue and we need to set the Next Byte
// field
//
if (WriteLength > WriteRemaining) {
WriteQueue->NextByteOffset = WriteLength - WriteRemaining;
}
//
// Set our status for the write irp to pending
//
Status = STATUS_PENDING;
}
} else {
DebugTrace(0, Dbg, "Complete the Write Irp\n", 0);
//
// The write irp is finished so we can complete it now
//
NpCompleteRequest( WriteIrp, STATUS_SUCCESS );
//
// Set our status for the write irp to success
//
Status = STATUS_SUCCESS;
}
//
// Now we need to advance the write queue to the next read irp to
// skip over flushes and closes
//
//****NpWriteDataQueue does this for us**** (VOID)NpGetNextRealDataQueueEntry( WriteQueue );
//
// And because we've done something we need to signal the
// other ends event
//
NpSignalEventTableEntry( Event );
NpReleaseCcb(Ccb);
DebugTrace(-1, Dbg, "NpInternalWrite -> %08lx\n", Status);
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NpInternalTransceive (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine does the internal (i.e., unbuffered) transceive named pipe
control function
Arguments:
NpfsDeviceObject - Supplies our device object
Irp - Supplies the being processed
Return Value:
NTSTATUS - An apprropriate return status
--*/
{
static IO_STATUS_BLOCK Iosb;
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
PETHREAD UserThread;
PUCHAR WriteBuffer;
ULONG WriteLength;
PUCHAR ReadBuffer;
ULONG ReadLength;
NODE_TYPE_CODE NodeTypeCode;
PCCB Ccb;
PNONPAGED_CCB NonpagedCcb;
NAMED_PIPE_END NamedPipeEnd;
PDATA_QUEUE ReadQueue;
PDATA_QUEUE WriteQueue;
PEVENT_TABLE_ENTRY Event;
READ_MODE ReadMode;
NAMED_PIPE_CONFIGURATION NamedPipeConfiguration;
ULONG WriteRemaining;
PIRP WriteIrp;
PDATA_ENTRY DataEntry;
//
// The following variable is used for abnormal unwind
//
PVOID UnwindStorage = NULL;
PAGED_CODE();
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "NpInternalTransceive\n", 0);
DebugTrace( 0, Dbg, "NpfsDeviceObject = %08lx\n", NpfsDeviceObject);
DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp);
DebugTrace( 0, Dbg, "FileObject = %08lx\n", IrpSp->FileObject);
WriteLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
WriteBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
ReadLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
ReadBuffer = Irp->AssociatedIrp.SystemBuffer;
//
// Get the Ccb and figure out who we are, and make sure we're not
// disconnected
//
if ((NodeTypeCode = NpDecodeFileObject( IrpSp->FileObject,
NULL,
&Ccb,
&NamedPipeEnd )) == NTC_UNDEFINED) {
DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0);
NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED );
DebugTrace(-1, Dbg, "NpInternalTransceive -> STATUS_PIPE_DISCONNECTED\n", 0 );
return STATUS_PIPE_DISCONNECTED;
}
//
// Now we only will allow transceive operations on the pipe and not a
// directory or the device
//
if (NodeTypeCode != NPFS_NTC_CCB) {
DebugTrace(0, Dbg, "FileObject is not for a named pipe\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
DebugTrace(-1, Dbg, "NpInternalTransceive -> STATUS_INVALID_PARAMETER\n", 0 );
return STATUS_INVALID_PARAMETER;
}
NonpagedCcb = Ccb->NonpagedCcb;
WriteIrp = NULL;
NpAcquireExclusiveCcb(Ccb);
try {
//
// Check that the pipe is in the connected state
//
if (Ccb->NamedPipeState != FILE_PIPE_CONNECTED_STATE) {
DebugTrace(0, Dbg, "Pipe not connected\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_PIPE_STATE );
try_return( Status = STATUS_INVALID_PIPE_STATE );
}
//
// Figure out the read/write queue, read mode, and event based
// on the end of the named pipe doing the transceive.
//
switch (NamedPipeEnd) {
case FILE_PIPE_SERVER_END:
ReadQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ];
WriteQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ];
Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ];
ReadMode = Ccb->ReadMode[ FILE_PIPE_SERVER_END ];
break;
case FILE_PIPE_CLIENT_END:
ReadQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ];
WriteQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ];
Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_SERVER_END ];
ReadMode = Ccb->ReadMode[ FILE_PIPE_CLIENT_END ];
break;
default:
NpBugCheck( NamedPipeEnd, 0, 0 );
}
//
// We only allow a transceive on a message mode, full duplex pipe.
//
NamedPipeConfiguration = Ccb->Fcb->Specific.Fcb.NamedPipeConfiguration;
if ((NamedPipeConfiguration != FILE_PIPE_FULL_DUPLEX) ||
(ReadMode != FILE_PIPE_MESSAGE_MODE)) {
DebugTrace(0, Dbg, "Bad pipe configuration or read mode\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_READ_MODE );
try_return( Status = STATUS_INVALID_READ_MODE );
}
//
// Check that the read queue is empty.
//
if (!NpIsDataQueueEmpty( ReadQueue )) {
DebugTrace(0, Dbg, "Read queue is not empty\n", 0);
NpCompleteRequest( Irp, STATUS_PIPE_BUSY );
try_return( Status = STATUS_PIPE_BUSY );
}
//
// Do the transceive write operation. We first try and push the data
// from the write buffer into any waiting readers in the write queue
// and if that succeeds then we can go on and do the read operation
// otherwise we need to make a copy of irp and to enqueue as
// a data entry into the write queue.
//
// Now we'll call our common write data queue routine to
// transfer data out of our write buffer into the data queue.
// If the result of the call is FALSE then we still have some
// write data to put into the write queue.
//
UserThread = Irp->Tail.Overlay.Thread;
if (!NpWriteDataQueue( WriteQueue,
ReadMode,
WriteBuffer,
WriteLength,
Ccb->Fcb->Specific.Fcb.NamedPipeType,
&WriteRemaining,
Ccb,
NamedPipeEnd,
UserThread )) {
PIO_STACK_LOCATION WriteIrpSp;
ASSERT( !NpIsDataQueueReaders( WriteQueue ));
DebugTrace(0, Dbg, "Add write to data queue\n", 0);
//
// We need to do some more write processing. So to handle
// this case we'll allocate a new irp and set its system
// buffer to be the remaining part of the write buffer
//
if ((WriteIrp = IoAllocateIrp( NpfsDeviceObject->DeviceObject.StackSize, FALSE )) == NULL) {
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
}
IoSetCompletionRoutine( WriteIrp, NpCompleteTransceiveIrp, NULL, TRUE, TRUE, TRUE );
WriteIrpSp = IoGetNextIrpStackLocation( WriteIrp );
if (WriteRemaining > 0) {
WriteIrp->AssociatedIrp.SystemBuffer = UnwindStorage = FsRtlAllocatePool( NonPagedPool,
WriteRemaining );
//
// Safely do the copy
//
try {
RtlCopyMemory( WriteIrp->AssociatedIrp.SystemBuffer,
&WriteBuffer[ WriteLength - WriteRemaining ],
WriteRemaining );
} except(EXCEPTION_EXECUTE_HANDLER) {
ExRaiseStatus( STATUS_INVALID_USER_BUFFER );
}
} else {
WriteIrp->AssociatedIrp.SystemBuffer = UnwindStorage = NULL;
}
//
// Set the current stack location
//
WriteIrp->CurrentLocation -= 1;
WriteIrp->Tail.Overlay.CurrentStackLocation = WriteIrpSp;
//
// Set it up to do buffered I/O and deallocate the buffer
// on completion.
if (WriteRemaining > 0) {
WriteIrp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
}
WriteIrp->UserIosb = &Iosb;
//
// Add this write request to the write queue
//
DataEntry = NpAddDataQueueEntry( WriteQueue,
WriteEntries,
Unbuffered,
WriteRemaining,
WriteIrp,
NULL );
//
// And set the security part of the data entry
//
NpSetDataEntryClientContext( NamedPipeEnd,
Ccb,
DataEntry,
UserThread );
//
// Now null out the write irp variable so that we know not
// to deallocate it on an error
//
WriteIrp = NULL;
UnwindStorage = NULL;
}
//
// Now we need to advance the write queue to the next read irp to
// skip over flushes and closes
//
//****NpWriteDataQueue does this for us**** (VOID)NpGetNextRealDataQueueEntry( WriteQueue );
//
// And because we've done something we need to signal the
// other ends event
//
NpSignalEventTableEntry( Event );
//
// Do the transceive read operation. This is just like an
// unbuffered read.
//
// Now we know that the read queue is empty so we'll enqueue this
// Irp to the read queue and return status pending, also mark the
// irp pending
//
ASSERT( NpIsDataQueueEmpty( ReadQueue ));
(VOID)NpAddDataQueueEntry( ReadQueue,
ReadEntries,
Unbuffered,
ReadLength,
Irp,
NULL );
IoMarkIrpPending( Irp );
(VOID)NpGetNextRealDataQueueEntry( ReadQueue );
//
// And because we've done something we need to signal the
// other ends event
//
NpSignalEventTableEntry( Event );
Status = STATUS_PENDING;
try_exit: NOTHING;
} finally {
NpReleaseCcb(Ccb);
if (WriteIrp != NULL) { IoFreeIrp( WriteIrp ); }
if (UnwindStorage != NULL) { ExFreePool( UnwindStorage ); }
}
DebugTrace(-1, Dbg, "NpInternalTransceive -> %08lx\n", Status);
return Status;
}
//
// Internal support routine
//
NTSTATUS
NpQueryClientProcess (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine does the query client process named pipe control function
Arguments:
NpfsDeviceObject - Supplies our device object
Irp - Supplies the being processed
Return Value:
NTSTATUS - An apprropriate return status
--*/
{
PIO_STACK_LOCATION IrpSp;
ULONG OutputBufferLength;
PCCB Ccb;
PFILE_PIPE_CLIENT_PROCESS_BUFFER ClientProcessBuffer;
PAGED_CODE();
//
// Get the current stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "NpQueryClientProcess\n", 0);
OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
//
// Decode the file object to figure out who we are.
//
if (NpDecodeFileObject( IrpSp->FileObject, NULL, &Ccb, NULL ) != NPFS_NTC_CCB) {
DebugTrace(0, Dbg, "Pipe is disconnected\n", 0);
NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED );
return STATUS_PIPE_DISCONNECTED;
}
//
// Make sure the output buffer is large enough
//
if (OutputBufferLength < sizeof(FILE_PIPE_CLIENT_PROCESS_BUFFER)) {
DebugTrace(0, Dbg, "Output System buffer size is too small\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
NpAcquireExclusiveCcb(Ccb);
//
// Copy over the client process ID
//
ClientProcessBuffer = Irp->AssociatedIrp.SystemBuffer;
ClientProcessBuffer->ClientSession = Ccb->ClientSession;
ClientProcessBuffer->ClientProcess = Ccb->ClientProcess;
//
// Set the information field to the size of the client process
// buffer
//
Irp->IoStatus.Information = sizeof(FILE_PIPE_CLIENT_PROCESS_BUFFER);
NpReleaseCcb(Ccb);
NpCompleteRequest( Irp, STATUS_SUCCESS );
DebugTrace(-1, Dbg, "NpQueryClientProcess -> STATUS_SUCCESS\n", 0);
return STATUS_SUCCESS;
}
//
// Internal support routine
//
NTSTATUS
NpSetClientProcess (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine does the set client process named pipe control function
Arguments:
NpfsDeviceObject - Supplies our device object
Irp - Supplies the being processed
Return Value:
NTSTATUS - An apprropriate return status
--*/
{
PIO_STACK_LOCATION IrpSp;
ULONG InputBufferLength;
PCCB Ccb;
PFILE_PIPE_CLIENT_PROCESS_BUFFER ClientProcessBuffer;
PAGED_CODE();
//
// Get the current stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "NpSetClientProcess\n", 0);
InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
//
// Decode the file object to figure out who we are.
//
if (NpDecodeFileObject( IrpSp->FileObject, NULL, &Ccb, NULL ) != NPFS_NTC_CCB) {
DebugTrace(0, Dbg, "Pipe is disconnected\n", 0);
NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED );
return STATUS_PIPE_DISCONNECTED;
}
//
// Make sure the input buffer is large enough
//
if (InputBufferLength < sizeof(FILE_PIPE_CLIENT_PROCESS_BUFFER)) {
DebugTrace(0, Dbg, "Input System buffer size is too small\n", 0);
NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
//
// Copy over the client process ID
//
ClientProcessBuffer = Irp->AssociatedIrp.SystemBuffer;
Ccb->ClientSession = ClientProcessBuffer->ClientSession;
Ccb->ClientProcess = ClientProcessBuffer->ClientProcess;
NpCompleteRequest( Irp, STATUS_SUCCESS );
DebugTrace(-1, Dbg, "NpSetClientProcess -> STATUS_SUCCESS\n", 0);
return STATUS_SUCCESS;
}
//
// Internal support routine
//
NTSTATUS
NpCompleteTransceiveIrp (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This is a local i/o completion routine used to complete the special
Irps allocated for transcieve. This routine simply deallocate the
irp and return status more processing
Arguments:
DeviceObject - Supplies the device object
Irp - Supplies the Irp to complete
Context - Supplies the context for the Irp
Return Value:
NTSTATUS - STATUS_MORE_PROCESSING_REQUIRED
--*/
{
UNREFERENCED_PARAMETER( DeviceObject );
UNREFERENCED_PARAMETER( Context );
PAGED_CODE();
if (Irp->AssociatedIrp.SystemBuffer != NULL) {
ExFreePool( Irp->AssociatedIrp.SystemBuffer );
}
IoFreeIrp( Irp );
return STATUS_MORE_PROCESSING_REQUIRED;
}