3111 lines
81 KiB
C
3111 lines
81 KiB
C
/*++
|
||
|
||
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)
|
||
|
||
|
||
|
||
#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
|
||
|
||
|
||
|
||
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();
|
||
|
||
Status = NpCommonFileSystemControl( NpfsDeviceObject,
|
||
Irp );
|
||
FsRtlExitFileSystem();
|
||
|
||
if (Status != STATUS_PENDING) {
|
||
NpCompleteRequest (Irp, Status);
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
LIST_ENTRY DeferredList;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Reference our input parameters to make things easier
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation (Irp);
|
||
|
||
InitializeListHead (&DeferredList);
|
||
|
||
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, &DeferredList);
|
||
break;
|
||
|
||
case FSCTL_PIPE_LISTEN:
|
||
|
||
NpAcquireSharedVcb ();
|
||
Status = NpListen (NpfsDeviceObject, Irp, &DeferredList);
|
||
break;
|
||
|
||
case FSCTL_PIPE_PEEK:
|
||
|
||
NpAcquireExclusiveVcb ();
|
||
Status = NpPeek (NpfsDeviceObject, Irp, &DeferredList);
|
||
break;
|
||
|
||
case FSCTL_PIPE_QUERY_EVENT:
|
||
|
||
NpAcquireExclusiveVcb ();
|
||
Status = NpQueryEvent (NpfsDeviceObject, Irp);
|
||
break;
|
||
|
||
case FSCTL_PIPE_TRANSCEIVE:
|
||
|
||
NpAcquireSharedVcb ();
|
||
Status = NpTransceive (NpfsDeviceObject, Irp, &DeferredList);
|
||
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, &DeferredList);
|
||
break;
|
||
|
||
case FSCTL_PIPE_INTERNAL_WRITE:
|
||
|
||
NpAcquireSharedVcb ();
|
||
Status = NpInternalWrite (NpfsDeviceObject, Irp, &DeferredList);
|
||
break;
|
||
|
||
case FSCTL_PIPE_INTERNAL_TRANSCEIVE:
|
||
|
||
NpAcquireSharedVcb ();
|
||
Status = NpInternalTransceive (NpfsDeviceObject, Irp, &DeferredList);
|
||
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:
|
||
|
||
return STATUS_NOT_SUPPORTED; // No lock acquired
|
||
}
|
||
|
||
NpReleaseVcb ();
|
||
|
||
//
|
||
// Complete any deferred IRPs now we have dropped the last lock
|
||
//
|
||
NpCompleteDeferredIrps (&DeferredList);
|
||
|
||
|
||
//
|
||
// 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;
|
||
NTSTATUS status;
|
||
|
||
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);
|
||
|
||
return STATUS_PIPE_DISCONNECTED;
|
||
}
|
||
|
||
NonpagedCcb = Ccb->NonpagedCcb;
|
||
|
||
//
|
||
// Reference the system buffer as an assign event buffer and make
|
||
// sure it's large enough
|
||
//
|
||
|
||
EventBuffer = Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
|
||
if (InputBufferLength < sizeof(FILE_PIPE_ASSIGN_EVENT_BUFFER)) {
|
||
|
||
DebugTrace(0, Dbg, "System buffer size is too small\n", 0);
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
|
||
//
|
||
// 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
|
||
//
|
||
|
||
status = STATUS_SUCCESS;
|
||
if (EventBuffer->EventHandle != NULL) {
|
||
|
||
status = NpAddEventTableEntry( &NpVcb->EventTable,
|
||
Ccb,
|
||
NamedPipeEnd,
|
||
EventBuffer->EventHandle,
|
||
EventBuffer->KeyValue,
|
||
PsGetCurrentProcess(),
|
||
Irp->RequestorMode,
|
||
&NonpagedCcb->EventTableEntry[ NamedPipeEnd ] );
|
||
}
|
||
|
||
|
||
DebugTrace(-1, Dbg, "NpAssignEvent -> STATUS_SUCCESS\n", 0);
|
||
return status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
NpDisconnect (
|
||
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
|
||
IN PIRP Irp,
|
||
IN PLIST_ENTRY DeferredList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does the disconnect control function
|
||
|
||
Arguments:
|
||
|
||
NpfsDeviceObject - Supplies our device object
|
||
|
||
Irp - Supplies the being processed
|
||
|
||
DeferredList - List of IRPs to complete after we drop the locks
|
||
|
||
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);
|
||
|
||
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);
|
||
|
||
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, DeferredList );
|
||
|
||
NpUninitializeSecurity( Ccb );
|
||
|
||
NpReleaseCcb(Ccb);
|
||
|
||
DebugTrace(-1, Dbg, "NpDisconnect -> %08lx\n", Status);
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
NpListen (
|
||
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
|
||
IN PIRP Irp,
|
||
IN PLIST_ENTRY DeferredList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does the listen control function
|
||
|
||
Arguments:
|
||
|
||
NpfsDeviceObject - Supplies our device object
|
||
|
||
Irp - Supplies the being processed
|
||
|
||
DeferredList - List of IRPs to complete once we drop the locks
|
||
|
||
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);
|
||
|
||
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);
|
||
|
||
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, DeferredList );
|
||
|
||
NpReleaseCcb(Ccb);
|
||
|
||
DebugTrace(-1, Dbg, "NpListen -> %08lx\n", Status);
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
NpPeek (
|
||
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
|
||
IN PIRP Irp,
|
||
IN PLIST_ENTRY DeferredList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does the peek control function
|
||
|
||
Arguments:
|
||
|
||
NpfsDeviceObject - Supplies our device object
|
||
|
||
Irp - Supplies the being processed
|
||
|
||
DeferredList - List of IRPS to be completed after we drop the locks
|
||
|
||
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;
|
||
|
||
PUCHAR ReadBuffer;
|
||
ULONG ReadLength;
|
||
ULONG ReadRemaining;
|
||
PDATA_ENTRY DataEntry;
|
||
|
||
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);
|
||
|
||
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);
|
||
|
||
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);
|
||
|
||
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);
|
||
|
||
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);
|
||
|
||
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 - ReadQueue->NextByteOffset;
|
||
|
||
//
|
||
// The number of messages and 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,
|
||
DeferredList );
|
||
|
||
Status = Iosb.Status;
|
||
LengthWritten += (ULONG)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;
|
||
|
||
|
||
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);
|
||
|
||
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);
|
||
|
||
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 - ReadQueue->NextByteOffset;
|
||
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 - WriteQueue->NextByteOffset;
|
||
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);
|
||
|
||
|
||
DebugTrace(-1, Dbg, "NpQueryEvent -> STATUS_SUCCESS\n", 0);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
NpTransceive (
|
||
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
|
||
IN PIRP Irp,
|
||
IN PLIST_ENTRY DeferredList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does the transceive named pipe control function
|
||
|
||
Arguments:
|
||
|
||
NpfsDeviceObject - Supplies our device object
|
||
|
||
Irp - Supplies the being processed
|
||
|
||
DeferredList - List of IRPs to complete after we drop locks
|
||
|
||
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;
|
||
|
||
//
|
||
// 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) {
|
||
|
||
return GetExceptionCode ();
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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);
|
||
|
||
|
||
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);
|
||
|
||
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);
|
||
|
||
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->ReadCompletionMode[ FILE_PIPE_SERVER_END ].ReadMode;
|
||
|
||
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->ReadCompletionMode[ FILE_PIPE_CLIENT_END ].ReadMode;
|
||
|
||
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);
|
||
|
||
try_return( Status = STATUS_INVALID_PIPE_STATE );
|
||
}
|
||
|
||
//
|
||
// Check that the read queue is empty.
|
||
//
|
||
|
||
if (!NpIsDataQueueEmpty( ReadQueue )) {
|
||
|
||
DebugTrace(0, Dbg, "Read queue is not empty\n", 0);
|
||
|
||
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;
|
||
Status = NpWriteDataQueue( WriteQueue,
|
||
ReadMode,
|
||
WriteBuffer,
|
||
WriteLength,
|
||
Ccb->Fcb->Specific.Fcb.NamedPipeType,
|
||
&WriteRemaining,
|
||
Ccb,
|
||
NamedPipeEnd,
|
||
UserThread,
|
||
DeferredList );
|
||
|
||
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
||
|
||
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, TRUE )) == NULL) {
|
||
|
||
try_return (Status = STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
IoSetCompletionRoutine( WriteIrp, NpCompleteTransceiveIrp, NULL, TRUE, TRUE, TRUE );
|
||
|
||
WriteIrpSp = IoGetNextIrpStackLocation( WriteIrp );
|
||
|
||
if (WriteRemaining > 0) {
|
||
|
||
WriteIrp->AssociatedIrp.SystemBuffer = NpAllocatePagedPoolWithQuota( WriteRemaining, 'wFpN' );
|
||
if (WriteIrp->AssociatedIrp.SystemBuffer == NULL) {
|
||
IoFreeIrp (WriteIrp);
|
||
try_return (Status = STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
//
|
||
// Safely do the copy
|
||
//
|
||
|
||
try {
|
||
|
||
RtlCopyMemory( WriteIrp->AssociatedIrp.SystemBuffer,
|
||
&WriteBuffer[ WriteLength - WriteRemaining ],
|
||
WriteRemaining );
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
NpFreePool (WriteIrp->AssociatedIrp.SystemBuffer);
|
||
IoFreeIrp (WriteIrp);
|
||
try_return (Status = GetExceptionCode ());
|
||
}
|
||
|
||
} else {
|
||
|
||
WriteIrp->AssociatedIrp.SystemBuffer = NULL;
|
||
}
|
||
|
||
//
|
||
// Set the current stack location, and set in the amount we are
|
||
// try to write.
|
||
//
|
||
|
||
WriteIrp->CurrentLocation -= 1;
|
||
WriteIrp->Tail.Overlay.CurrentStackLocation = WriteIrpSp;
|
||
WriteIrp->Tail.Overlay.Thread = UserThread;
|
||
WriteIrp->IoStatus.Information = WriteRemaining;
|
||
|
||
WriteIrpSp->Parameters.Write.Length = WriteRemaining;
|
||
WriteIrpSp->MajorFunction = IRP_MJ_WRITE;
|
||
|
||
//
|
||
// 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
|
||
//
|
||
|
||
Status = NpAddDataQueueEntry( NamedPipeEnd,
|
||
Ccb,
|
||
WriteQueue,
|
||
WriteEntries,
|
||
Unbuffered,
|
||
WriteRemaining,
|
||
WriteIrp,
|
||
NULL,
|
||
0);
|
||
|
||
if (Status != STATUS_PENDING) {
|
||
NpDeferredCompleteRequest (WriteIrp, Status, DeferredList);
|
||
}
|
||
|
||
}
|
||
|
||
if (!NT_SUCCESS (Status)) {
|
||
try_return (NOTHING);
|
||
}
|
||
|
||
//
|
||
// 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 ));
|
||
|
||
Status = NpAddDataQueueEntry( NamedPipeEnd,
|
||
Ccb,
|
||
ReadQueue,
|
||
ReadEntries,
|
||
Buffered,
|
||
ReadLength,
|
||
Irp,
|
||
NULL,
|
||
0 );
|
||
if (!NT_SUCCESS (Status)) {
|
||
try_return (NOTHING);
|
||
}
|
||
|
||
//
|
||
// And because we've done something we need to signal the
|
||
// other ends event
|
||
//
|
||
|
||
NpSignalEventTableEntry( Event );
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
NpReleaseCcb(Ccb);
|
||
}
|
||
|
||
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
|
||
UNICODE_STRING RemainingPart;
|
||
BOOLEAN Translated;
|
||
|
||
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);
|
||
|
||
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);
|
||
|
||
try_return( Status = STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
WaitBuffer = Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
//
|
||
// Check for an invalid buffer. The Name Length cannot be greater than
|
||
// MAXUSHORT minus the backslash otherwise it will overflow the buffer.
|
||
// We don't need to check for less than 0 because it is unsigned.
|
||
//
|
||
|
||
if ((WaitBuffer->NameLength > (MAXUSHORT - 2)) ||
|
||
(FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name[0]) + WaitBuffer->NameLength > InputBufferLength)) {
|
||
|
||
DebugTrace(0, Dbg, "System buffer size or name length is too small\n", 0);
|
||
|
||
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 = NpAllocatePagedPool( Name.Length, 'WFpN' );
|
||
if (LocalBuffer == NULL) {
|
||
try_return( Status = STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
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) ) {
|
||
|
||
try_return( NOTHING );
|
||
}
|
||
|
||
//
|
||
// Now check to see if we can find a named pipe with the right
|
||
// name
|
||
//
|
||
|
||
Fcb = NpFindPrefix( &Name, CaseInsensitive, &RemainingPart );
|
||
|
||
//
|
||
// 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);
|
||
|
||
try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND );
|
||
}
|
||
|
||
//
|
||
// If translated then Name.Buffer would point to the translated buffer
|
||
//
|
||
|
||
Translated = (Name.Buffer != LocalBuffer);
|
||
|
||
//
|
||
// 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);
|
||
|
||
try_return( Status = STATUS_SUCCESS );
|
||
}
|
||
|
||
//
|
||
// We weren't able to find one so we need to add a new waiter
|
||
//
|
||
|
||
Status = NpAddWaiter( &NpVcb->WaitQueue,
|
||
Fcb->Specific.Fcb.DefaultTimeOut,
|
||
Irp,
|
||
Translated ? &Name : NULL);
|
||
|
||
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
if (LocalBuffer != NULL) {
|
||
NpFreePool( 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);
|
||
|
||
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);
|
||
|
||
DebugTrace(-1, Dbg, "NpImpersonate -> STATUS_ILLEGAL_FUNCTION\n", 0 );
|
||
return STATUS_ILLEGAL_FUNCTION;
|
||
}
|
||
|
||
//
|
||
// set up the impersonation
|
||
//
|
||
|
||
Status = NpImpersonateClientContext( Ccb );
|
||
|
||
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,
|
||
IN PLIST_ENTRY DeferredList
|
||
)
|
||
|
||
/*++
|
||
|
||
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.
|
||
|
||
DeferredList - List of IRP's to be completed later after we drop the locks
|
||
|
||
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);
|
||
|
||
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);
|
||
|
||
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);
|
||
|
||
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);
|
||
|
||
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);
|
||
|
||
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->ReadCompletionMode[ NamedPipeEnd ].ReadMode;
|
||
CompletionMode = Ccb->ReadCompletionMode[ NamedPipeEnd ].CompletionMode;
|
||
|
||
if (ReadOverflowOperation == TRUE && ReadMode != FILE_PIPE_MESSAGE_MODE) {
|
||
NpReleaseCcb(Ccb);
|
||
return STATUS_INVALID_READ_MODE;
|
||
}
|
||
|
||
|
||
//
|
||
// 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);
|
||
|
||
Status = STATUS_PIPE_BROKEN;
|
||
|
||
} else if (CompletionMode == FILE_PIPE_QUEUE_OPERATION) {
|
||
|
||
DebugTrace(0, Dbg, "Put the irp into the read queue\n", 0);
|
||
|
||
Status = NpAddDataQueueEntry( NamedPipeEnd,
|
||
Ccb,
|
||
ReadQueue,
|
||
ReadEntries,
|
||
Unbuffered,
|
||
ReadLength,
|
||
ReadIrp,
|
||
NULL,
|
||
0 );
|
||
|
||
} else {
|
||
|
||
DebugTrace(0, Dbg, "Complete the irp with pipe empty\n", 0);
|
||
|
||
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,
|
||
DeferredList );
|
||
|
||
Status = ReadIrp->IoStatus.Status;
|
||
|
||
//
|
||
// Now set the remaining byte count in the allocation size of
|
||
// the Irp.
|
||
//
|
||
|
||
ReadIrp->Overlay.AllocationSize.QuadPart = ReadQueue->BytesInQueue - ReadQueue->NextByteOffset;
|
||
|
||
//
|
||
// Finish up the read irp.
|
||
//
|
||
}
|
||
|
||
//
|
||
// 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,
|
||
IN PLIST_ENTRY DeferredList
|
||
)
|
||
|
||
/*++
|
||
|
||
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;
|
||
|
||
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);
|
||
|
||
//
|
||
// This is a FSCTL path being used as a write. Make sure we can set the .Information field to the number
|
||
// of bytes written.
|
||
//
|
||
NpConvertFsctlToWrite (Irp);
|
||
|
||
//
|
||
// 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);
|
||
|
||
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);
|
||
|
||
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);
|
||
|
||
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->ReadCompletionMode[ FILE_PIPE_CLIENT_END ].ReadMode;
|
||
|
||
break;
|
||
|
||
case FILE_PIPE_CLIENT_END:
|
||
|
||
WriteQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ];
|
||
|
||
Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_SERVER_END ];
|
||
ReadMode = Ccb->ReadCompletionMode[ FILE_PIPE_SERVER_END ].ReadMode;
|
||
|
||
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);
|
||
return STATUS_PIPE_DISCONNECTED;
|
||
|
||
case FILE_PIPE_LISTENING_STATE:
|
||
|
||
DebugTrace(0, Dbg, "Pipe in listening state\n", 0);
|
||
|
||
NpReleaseCcb(Ccb);
|
||
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);
|
||
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->ReadCompletionMode[NamedPipeEnd].CompletionMode == 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))
|
||
|
||
||
|
||
|
||
(!NpIsDataQueueReaders( WriteQueue ))) {
|
||
|
||
DebugTrace(0, Dbg, "Cannot complete the message without blocking\n", 0);
|
||
|
||
NpReleaseCcb(Ccb);
|
||
Irp->IoStatus.Information = 0;
|
||
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;
|
||
Status = NpWriteDataQueue( WriteQueue,
|
||
ReadMode,
|
||
WriteBuffer,
|
||
WriteLength,
|
||
Ccb->Fcb->Specific.Fcb.NamedPipeType,
|
||
&WriteRemaining,
|
||
Ccb,
|
||
NamedPipeEnd,
|
||
UserThread,
|
||
DeferredList );
|
||
|
||
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
||
|
||
ASSERT( !NpIsDataQueueReaders( WriteQueue ));
|
||
|
||
//
|
||
// 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->ReadCompletionMode[NamedPipeEnd].CompletionMode == FILE_PIPE_COMPLETE_OPERATION) {
|
||
|
||
DebugTrace(0, Dbg, "Complete the byte stream write immediately\n", 0);
|
||
|
||
Irp->IoStatus.Information = WriteLength - WriteRemaining;
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
DebugTrace(0, Dbg, "Add write to data queue\n", 0);
|
||
|
||
//
|
||
// Add this write request to the write queue
|
||
//
|
||
|
||
Status = NpAddDataQueueEntry( NamedPipeEnd,
|
||
Ccb,
|
||
WriteQueue,
|
||
WriteEntries,
|
||
Unbuffered,
|
||
WriteLength,
|
||
Irp,
|
||
NULL,
|
||
WriteLength - WriteRemaining);
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
DebugTrace(0, Dbg, "Complete the Write Irp\n", 0);
|
||
|
||
|
||
//
|
||
// The write irp is finished so we can complete it now
|
||
//
|
||
|
||
}
|
||
|
||
//
|
||
// 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,
|
||
IN PLIST_ENTRY DeferredList
|
||
)
|
||
|
||
/*++
|
||
|
||
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
|
||
|
||
DeferredList - List of IRP's to be completed once we drop our locks.
|
||
|
||
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;
|
||
|
||
//
|
||
// 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);
|
||
|
||
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);
|
||
|
||
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);
|
||
|
||
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->ReadCompletionMode[ FILE_PIPE_SERVER_END ].ReadMode;
|
||
|
||
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->ReadCompletionMode[ FILE_PIPE_CLIENT_END ].ReadMode;
|
||
|
||
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);
|
||
|
||
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);
|
||
|
||
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;
|
||
Status = NpWriteDataQueue( WriteQueue,
|
||
ReadMode,
|
||
WriteBuffer,
|
||
WriteLength,
|
||
Ccb->Fcb->Specific.Fcb.NamedPipeType,
|
||
&WriteRemaining,
|
||
Ccb,
|
||
NamedPipeEnd,
|
||
UserThread,
|
||
DeferredList );
|
||
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
||
|
||
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, TRUE )) == NULL) {
|
||
|
||
try_return( Status = STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
IoSetCompletionRoutine( WriteIrp, NpCompleteTransceiveIrp, NULL, TRUE, TRUE, TRUE );
|
||
|
||
WriteIrpSp = IoGetNextIrpStackLocation( WriteIrp );
|
||
|
||
if (WriteRemaining > 0) {
|
||
|
||
WriteIrp->AssociatedIrp.SystemBuffer = NpAllocatePagedPoolWithQuota( WriteRemaining, 'wFpN' );
|
||
if (WriteIrp->AssociatedIrp.SystemBuffer == NULL) {
|
||
IoFreeIrp (WriteIrp);
|
||
try_return (Status = STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
//
|
||
// Safely do the copy
|
||
//
|
||
|
||
try {
|
||
|
||
RtlCopyMemory( WriteIrp->AssociatedIrp.SystemBuffer,
|
||
&WriteBuffer[ WriteLength - WriteRemaining ],
|
||
WriteRemaining );
|
||
|
||
WriteIrp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
NpFreePool (WriteIrp->AssociatedIrp.SystemBuffer);
|
||
IoFreeIrp (WriteIrp);
|
||
try_return (Status = GetExceptionCode ());
|
||
}
|
||
|
||
} else {
|
||
|
||
WriteIrp->AssociatedIrp.SystemBuffer = NULL;
|
||
}
|
||
|
||
//
|
||
// Set the current stack location
|
||
//
|
||
|
||
WriteIrp->CurrentLocation -= 1;
|
||
WriteIrp->Tail.Overlay.CurrentStackLocation = WriteIrpSp;
|
||
WriteIrp->Tail.Overlay.Thread = UserThread;
|
||
WriteIrpSp->MajorFunction = IRP_MJ_WRITE;
|
||
WriteIrp->UserIosb = &Iosb;
|
||
|
||
//
|
||
// Add this write request to the write queue
|
||
//
|
||
|
||
Status = NpAddDataQueueEntry( NamedPipeEnd,
|
||
Ccb,
|
||
WriteQueue,
|
||
WriteEntries,
|
||
Unbuffered,
|
||
WriteRemaining,
|
||
WriteIrp,
|
||
NULL,
|
||
0 );
|
||
if (Status != STATUS_PENDING) {
|
||
NpDeferredCompleteRequest (WriteIrp, Status, DeferredList);
|
||
}
|
||
}
|
||
if (!NT_SUCCESS (Status)) {
|
||
try_return (NOTHING)
|
||
}
|
||
//
|
||
// 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 ));
|
||
|
||
Status = NpAddDataQueueEntry( NamedPipeEnd,
|
||
Ccb,
|
||
ReadQueue,
|
||
ReadEntries,
|
||
Unbuffered,
|
||
ReadLength,
|
||
Irp,
|
||
NULL,
|
||
0 );
|
||
if (!NT_SUCCESS (Status)) {
|
||
try_return (Status);
|
||
}
|
||
|
||
|
||
//
|
||
// And because we've done something we need to signal the
|
||
// other ends event
|
||
//
|
||
|
||
NpSignalEventTableEntry( Event );
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
NpReleaseCcb(Ccb);
|
||
}
|
||
|
||
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
|
||
|
||
The output buffer may be either a FILE_PIPE_CLIENT_PROCESS_BUFFER or a
|
||
FILE_PIPE_CLIENT_PROCESS_BUFFER_EX.
|
||
|
||
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_EX ClientProcessBuffer;
|
||
PCLIENT_INFO ClientInfo;
|
||
CLIENT_INFO NullInfo = {0};
|
||
|
||
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);
|
||
|
||
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);
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
NpAcquireExclusiveCcb(Ccb);
|
||
|
||
//
|
||
// Copy over the client process ID
|
||
//
|
||
|
||
ClientProcessBuffer = Irp->AssociatedIrp.SystemBuffer;
|
||
ClientProcessBuffer->ClientProcess = Ccb->ClientProcess;
|
||
|
||
ClientInfo = Ccb->ClientInfo;
|
||
if (ClientInfo == NULL) {
|
||
ClientInfo = &NullInfo;
|
||
}
|
||
ClientProcessBuffer->ClientSession = ClientInfo->ClientSession;
|
||
|
||
//
|
||
// Return extended client information if so requested
|
||
// Set the information field to the size of the client process
|
||
// buffer
|
||
//
|
||
|
||
if (OutputBufferLength >= sizeof(FILE_PIPE_CLIENT_PROCESS_BUFFER_EX)) {
|
||
|
||
|
||
ClientProcessBuffer->ClientComputerNameLength =
|
||
ClientInfo->ClientComputerNameLength;
|
||
|
||
RtlCopyMemory( ClientProcessBuffer->ClientComputerBuffer,
|
||
ClientInfo->ClientComputerBuffer,
|
||
ClientInfo->ClientComputerNameLength );
|
||
ClientProcessBuffer->ClientComputerBuffer[
|
||
ClientProcessBuffer->ClientComputerNameLength / sizeof(WCHAR)] = L'\0';
|
||
|
||
Irp->IoStatus.Information = sizeof(FILE_PIPE_CLIENT_PROCESS_BUFFER_EX);
|
||
|
||
} else {
|
||
|
||
Irp->IoStatus.Information = sizeof(FILE_PIPE_CLIENT_PROCESS_BUFFER);
|
||
|
||
}
|
||
NpReleaseCcb(Ccb);
|
||
|
||
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
|
||
|
||
Note that we expect a FILE_PIPE_CLIENT_PROCESS_BUFFER_EX structure to be
|
||
passed in to us.
|
||
|
||
Arguments:
|
||
|
||
NpfsDeviceObject - Supplies our device object
|
||
|
||
Irp - Supplies the being processed
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - An apprropriate return status
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION IrpSp;
|
||
PCLIENT_INFO ClientInfo;
|
||
ULONG InputBufferLength;
|
||
|
||
PCCB Ccb;
|
||
|
||
PFILE_PIPE_CLIENT_PROCESS_BUFFER_EX ClientProcessBuffer;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get the current stack location
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "NpSetClientProcess\n", 0);
|
||
|
||
//
|
||
// Only allow kernel callers for this API as RPC relies on this info being solid.
|
||
//
|
||
if (IrpSp->MinorFunction != IRP_MN_KERNEL_CALL) {
|
||
return STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
|
||
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);
|
||
|
||
return STATUS_PIPE_DISCONNECTED;
|
||
}
|
||
|
||
//
|
||
// Make sure the input buffer is large enough
|
||
//
|
||
|
||
if (InputBufferLength != sizeof(FILE_PIPE_CLIENT_PROCESS_BUFFER_EX)) {
|
||
|
||
DebugTrace(0, Dbg, "Input System buffer size is too small\n", 0);
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
ClientProcessBuffer = Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
//
|
||
// Make verify input length is valid
|
||
//
|
||
|
||
if (ClientProcessBuffer->ClientComputerNameLength >
|
||
FILE_PIPE_COMPUTER_NAME_LENGTH * sizeof (WCHAR)) {
|
||
|
||
DebugTrace(0, Dbg, "Computer Name length is too large\n", 0);
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
ClientInfo = NpAllocatePagedPoolWithQuota (FIELD_OFFSET (CLIENT_INFO, ClientComputerBuffer) +
|
||
ClientProcessBuffer->ClientComputerNameLength,
|
||
'iFpN');
|
||
|
||
if (ClientInfo == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
if (Ccb->ClientInfo != NULL) {
|
||
NpFreePool (Ccb->ClientInfo);
|
||
}
|
||
|
||
Ccb->ClientInfo = ClientInfo;
|
||
//
|
||
// Copy over the client process ID
|
||
//
|
||
|
||
ClientInfo->ClientSession = ClientProcessBuffer->ClientSession;
|
||
Ccb->ClientProcess = ClientProcessBuffer->ClientProcess;
|
||
|
||
ClientInfo->ClientComputerNameLength = ClientProcessBuffer->ClientComputerNameLength;
|
||
RtlCopyMemory( ClientInfo->ClientComputerBuffer,
|
||
ClientProcessBuffer->ClientComputerBuffer,
|
||
ClientProcessBuffer->ClientComputerNameLength );
|
||
|
||
|
||
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) {
|
||
|
||
NpFreePool( Irp->AssociatedIrp.SystemBuffer );
|
||
}
|
||
|
||
IoFreeIrp( Irp );
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|