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.
488 lines
12 KiB
488 lines
12 KiB
/*++
|
|
|
|
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,
|
|
IN PLIST_ENTRY DeferredList
|
|
);
|
|
|
|
#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;
|
|
LIST_ENTRY DeferredList;
|
|
|
|
DebugTrace(+1, Dbg, "NpFsdRead\n", 0);
|
|
DbgDoit( NpSlowReadCalls += 1 );
|
|
|
|
PAGED_CODE();
|
|
|
|
InitializeListHead (&DeferredList);
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
FsRtlEnterFileSystem ();
|
|
|
|
NpAcquireSharedVcb ();
|
|
|
|
(VOID) NpCommonRead (IrpSp->FileObject,
|
|
Irp->UserBuffer,
|
|
IrpSp->Parameters.Read.Length,
|
|
&Iosb,
|
|
Irp,
|
|
&DeferredList);
|
|
|
|
NpReleaseVcb ();
|
|
|
|
NpCompleteDeferredIrps (&DeferredList);
|
|
|
|
FsRtlExitFileSystem ();
|
|
|
|
if (Iosb.Status != STATUS_PENDING) {
|
|
Irp->IoStatus.Information = Iosb.Information;
|
|
NpCompleteRequest (Irp, Iosb.Status);
|
|
}
|
|
//
|
|
// 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;
|
|
LIST_ENTRY DeferredList;
|
|
|
|
UNREFERENCED_PARAMETER (FileOffset);
|
|
UNREFERENCED_PARAMETER (Wait);
|
|
UNREFERENCED_PARAMETER (LockKey);
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
PAGED_CODE();
|
|
|
|
InitializeListHead (&DeferredList);
|
|
|
|
FsRtlEnterFileSystem ();
|
|
|
|
NpAcquireSharedVcb ();
|
|
|
|
Results = NpCommonRead (FileObject,
|
|
Buffer,
|
|
Length,
|
|
IoStatus,
|
|
NULL,
|
|
&DeferredList);
|
|
#if DBG
|
|
if (Results) {
|
|
NpFastReadTrue += 1;
|
|
} else {
|
|
NpFastReadFalse += 1;
|
|
}
|
|
#endif
|
|
|
|
NpReleaseVcb ();
|
|
|
|
NpCompleteDeferredIrps (&DeferredList);
|
|
|
|
FsRtlExitFileSystem ();
|
|
return Results;
|
|
}
|
|
|
|
|
|
//
|
|
// Internal support routine
|
|
//
|
|
|
|
BOOLEAN
|
|
NpCommonRead (
|
|
IN PFILE_OBJECT FileObject,
|
|
OUT PVOID ReadBuffer,
|
|
IN ULONG ReadLength,
|
|
OUT PIO_STATUS_BLOCK Iosb,
|
|
IN PIRP Irp OPTIONAL,
|
|
IN PLIST_ENTRY DeferredList
|
|
)
|
|
|
|
/*++
|
|
|
|
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
|
|
|
|
DeferredList - List of IRP's to be completed after we drop the locks
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
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->ReadCompletionMode[ NamedPipeEnd ].ReadMode;
|
|
CompletionMode = Ccb->ReadCompletionMode[ NamedPipeEnd ].CompletionMode;
|
|
|
|
//
|
|
// 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;
|
|
|
|
} 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);
|
|
|
|
Iosb->Status = NpAddDataQueueEntry( NamedPipeEnd,
|
|
Ccb,
|
|
ReadQueue,
|
|
ReadEntries,
|
|
Buffered,
|
|
ReadLength,
|
|
Irp,
|
|
NULL,
|
|
0 );
|
|
|
|
if (!NT_SUCCESS (Iosb->Status)) {
|
|
try_return(Status = FALSE);
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
DebugTrace(0, Dbg, "Complete the irp with pipe empty\n", 0);
|
|
|
|
Iosb->Status = 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,
|
|
DeferredList );
|
|
|
|
if (!NT_SUCCESS (Iosb->Status)) {
|
|
try_return(Status = TRUE);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|