mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3110 lines
78 KiB
3110 lines
78 KiB
/*++
|
|
|
|
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;
|
|
}
|
|
|