/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
Read.c
Abstract:
This module implements the File Read routine for NPFS called by the
dispatch driver.
Author:
Gary Kimura [GaryKi] 21-Aug-1990
Revision History:
--*/
#include "NpProcs.h"
//
// The debug trace level
//
#define Dbg (DEBUG_TRACE_READ)
#if DBG
ULONG NpFastReadTrue = 0;
ULONG NpFastReadFalse = 0;
ULONG NpSlowReadCalls = 0;
#endif
//
// local procedure prototypes
//
BOOLEAN
NpCommonRead (
IN PFILE_OBJECT FileObject,
OUT PVOID ReadBuffer,
IN ULONG ReadLength,
OUT PIO_STATUS_BLOCK Iosb,
IN PIRP Irp OPTIONAL
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NpCommonRead)
#pragma alloc_text(PAGE, NpFastRead)
#pragma alloc_text(PAGE, NpFsdRead)
#endif
NTSTATUS
NpFsdRead (
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine implements the FSD part of the NtReadFile 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
--*/
{
IO_STATUS_BLOCK Iosb;
PIO_STACK_LOCATION IrpSp;
DebugTrace(+1, Dbg, "NpFsdRead\n", 0);
DbgDoit( NpSlowReadCalls += 1 );
PAGED_CODE();
IrpSp = IoGetCurrentIrpStackLocation( Irp );
FsRtlEnterFileSystem();
NpAcquireSharedVcb();
try {
(VOID) NpCommonRead( IrpSp->FileObject,
Irp->UserBuffer,
IrpSp->Parameters.Read.Length,
&Iosb,
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
//
Iosb.Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() );
}
NpReleaseVcb();
FsRtlExitFileSystem();
//
// And return to our caller
//
DebugTrace(-1, Dbg, "NpFsdRead -> %08lx\n", Iosb.Status );
return Iosb.Status;
}
BOOLEAN
NpFastRead (
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN BOOLEAN Wait,
IN ULONG LockKey,
OUT PVOID Buffer,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routine does a fast read bypassing the usual file system
entry routine (i.e., without the Irp).
Arguments:
FileObject - Pointer to the file object being read.
FileOffset - Byte offset in file for desired data.
Length - Length of desired data in bytes.
Wait - FALSE if caller may not block, TRUE otherwise
LockKey - Supplies the Key used to use if the byte range being read is locked.
Buffer - Pointer to output buffer to which data should be copied.
IoStatus - Pointer to standard I/O status block to receive the status
for the transfer.
Return Value:
BOOLEAN - TRUE if the operation completed successfully and FALSE if the
caller needs to take the long IRP based route.
--*/
{
BOOLEAN Results = FALSE;
UNREFERENCED_PARAMETER( FileOffset );
UNREFERENCED_PARAMETER( Wait );
UNREFERENCED_PARAMETER( LockKey );
UNREFERENCED_PARAMETER( DeviceObject );
PAGED_CODE();
#if DBG
FsRtlEnterFileSystem();
NpAcquireSharedVcb();
try {
if (NpCommonRead( FileObject,
Buffer,
Length,
IoStatus,
NULL )) {
NpFastReadTrue += 1;
Results = TRUE;
} else {
NpFastReadFalse += 1;
}
} except( FsRtlIsNtstatusExpected(GetExceptionCode())
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH ) {
NOTHING;
}
NpReleaseVcb();
FsRtlExitFileSystem();
return Results;
#else
FsRtlEnterFileSystem();
NpAcquireSharedVcb();
try {
Results = NpCommonRead( FileObject,
Buffer,
Length,
IoStatus,
NULL );
} except( FsRtlIsNtstatusExpected(GetExceptionCode())
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH ) {
NOTHING;
}
NpReleaseVcb();
FsRtlExitFileSystem();
return Results;
#endif
}
//
// Internal support routine
//
BOOLEAN
NpCommonRead (
IN PFILE_OBJECT FileObject,
OUT PVOID ReadBuffer,
IN ULONG ReadLength,
OUT PIO_STATUS_BLOCK Iosb,
IN PIRP Irp OPTIONAL
)
/*++
Routine Description:
This is the common routine for reading a named pipe both via the fast
path and with an Irp
Arguments:
FileObject - Supplies the file object used in this operation
ReadBuffer - Supplies the buffer where data is to be written
ReadLength - Supplies the length of read buffer in bytes
Iosb - Receives the final completion status of this operation
Irp - Optionally supplies an Irp to be used in this operation
Return Value:
BOOLEAN - TRUE if the operation was successful and FALSE if the caller
needs to take the longer Irp based route.
--*/
{
NODE_TYPE_CODE NodeTypeCode;
PCCB Ccb;
PNONPAGED_CCB NonpagedCcb;
NAMED_PIPE_END NamedPipeEnd;
NAMED_PIPE_CONFIGURATION NamedPipeConfiguration;
ULONG ReadRemaining;
READ_MODE ReadMode;
COMPLETION_MODE CompletionMode;
PDATA_QUEUE ReadQueue;
PEVENT_TABLE_ENTRY Event;
BOOLEAN Status;
PAGED_CODE();
DebugTrace(+1, Dbg, "NpCommonRead\n", 0);
DebugTrace( 0, Dbg, "FileObject = %08lx\n", FileObject);
DebugTrace( 0, Dbg, "ReadBuffer = %08lx\n", ReadBuffer);
DebugTrace( 0, Dbg, "ReadLength = %08lx\n", ReadLength);
DebugTrace( 0, Dbg, "Iosb = %08lx\n", Iosb);
DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp);
Iosb->Information = 0;
//
// Get the Ccb and figure out who we are, and make sure we're not
// disconnected
//
if ((NodeTypeCode = NpDecodeFileObject( FileObject,
NULL,
&Ccb,
&NamedPipeEnd )) == NTC_UNDEFINED) {
DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0);
Iosb->Status = STATUS_PIPE_DISCONNECTED;
if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); }
return TRUE;
}
//
// 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);
Iosb->Status = STATUS_INVALID_PARAMETER;
if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); }
return TRUE;
}
NpAcquireExclusiveCcb(Ccb);
NonpagedCcb = Ccb->NonpagedCcb;
try {
//
// Check if the pipe is not in the connected state.
//
if ((Ccb->NamedPipeState == FILE_PIPE_DISCONNECTED_STATE) ||
(Ccb->NamedPipeState == FILE_PIPE_LISTENING_STATE)) {
DebugTrace(0, Dbg, "Pipe in disconnected or listening state\n", 0);
if (Ccb->NamedPipeState == FILE_PIPE_DISCONNECTED_STATE) {
Iosb->Status = STATUS_PIPE_DISCONNECTED;
} else {
Iosb->Status = STATUS_PIPE_LISTENING;
}
if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, Iosb->Status ); }
try_return(Status = TRUE);
}
ASSERT((Ccb->NamedPipeState == FILE_PIPE_CONNECTED_STATE) ||
(Ccb->NamedPipeState == FILE_PIPE_CLOSING_STATE));
//
// 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);
Iosb->Status = STATUS_INVALID_PARAMETER;
if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); }
try_return (Status = TRUE);
}
//
// Reference our input parameters to make things easier, and
// initialize our main variables that describe the Read command
//
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.
//
if (NamedPipeEnd == FILE_PIPE_SERVER_END) {
ReadQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ];
Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ];
} else {
ReadQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ];
Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_SERVER_END ];
}
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);
Iosb->Status = STATUS_PIPE_BROKEN;
if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, STATUS_PIPE_BROKEN ); }
} else if (CompletionMode == FILE_PIPE_QUEUE_OPERATION) {
if (!ARGUMENT_PRESENT(Irp)) {
DebugTrace(0, Dbg, "Need to supply Irp\n", 0);
try_return(Status = FALSE);
}
DebugTrace(0, Dbg, "Put the irp into the read queue\n", 0);
(VOID)NpAddDataQueueEntry( ReadQueue,
ReadEntries,
Buffered,
ReadLength,
Irp,
NULL );
IoMarkIrpPending( Irp );
Iosb->Status = STATUS_PENDING;
} else {
DebugTrace(0, Dbg, "Complete the irp with pipe empty\n", 0);
Iosb->Status = STATUS_PIPE_EMPTY;
if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, STATUS_PIPE_EMPTY ); }
}
} else {
//
// otherwise there we have a read irp against a read queue
// that contains one or more write entries.
//
*Iosb = NpReadDataQueue( ReadQueue,
FALSE,
FALSE,
ReadBuffer,
ReadLength,
ReadMode,
Ccb );
//
// Finish up the read irp.
//
if (ARGUMENT_PRESENT(Irp)) { Irp->IoStatus = *Iosb; NpCompleteRequest( Irp, Iosb->Status ); }
}
//
// Now we need to advance the read queue to the next write irp to
// skip over flushes and closes
//
(VOID)NpGetNextRealDataQueueEntry( ReadQueue );
Status = TRUE;
//
// 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, "NpCommonRead -> TRUE\n", 0);
return Status;
}