/*++ 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; }