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