summaryrefslogtreecommitdiffstats
path: root/private/nw/rdr/read.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/rdr/read.c')
-rw-r--r--private/nw/rdr/read.c2838
1 files changed, 2838 insertions, 0 deletions
diff --git a/private/nw/rdr/read.c b/private/nw/rdr/read.c
new file mode 100644
index 000000000..de5e63f03
--- /dev/null
+++ b/private/nw/rdr/read.c
@@ -0,0 +1,2838 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Read.c
+
+Abstract:
+
+ This module implements support for NtReadFile for the
+ NetWare redirector called by the dispatch driver.
+
+Author:
+
+ Colin Watson [ColinW] 07-Apr-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#ifdef NWDBG
+#include <stdlib.h> // rand()
+#endif
+
+//
+// The local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_READ)
+
+#define SIZE_ADJUST( ic ) \
+ ( sizeof( ULONG ) + sizeof( ULONG ) + ( ic->Specific.Read.FileOffset & 0x03 ) )
+
+//
+// Local procedure prototypes
+//
+
+NTSTATUS
+NwCommonRead (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+ReadNcp(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+ReadNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+VOID
+BuildReadNcp(
+ PIRP_CONTEXT IrpContext,
+ ULONG FileOffset,
+ USHORT Length
+ );
+
+NTSTATUS
+ParseReadResponse(
+ PIRP_CONTEXT IrpContext,
+ PNCP_READ_RESPONSE Response,
+ ULONG BytesAvailable,
+ PUSHORT Length
+ );
+
+NTSTATUS
+BurstRead(
+ PIRP_CONTEXT IrpContext
+ );
+
+VOID
+BuildBurstReadRequest(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG Handle,
+ IN ULONG FileOffset,
+ IN ULONG Length
+ );
+
+NTSTATUS
+BurstReadCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+VOID
+RecordPacketReceipt(
+ IN OUT PIRP_CONTEXT IrpContext,
+ IN PVOID ReadData,
+ IN ULONG DataOffset,
+ IN USHORT BytesCount,
+ IN BOOLEAN CopyData
+ );
+
+BOOLEAN
+VerifyBurstRead(
+ PIRP_CONTEXT IrpContext
+ );
+
+VOID
+FreePacketList(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+BurstReadReceive(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PULONG BytesAccepted,
+ IN PUCHAR Response,
+ OUT PMDL *pReceiveMdl
+ );
+
+NTSTATUS
+ReadNcpReceive(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PULONG BytesAccepted,
+ IN PUCHAR Response,
+ OUT PMDL *pReceiveMdl
+ );
+
+NTSTATUS
+ParseBurstReadResponse(
+ IN PIRP_CONTEXT IrpContext,
+ PVOID Response,
+ ULONG BytesAvailable,
+ PUCHAR Flags,
+ PULONG DataOffset,
+ PUSHORT BytesThisPacket,
+ PUCHAR *ReadData,
+ PULONG TotalBytesRead
+ );
+
+PMDL
+AllocateReceivePartialMdl(
+ PMDL FullMdl,
+ ULONG DataOffset,
+ ULONG BytesThisPacket
+ );
+
+VOID
+SetConnectionTimeout(
+ PNONPAGED_SCB pNpScb,
+ ULONG Length
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdRead )
+#pragma alloc_text( PAGE, NwCommonRead )
+#pragma alloc_text( PAGE, ReadNcp )
+#pragma alloc_text( PAGE, BurstRead )
+#pragma alloc_text( PAGE, BuildBurstReadRequest )
+#pragma alloc_text( PAGE, ResubmitBurstRead )
+#pragma alloc_text( PAGE, SetConnectionTimeout )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, ReadNcpCallback )
+#pragma alloc_text( PAGE1, ReadNcpReceive )
+#pragma alloc_text( PAGE1, BuildReadNcp )
+#pragma alloc_text( PAGE1, ParseReadResponse )
+#pragma alloc_text( PAGE1, BurstReadCallback )
+#pragma alloc_text( PAGE1, BurstReadTimeout )
+#pragma alloc_text( PAGE1, RecordPacketReceipt )
+#pragma alloc_text( PAGE1, VerifyBurstRead )
+#pragma alloc_text( PAGE1, FreePacketList )
+#pragma alloc_text( PAGE1, BurstReadReceive )
+#pragma alloc_text( PAGE1, ParseBurstReadResponse )
+#pragma alloc_text( PAGE1, AllocateReceivePartialMdl )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+NwFsdRead(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the FSD routine that handles NtReadFile.
+
+Arguments:
+
+ NwfsDeviceObject - Supplies the device object for the read function.
+
+ Irp - Supplies the IRP to process.
+
+Return Value:
+
+ NTSTATUS - The result status.
+
+--*/
+
+{
+ PIRP_CONTEXT pIrpContext = NULL;
+ NTSTATUS status;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdRead\n", 0);
+
+ //
+ // Call the common direcotry control routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonRead( pIrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( pIrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // 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 = NwProcessException( pIrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( pIrpContext ) {
+
+ if ( status != STATUS_PENDING ) {
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ }
+
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdRead -> %08lx\n", status );
+
+ Stats.ReadOperations++;
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonRead (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine does the common code for NtReadFile.
+
+Arguments:
+
+ IrpContext - Supplies the request being processed.
+
+Return Value:
+
+ NTSTATUS - The return status for the operation
+
+--*/
+
+{
+ NTSTATUS status;
+
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PFCB fcb;
+ PVOID fsContext;
+
+ ULONG BufferLength; // Size application requested to read
+ ULONG ByteOffset;
+ ULONG PreviousByteOffset;
+ ULONG BytesRead;
+ ULONG NewBufferLength;
+ PVOID SystemBuffer;
+
+ //
+ // Get the current stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "CommonRead...\n", 0);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
+
+ //
+ // Decode the file object to figure out who we are. If the result
+ // is not the root DCB then its an illegal parameter.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ (PVOID *)&icb );
+
+ fcb = (PFCB)icb->SuperType.Fcb;
+
+ if (((nodeTypeCode != NW_NTC_ICB) &&
+ (nodeTypeCode != NW_NTC_ICB_SCB)) ||
+ (!icb->HasRemoteHandle) ) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcbSpecial( icb );
+
+ if ( fcb->NodeTypeCode == NW_NTC_FCB ) {
+
+ IrpContext->pScb = fcb->Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = icb;
+
+ } else if ( fcb->NodeTypeCode == NW_NTC_SCB ) {
+
+ IrpContext->pScb = icb->SuperType.Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = icb;
+ fcb = NULL;
+
+ } else {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", status );
+ return status;
+ }
+
+ BufferLength = irpSp->Parameters.Read.Length;
+ ByteOffset = irpSp->Parameters.Read.ByteOffset.LowPart;
+
+ //
+ // Fail reads beyond file offset 4GB.
+ //
+
+ if ( irpSp->Parameters.Read.ByteOffset.HighPart != 0 ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ //
+ // Special case 0 length read.
+ //
+
+ if ( BufferLength == 0 ) {
+ Irp->IoStatus.Information = 0;
+ return( STATUS_SUCCESS );
+ }
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn( Irp->Flags, IRP_PAGING_IO)) {
+
+ PreviousByteOffset = irpSp->FileObject->CurrentByteOffset.LowPart;
+ irpSp->FileObject->CurrentByteOffset.LowPart = ByteOffset;
+ }
+
+ //
+ // First flush the write behind cache unless this is a
+ // file stream operation.
+ //
+
+ if ( fcb ) {
+
+ status = AcquireFcbAndFlushCache( IrpContext, fcb->NonPagedFcb );
+ if ( !NT_SUCCESS( status ) ) {
+ goto ResetByteOffsetAndExit;
+ }
+
+ //
+ // Read as much as we can from cache.
+ //
+
+ NwMapUserBuffer( Irp, KernelMode, &SystemBuffer );
+
+ BytesRead = CacheRead(
+ fcb->NonPagedFcb,
+ ByteOffset,
+ BufferLength,
+#if NWFASTIO
+ SystemBuffer,
+ FALSE );
+#else
+ SystemBuffer );
+#endif
+
+ //
+ // If all the data was the the cache, we are done.
+ //
+
+ if ( BytesRead == BufferLength ) {
+
+ Irp->IoStatus.Information = BytesRead;
+
+ //
+ // Update the current byte offset in the file if it is a
+ // synchronous file (and this is not paging I/O).
+ //
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn( Irp->Flags, IRP_PAGING_IO)) {
+
+ irpSp->FileObject->CurrentByteOffset.QuadPart += BytesRead;
+ }
+
+ //
+ // If this is a paging read, we need to flush the MDL
+ // since on some systems the I-cache and D-cache
+ // are not synchronized.
+ //
+
+ if (FlagOn(Irp->Flags, IRP_PAGING_IO)) {
+ KeFlushIoBuffers( Irp->MdlAddress, TRUE, FALSE);
+ }
+
+ //
+ // Record read offset and size to discover a sequential read pattern.
+ //
+
+ fcb->LastReadOffset = irpSp->Parameters.Read.ByteOffset.LowPart;
+ fcb->LastReadSize = irpSp->Parameters.Read.Length;
+
+ DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", STATUS_SUCCESS );
+ return( STATUS_SUCCESS );
+ }
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ // Protect read cache
+ NwAcquireExclusiveFcb( fcb->NonPagedFcb, TRUE );
+
+ IrpContext->Specific.Read.CacheReadSize = BytesRead;
+ fcb->NonPagedFcb->CacheFileOffset = ByteOffset + BufferLength;
+
+ ByteOffset += BytesRead;
+ BufferLength -= BytesRead;
+
+ NewBufferLength = CalculateReadAheadSize(
+ IrpContext,
+ fcb->NonPagedFcb,
+ BytesRead,
+ ByteOffset,
+ BufferLength );
+
+ IrpContext->Specific.Read.ReadAheadSize = NewBufferLength - BufferLength;
+
+ } else {
+
+ //
+ // This is a read from a ds file stream handle. For now,
+ // there's no cache support.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ BytesRead = 0;
+
+ IrpContext->Specific.Read.CacheReadSize = BytesRead;
+ IrpContext->Specific.Read.ReadAheadSize = 0;
+ }
+
+ //
+ // If burst mode is enabled, and this read is too big to do in a single
+ // core read NCP, use burst mode.
+ //
+ // BUGBUG: We don't support burst against a ds file stream yet.
+ //
+
+ if ( IrpContext->pNpScb->ReceiveBurstModeEnabled &&
+ NewBufferLength > IrpContext->pNpScb->BufferSize &&
+ fcb ) {
+ status = BurstRead( IrpContext );
+ } else {
+ status = ReadNcp( IrpContext );
+ }
+
+ Irp->MdlAddress = IrpContext->pOriginalMdlAddress;
+
+ if (Irp->MdlAddress != NULL) {
+ // Next might point to the cache mdl.
+ Irp->MdlAddress->Next = NULL;
+ }
+
+ if ( NT_SUCCESS( status ) ) {
+
+ //
+ // Update the current byte offset in the file if it is a
+ // synchronous file (and this is not paging I/O).
+ //
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn( Irp->Flags, IRP_PAGING_IO)) {
+
+ irpSp->FileObject->CurrentByteOffset.QuadPart += Irp->IoStatus.Information;
+ }
+
+ //
+ // If this is a paging read, we need to flush the MDL
+ // since on some systems the I-cache and D-cache
+ // are not synchronized.
+ //
+
+ if (FlagOn(Irp->Flags, IRP_PAGING_IO)) {
+ KeFlushIoBuffers( Irp->MdlAddress, TRUE, FALSE);
+ }
+
+ //
+ // If we received 0 bytes without an error, we must be beyond
+ // the end of file.
+ //
+
+ if ( Irp->IoStatus.Information == 0 ) {
+ status = STATUS_END_OF_FILE;
+ }
+ }
+
+ //
+ // Record read offset and size to discover a sequential read pattern.
+ //
+
+ if ( fcb ) {
+
+ fcb->LastReadOffset = irpSp->Parameters.Read.ByteOffset.LowPart;
+ fcb->LastReadSize = irpSp->Parameters.Read.Length;
+
+ NwReleaseFcb( fcb->NonPagedFcb );
+
+ }
+
+ DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", status);
+
+ResetByteOffsetAndExit:
+
+ if ( !NT_SUCCESS( status ) ) {
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn( Irp->Flags, IRP_PAGING_IO)) {
+
+ irpSp->FileObject->CurrentByteOffset.LowPart = PreviousByteOffset;
+
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS
+ReadNcp(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine exchanges a series of read NCPs with the server.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ Icb - Supplies the file specific information.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG Length; // Size we will send to the server
+ PMDL DataMdl;
+
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PICB Icb;
+ ULONG ByteOffset;
+ ULONG BufferLength;
+ ULONG MdlLength;
+ BOOLEAN Done;
+ PMDL Mdl, NextMdl;
+
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+ Icb = IrpContext->Icb;
+
+ BufferLength = irpSp->Parameters.Read.Length +
+ IrpContext->Specific.Read.ReadAheadSize -
+ IrpContext->Specific.Read.CacheReadSize;
+
+ ByteOffset = irpSp->Parameters.Read.ByteOffset.LowPart +
+ IrpContext->Specific.Read.CacheReadSize;
+
+ IrpContext->Specific.Read.FileOffset = ByteOffset;
+
+ DebugTrace(+1, Dbg, "ReadNcp...\n", 0);
+ DebugTrace( 0, Dbg, "irp = %08lx\n", (ULONG)irp);
+ DebugTrace( 0, Dbg, "File = %wZ\n", &Icb->SuperType.Fcb->FullFileName);
+ DebugTrace( 0, Dbg, "Length = %ld\n", BufferLength);
+ DebugTrace( 0, Dbg, "Offset = %d\n", ByteOffset);
+
+ if ( Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_FCB ) {
+
+ pScb = Icb->SuperType.Fcb->Scb;
+
+ } else if ( Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_SCB ) {
+
+ pScb = Icb->SuperType.Scb;
+
+ }
+
+ ASSERT( pScb );
+
+ //
+ // Update the original MDL record in the Irp context so that we
+ // can restore it on i/o completion.
+ //
+
+ IrpContext->pOriginalMdlAddress = irp->MdlAddress;
+
+ Length = MIN( IrpContext->pNpScb->BufferSize, BufferLength );
+
+ //
+ // The old servers will not accept reads that cross 4k boundaries in the file
+ //
+
+ if ((IrpContext->pNpScb->PageAlign) &&
+ (DIFFERENT_PAGES( ByteOffset, Length ))) {
+
+ Length = 4096 - ((ULONG)ByteOffset & (4096-1));
+
+ }
+
+ IrpContext->Specific.Read.Buffer = irp->UserBuffer;
+ IrpContext->Specific.Read.ReadOffset = IrpContext->Specific.Read.CacheReadSize;
+ IrpContext->Specific.Read.RemainingLength = BufferLength;
+ IrpContext->Specific.Read.PartialMdl = NULL;
+
+ //
+ // Set up to process a read NCP
+ //
+
+ pNpScb = pScb->pNpScb;
+ IrpContext->pEx = ReadNcpCallback;
+ IrpContext->Destination = pNpScb->RemoteAddress;
+ IrpContext->PacketType = NCP_FUNCTION;
+ IrpContext->ReceiveDataRoutine = ReadNcpReceive;
+
+ pNpScb->MaxTimeOut = 2 * pNpScb->TickCount + 10;
+ pNpScb->TimeOut = pNpScb->SendTimeout;
+ pNpScb->RetryCount = DefaultRetryCount;
+
+ Done = FALSE;
+
+ while ( !Done ) {
+
+ //
+ // Setup to do at most 64K of i/o asynchronously, or buffer length.
+ //
+
+ IrpContext->Specific.Read.BurstSize =
+ MIN( 64 * 1024, IrpContext->Specific.Read.RemainingLength );
+
+ IrpContext->Specific.Read.BurstRequestOffset = 0;
+
+ //
+ // Try to allocate an MDL for this i/o.
+ //
+
+ if ( IrpContext->Specific.Read.ReadAheadSize == 0 ) {
+ MdlLength = IrpContext->Specific.Read.BurstSize;
+ } else {
+ MdlLength = IrpContext->Specific.Read.BurstSize - IrpContext->Specific.Read.ReadAheadSize;
+ }
+
+ DataMdl = ALLOCATE_MDL(
+ (PCHAR)IrpContext->Specific.Read.Buffer +
+ IrpContext->Specific.Read.ReadOffset,
+ MdlLength,
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL);
+
+ if ( DataMdl == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ IrpContext->Specific.Read.FullMdl = DataMdl;
+
+ //
+ // If there is no MDL for this read, probe the data MDL to
+ // lock it's pages down. Otherwise, use the data MDL as
+ // a partial MDL.
+ //
+
+ if ( IrpContext->pOriginalMdlAddress == NULL ) {
+
+ try {
+ MmProbeAndLockPages( DataMdl, irp->RequestorMode, IoWriteAccess);
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ FREE_MDL( DataMdl );
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ IoBuildPartialMdl(
+ IrpContext->pOriginalMdlAddress,
+ DataMdl,
+ (PCHAR)IrpContext->Specific.Read.Buffer +
+ IrpContext->Specific.Read.ReadOffset,
+ MdlLength );
+
+ }
+
+ IrpContext->Specific.Read.BurstBuffer = MmGetSystemAddressForMdl( DataMdl );
+
+ if ( IrpContext->Specific.Read.BurstSize ==
+ IrpContext->Specific.Read.RemainingLength ) {
+ Done = TRUE;
+ }
+
+ if ( IrpContext->Specific.Read.ReadAheadSize != 0 ) {
+ DataMdl->Next = Icb->NpFcb->CacheMdl;
+ }
+
+ IrpContext->Specific.Read.LastReadLength = Length;
+
+ //
+ // Build and send the request.
+ //
+
+ BuildReadNcp(
+ IrpContext,
+ IrpContext->Specific.Read.FileOffset,
+ (USHORT) MIN( Length, IrpContext->Specific.Read.RemainingLength ) );
+
+ status = PrepareAndSendPacket( IrpContext );
+ if ( NT_SUCCESS( status )) {
+ KeWaitForSingleObject(
+ &IrpContext->Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+
+ status = IrpContext->Specific.Read.Status;
+
+ }
+
+ //
+ // Stop looping if the read failed, or we read less data than
+ // requested.
+ //
+
+ if ( !NT_SUCCESS( status ) ||
+ IrpContext->Specific.Read.BurstSize != 0 ) {
+
+ Done = TRUE;
+
+ }
+
+ if ( IrpContext->pOriginalMdlAddress == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+
+ }
+
+ //
+ // Free the receive MDL if one was allocated.
+ //
+
+ Mdl = IrpContext->Specific.Read.PartialMdl;
+
+ while ( Mdl != NULL ) {
+ NextMdl = Mdl->Next;
+ FREE_MDL( Mdl );
+ Mdl = NextMdl;
+ }
+
+ DebugTrace(-1, Dbg, "ReadNcp -> %08lx\n", status );
+
+ Stats.ReadNcps++;
+
+ return status;
+}
+
+
+NTSTATUS
+ReadNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ BytesAvailable - Number of bytes in the response.
+
+ Response - The response data.
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG Length;
+ USHORT USLength;
+ PNONPAGED_FCB NpFcb;
+
+ DebugTrace(0, Dbg, "ReadNcpCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ IrpContext->Specific.Read.Status = STATUS_REMOTE_NOT_LISTENING;
+
+ NwSetIrpContextEvent( IrpContext );
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ //
+ // How much data was received?
+ //
+
+ Status = ParseReadResponse(
+ IrpContext,
+ (PNCP_READ_RESPONSE)Response,
+ BytesAvailable,
+ &USLength );
+
+ Length = (ULONG)USLength;
+ DebugTrace(0, Dbg, "Ncp contains %d bytes\n", Length);
+
+ if ((NT_SUCCESS(Status)) &&
+ (Length != 0)) {
+
+ //
+ // If we are receiving the data at indication time, copy the
+ // user's data to the user's buffer.
+ //
+
+ if ( Response != IrpContext->rsp ) {
+
+ //
+ // Read in the data.
+ // Note: If the FileOffset is at an odd byte then the server
+ // will insert an extra pad byte.
+ //
+
+ CopyBufferToMdl(
+ IrpContext->Specific.Read.FullMdl,
+ IrpContext->Specific.Read.BurstRequestOffset,
+ Response + sizeof( NCP_READ_RESPONSE ) + ( IrpContext->Specific.Read.FileOffset & 1),
+ Length );
+
+ DebugTrace( 0, Dbg, "RxLength= %ld\n", Length);
+
+ dump( Dbg,(PUCHAR)IrpContext->Specific.Read.BurstBuffer +
+ IrpContext->Specific.Read.BurstRequestOffset,
+ Length);
+
+ }
+
+ DebugTrace( 0, Dbg, "RxLength= %ld\n", Length);
+ IrpContext->Specific.Read.ReadOffset += Length;
+ IrpContext->Specific.Read.BurstRequestOffset += Length;
+ IrpContext->Specific.Read.FileOffset += Length;
+ IrpContext->Specific.Read.RemainingLength -= Length;
+ IrpContext->Specific.Read.BurstSize -= Length;
+ }
+
+ DebugTrace( 0, Dbg, "RemainingLength = %ld\n",IrpContext->Specific.Read.RemainingLength);
+
+ //
+ // If the previous read was succesful, and we received as much data
+ // as we asked for, and there is more locked data, send the next
+ // read request.
+ //
+
+
+ if ( ( NT_SUCCESS( Status ) ) &&
+ ( IrpContext->Specific.Read.BurstSize != 0 ) &&
+ ( Length == IrpContext->Specific.Read.LastReadLength ) ) {
+
+ //
+ // Read the next packet.
+ //
+
+ Length = MIN( IrpContext->pNpScb->BufferSize,
+ IrpContext->Specific.Read.BurstSize );
+
+ //
+ // The server will not accept reads that cross 4k boundaries
+ // in the file.
+ //
+
+ if ((IrpContext->pNpScb->PageAlign) &&
+ (DIFFERENT_PAGES( IrpContext->Specific.Read.FileOffset, Length ))) {
+ Length = 4096 - ((ULONG)IrpContext->Specific.Read.FileOffset & (4096-1));
+ }
+
+ IrpContext->Specific.Read.LastReadLength = Length;
+ DebugTrace( 0, Dbg, "Length = %ld\n", Length);
+
+ //
+ // Build and send the request.
+ //
+
+ BuildReadNcp(
+ IrpContext,
+ IrpContext->Specific.Read.FileOffset,
+ (USHORT)Length );
+
+ Status = PrepareAndSendPacket( IrpContext );
+
+ Stats.ReadNcps++;
+
+ if ( !NT_SUCCESS(Status) ) {
+ // Abandon this request
+ goto returnstatus;
+ }
+
+ } else {
+returnstatus:
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+ NpFcb = IrpContext->Icb->NpFcb;
+
+ if ( IrpContext->Icb->NodeTypeCode == NW_NTC_ICB_SCB ) {
+ NpFcb = NULL;
+ }
+
+ //
+ // Calculate how much data we read into the cache, and how much data
+ // we read into the users buffer.
+ //
+
+ if ( NpFcb ) {
+
+ if ( IrpContext->Specific.Read.ReadOffset > irpSp->Parameters.Read.Length ) {
+
+ ASSERT(NpFcb->CacheBuffer != NULL ) ; // had better be there..
+
+ NpFcb->CacheDataSize = IrpContext->Specific.Read.ReadOffset -
+ irpSp->Parameters.Read.Length;
+
+ Irp->IoStatus.Information = irpSp->Parameters.Read.Length;
+
+ } else {
+
+ NpFcb->CacheDataSize = 0;
+ Irp->IoStatus.Information = IrpContext->Specific.Read.ReadOffset;
+
+ }
+
+ } else {
+
+ Irp->IoStatus.Information = IrpContext->Specific.Read.ReadOffset;
+
+ }
+
+ //
+ // We're done with this request, signal the reading thread.
+ //
+
+ IrpContext->Specific.Read.Status = Status;
+
+ NwSetIrpContextEvent( IrpContext );
+ }
+
+ DebugTrace( 0, Dbg, "ReadNcpCallback -> %08lx\n", Status );
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+ReadNcpReceive(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PULONG BytesAccepted,
+ IN PUCHAR Response,
+ OUT PMDL *pReceiveMdl
+ )
+{
+ PMDL ReceiveMdl;
+ PMDL Mdl, NextMdl;
+
+ DebugTrace( 0, Dbg, "ReadNcpReceive\n", 0 );
+
+ Mdl = IrpContext->Specific.Read.PartialMdl;
+ IrpContext->Specific.Read.PartialMdl = NULL;
+
+ while ( Mdl != NULL ) {
+ NextMdl = Mdl->Next;
+ FREE_MDL( Mdl );
+ Mdl = NextMdl;
+ }
+
+ //
+ // Set up receive MDL. Note that we get an extra byte of header
+ // when reading from an odd offset.
+ //
+
+ IrpContext->RxMdl->ByteCount = sizeof( NCP_READ_RESPONSE ) +
+ (IrpContext->Specific.Read.FileOffset & 1);
+
+ ASSERT( IrpContext->Specific.Read.FullMdl != NULL );
+
+ //
+ // If we are reading at EOF, or there was a read error there will
+ // be a small response.
+ //
+
+ if ( BytesAvailable > MmGetMdlByteCount( IrpContext->RxMdl ) ) {
+
+ ReceiveMdl = AllocateReceivePartialMdl(
+ IrpContext->Specific.Read.FullMdl,
+ IrpContext->Specific.Read.BurstRequestOffset,
+ BytesAvailable - MmGetMdlByteCount( IrpContext->RxMdl ) );
+
+ IrpContext->RxMdl->Next = ReceiveMdl;
+
+ // Record Mdl to free when CopyIndicatedData or Irp completed.
+ IrpContext->Specific.Read.PartialMdl = ReceiveMdl;
+
+ } else {
+
+ IrpContext->RxMdl->Next = NULL;
+
+ }
+
+ *pReceiveMdl = IrpContext->RxMdl;
+ return STATUS_SUCCESS;
+}
+
+
+VOID
+BuildReadNcp(
+ PIRP_CONTEXT IrpContext,
+ ULONG FileOffset,
+ USHORT Length
+ )
+{
+ PNCP_READ_REQUEST ReadRequest;
+
+ ReadRequest = (PNCP_READ_REQUEST)IrpContext->req;
+
+ ReadRequest->RequestHeader.NcpHeader.Command = PEP_COMMAND_REQUEST;
+ ReadRequest->RequestHeader.NcpHeader.ConnectionIdLow =
+ IrpContext->pNpScb->ConnectionNo;
+ ReadRequest->RequestHeader.NcpHeader.ConnectionIdHigh =
+ IrpContext->pNpScb->ConnectionNoHigh;
+ ReadRequest->RequestHeader.NcpHeader.TaskId =
+ IrpContext->Icb->Pid;
+
+ ReadRequest->RequestHeader.FunctionCode = NCP_READ_FILE;
+ ReadRequest->Unused = 0;
+ RtlMoveMemory(
+ ReadRequest->Handle,
+ IrpContext->Icb->Handle,
+ sizeof( IrpContext->Icb->Handle ) );
+
+ LongByteSwap( ReadRequest->FileOffset, FileOffset );
+ ShortByteSwap( ReadRequest->Length, Length );
+
+ IrpContext->TxMdl->ByteCount = sizeof( *ReadRequest );
+ SetFlag( IrpContext->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED );
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ return;
+}
+
+NTSTATUS
+ParseReadResponse(
+ PIRP_CONTEXT IrpContext,
+ PNCP_READ_RESPONSE Response,
+ ULONG BytesAvailable,
+ PUSHORT Length )
+{
+ NTSTATUS Status;
+
+ Status = ParseNcpResponse( IrpContext, &Response->ResponseHeader );
+
+ if (!NT_SUCCESS(Status)) {
+ return( Status );
+ }
+
+ if ( BytesAvailable < sizeof( NCP_READ_RESPONSE ) ) {
+ return( STATUS_UNEXPECTED_NETWORK_ERROR );
+ }
+
+ ShortByteSwap( *Length, Response->Length );
+
+ return( Status );
+}
+
+NTSTATUS
+BurstRead(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine exchanges a series of burst read NCPs with the server.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ ByteOffset - The file offset for the read.
+
+ BufferLength - The number of bytes to read.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG Length; // Size we will send to the server
+ PMDL DataMdl;
+ ULONG MdlLength;
+
+ PSCB pScb;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PICB Icb;
+ PNONPAGED_SCB pNpScb;
+ ULONG ByteOffset;
+ ULONG BufferLength;
+
+ BOOLEAN Done;
+
+ PAGED_CODE();
+
+ pNpScb = IrpContext->pNpScb;
+
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+ Icb = IrpContext->Icb;
+
+ BufferLength = irpSp->Parameters.Read.Length +
+ IrpContext->Specific.Read.ReadAheadSize -
+ IrpContext->Specific.Read.CacheReadSize;
+
+ ByteOffset = irpSp->Parameters.Read.ByteOffset.LowPart +
+ IrpContext->Specific.Read.CacheReadSize;
+
+ IrpContext->Specific.Read.FileOffset = ByteOffset;
+ IrpContext->Specific.Read.TotalReadOffset = ByteOffset;
+ IrpContext->Specific.Read.TotalReadLength = BufferLength;
+
+ DebugTrace(+1, Dbg, "BurstRead...\n", 0);
+ DebugTrace( 0, Dbg, "irp = %08lx\n", (ULONG)irp);
+ DebugTrace( 0, Dbg, "File = %wZ\n", &Icb->SuperType.Fcb->FullFileName);
+ DebugTrace( 0, Dbg, "Length = %ld\n", BufferLength);
+ DebugTrace( 0, Dbg, "Offset = %ld\n", ByteOffset);
+ DebugTrace( 0, Dbg, "Org Len = %ld\n", irpSp->Parameters.Read.Length );
+ DebugTrace( 0, Dbg, "Org Off = %ld\n", irpSp->Parameters.Read.ByteOffset.LowPart );
+
+ ASSERT (Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_FCB);
+
+ pScb = Icb->SuperType.Fcb->Scb;
+
+ ASSERT (pScb->NodeTypeCode == NW_NTC_SCB);
+
+ //
+ // Update the original MDL record in the Irp context so that we
+ // can restore it on i/o completion.
+ //
+
+ IrpContext->pOriginalMdlAddress = irp->MdlAddress;
+
+ Length = MIN( pNpScb->MaxReceiveSize, BufferLength );
+
+ if ( pNpScb->BurstRenegotiateReqd ) {
+ pNpScb->BurstRenegotiateReqd = FALSE;
+
+ RenegotiateBurstMode( IrpContext, pNpScb );
+ }
+
+ IrpContext->Specific.Read.ReadOffset = IrpContext->Specific.Read.CacheReadSize;
+ IrpContext->Specific.Read.RemainingLength = BufferLength;
+ IrpContext->Specific.Read.LastReadLength = Length;
+
+ InitializeListHead( &IrpContext->Specific.Read.PacketList );
+ IrpContext->Specific.Read.BurstRequestOffset = 0;
+ IrpContext->Specific.Read.BurstSize = 0;
+ IrpContext->Specific.Read.DataReceived = FALSE;
+
+ IrpContext->pTdiStruct = &pNpScb->Burst;
+ IrpContext->TimeoutRoutine = BurstReadTimeout;
+ IrpContext->ReceiveDataRoutine = BurstReadReceive;
+
+ IrpContext->Specific.Read.Buffer = irp->UserBuffer;
+
+ IrpContext->pEx = BurstReadCallback;
+ IrpContext->Destination = pNpScb->RemoteAddress;
+ IrpContext->PacketType = NCP_BURST;
+
+ //
+ // Tell BurstWrite that it needs to send a dummy Ncp on the next write.
+ //
+
+ pNpScb->BurstDataWritten = 0x00010000;
+
+ //
+ // The server will pause NwReceiveDelay between packets. Make sure we have our timeout
+ // so that we will take that into account.
+ //
+
+ SetConnectionTimeout( IrpContext->pNpScb, Length );
+
+ Done = FALSE;
+
+ while ( !Done ) {
+
+ //
+ // Set burst read timeouts to how long we think the burst should take.
+ //
+
+ pNpScb->RetryCount = 20;
+
+ //
+ // Allocate and build an MDL for the users buffer.
+ //
+
+ if ( IrpContext->Specific.Read.ReadAheadSize == 0 ) {
+ MdlLength = Length;
+ } else {
+ MdlLength = Length - IrpContext->Specific.Read.ReadAheadSize;
+ }
+
+ DataMdl = ALLOCATE_MDL(
+ (PCHAR)IrpContext->Specific.Read.Buffer +
+ IrpContext->Specific.Read.ReadOffset,
+ MdlLength,
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL);
+
+ if ( DataMdl == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // If there is no MDL for this read, probe the data MDL to lock it's
+ // pages down.
+ //
+ // Otherwise, use the data MDL as a partial MDL and lock the pages
+ // accordingly.
+ //
+
+ if ( IrpContext->pOriginalMdlAddress == NULL ) {
+
+ try {
+ MmProbeAndLockPages( DataMdl, irp->RequestorMode, IoWriteAccess);
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ FREE_MDL( DataMdl );
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ IoBuildPartialMdl(
+ IrpContext->pOriginalMdlAddress,
+ DataMdl,
+ (PCHAR)IrpContext->Specific.Read.Buffer +
+ IrpContext->Specific.Read.ReadOffset,
+ MdlLength );
+ }
+
+ IrpContext->Specific.Read.FullMdl = DataMdl;
+ IrpContext->Specific.Read.BurstBuffer = MmGetSystemAddressForMdl( DataMdl );
+
+ if ( IrpContext->Specific.Read.ReadAheadSize != 0 ) {
+ DataMdl->Next = Icb->NpFcb->CacheMdl;
+ }
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_BURST_REQUEST | IRP_FLAG_BURST_PACKET );
+
+ //
+ // Send the request.
+ //
+
+ BuildBurstReadRequest(
+ IrpContext,
+ *(ULONG UNALIGNED *)(&Icb->Handle[2]),
+ IrpContext->Specific.Read.FileOffset,
+ Length );
+
+ status = PrepareAndSendPacket( IrpContext );
+ if ( NT_SUCCESS( status )) {
+ status = KeWaitForSingleObject(
+ &IrpContext->Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+ }
+
+ if ( IrpContext->pOriginalMdlAddress == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+ FreePacketList( IrpContext );
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_BURST_REQUEST );
+
+ status = IrpContext->Specific.Read.Status;
+
+ if ( status != STATUS_REMOTE_NOT_LISTENING ) {
+ IrpContext->pNpScb->BurstRequestNo++;
+ NwProcessReceiveBurstSuccess( IrpContext->pNpScb );
+ }
+
+ if ( !NT_SUCCESS( status ) ) {
+ return( status );
+ }
+
+ //
+ // Update the read status data.
+ //
+
+ IrpContext->Specific.Read.ReadOffset +=
+ IrpContext->Specific.Read.BurstSize;
+ IrpContext->Specific.Read.FileOffset +=
+ IrpContext->Specific.Read.BurstSize;
+ IrpContext->Specific.Read.RemainingLength -=
+ IrpContext->Specific.Read.BurstSize;
+
+ if ( IrpContext->Specific.Read.LastReadLength ==
+ IrpContext->Specific.Read.BurstSize &&
+
+ IrpContext->Specific.Read.RemainingLength > 0 ) {
+
+ //
+ // We've received all the data from the current burst, and we
+ // received as many bytes as we asked for, and we need more data
+ // to satisfy the users read request, start another read burst.
+ //
+
+ Length = MIN( IrpContext->pNpScb->MaxReceiveSize,
+ IrpContext->Specific.Read.RemainingLength );
+
+ DebugTrace( 0, Dbg, "Requesting another burst, length = %ld\n", Length);
+
+ ASSERT( Length != 0 );
+
+ IrpContext->Specific.Read.LastReadLength = Length;
+ (PUCHAR)IrpContext->Specific.Read.BurstBuffer +=
+ IrpContext->Specific.Read.BurstSize;
+ IrpContext->Specific.Read.BurstRequestOffset = 0;
+ IrpContext->Specific.Read.BurstSize = 0;
+ IrpContext->Specific.Read.DataReceived = FALSE;
+
+ } else {
+ Done = TRUE;
+ }
+
+ }
+
+
+ //
+ // Calculate how much data we read into the cache, and how much data
+ // we read into the users buffer.
+ //
+
+ if ( IrpContext->Specific.Read.ReadOffset > irpSp->Parameters.Read.Length ) {
+
+ ASSERT(Icb->NpFcb->CacheBuffer != NULL ) ; // this had better be there
+
+ Icb->NpFcb->CacheDataSize =
+ IrpContext->Specific.Read.ReadOffset -
+ irpSp->Parameters.Read.Length;
+
+ irp->IoStatus.Information = irpSp->Parameters.Read.Length;
+
+ } else {
+
+ Icb->NpFcb->CacheDataSize = 0;
+ irp->IoStatus.Information = IrpContext->Specific.Read.ReadOffset;
+
+ }
+
+ DebugTrace( 0, Dbg, "BytesRead -> %08lx\n", irp->IoStatus.Information );
+ DebugTrace(-1, Dbg, "BurstRead -> %08lx\n", status );
+
+ Stats.PacketBurstReadNcps++;
+ return status;
+}
+
+VOID
+BuildBurstReadRequest(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG Handle,
+ IN ULONG FileOffset,
+ IN ULONG Length
+ )
+{
+ PNCP_BURST_READ_REQUEST BurstRead;
+ PNONPAGED_SCB pNpScb;
+ ULONG Temp;
+
+ BurstRead = (PNCP_BURST_READ_REQUEST)(IrpContext->req);
+ pNpScb = IrpContext->pNpScb;
+
+ BurstRead->BurstHeader.Command = PEP_COMMAND_BURST;
+ BurstRead->BurstHeader.Flags = BURST_FLAG_END_OF_BURST;
+ BurstRead->BurstHeader.StreamType = 0x02;
+ BurstRead->BurstHeader.SourceConnection = pNpScb->SourceConnectionId;
+ BurstRead->BurstHeader.DestinationConnection = pNpScb->DestinationConnectionId;
+
+ LongByteSwap( BurstRead->BurstHeader.SendDelayTime, pNpScb->NwReceiveDelay );
+
+ pNpScb->CurrentBurstDelay = pNpScb->NwReceiveDelay;
+
+ Temp = sizeof( NCP_BURST_READ_REQUEST ) - sizeof( NCP_BURST_HEADER );
+ LongByteSwap( BurstRead->BurstHeader.DataSize, Temp);
+
+ BurstRead->BurstHeader.BurstOffset = 0;
+
+ ShortByteSwap( BurstRead->BurstHeader.BurstLength, Temp );
+
+ BurstRead->BurstHeader.MissingFragmentCount = 0;
+
+ BurstRead->Function = 1;
+ BurstRead->Handle = Handle;
+
+ LongByteSwap(
+ BurstRead->TotalReadOffset,
+ IrpContext->Specific.Read.TotalReadOffset );
+
+ LongByteSwap(
+ BurstRead->TotalReadLength,
+ IrpContext->Specific.Read.TotalReadLength );
+
+ LongByteSwap( BurstRead->Offset, FileOffset );
+ LongByteSwap( BurstRead->Length, Length );
+
+ IrpContext->TxMdl->ByteCount = sizeof( NCP_BURST_READ_REQUEST );
+}
+
+#ifdef NWDBG
+int DropReadPackets;
+#endif
+
+
+NTSTATUS
+BurstReadCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+ BytesAvailable - Actual number of bytes in the received message.
+
+ RspData - Points to the receive buffer.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG DataOffset;
+ ULONG TotalBytesRead;
+ PUCHAR ReadData;
+ USHORT BytesThisPacket = 0;
+ UCHAR Flags;
+ KIRQL OldIrql;
+
+ DebugTrace(+1, Dbg, "BurstReadCallback...\n", 0);
+ DebugTrace( 0, Dbg, "IrpContext = %X\n", IrpContext );
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server.
+ //
+
+ IrpContext->Specific.Read.Status = STATUS_REMOTE_NOT_LISTENING;
+ NwSetIrpContextEvent( IrpContext );
+
+ DebugTrace( -1, Dbg, "BurstReadCallback -> %X\n", STATUS_REMOTE_NOT_LISTENING );
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ Stats.PacketBurstReadNcps++;
+
+ if ( Response != IrpContext->rsp ) {
+
+ //
+ // Acquire the SCB spin lock to protect access to the list
+ // of received data for this read.
+ //
+
+ KeAcquireSpinLock( &IrpContext->pNpScb->NpScbSpinLock, &OldIrql );
+
+ Status = ParseBurstReadResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ &Flags,
+ &DataOffset,
+ &BytesThisPacket,
+ &ReadData,
+ &TotalBytesRead );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ IrpContext->Specific.Read.Status = Status;
+ KeReleaseSpinLock( &IrpContext->pNpScb->NpScbSpinLock, OldIrql );
+ return( STATUS_SUCCESS );
+ }
+
+ //
+ // Update the list of data received, and copy the data to the users
+ // buffer.
+ //
+
+ RecordPacketReceipt( IrpContext, ReadData, DataOffset, BytesThisPacket, TRUE );
+ KeReleaseSpinLock( &IrpContext->pNpScb->NpScbSpinLock, OldIrql );
+
+ } else {
+ Flags = IrpContext->Specific.Read.Flags;
+ }
+
+ //
+ // If this isn't the last packet setup for the next burst packet.
+ //
+
+ if ( !FlagOn( Flags, BURST_FLAG_END_OF_BURST ) ) {
+
+ DebugTrace(0, Dbg, "Waiting for another packet\n", 0);
+
+ IrpContext->pNpScb->OkToReceive = TRUE;
+
+ DebugTrace( -1, Dbg, "BurstReadCallback -> %X\n", STATUS_SUCCESS );
+ return( STATUS_SUCCESS );
+ }
+
+ DebugTrace(0, Dbg, "Received final packet\n", 0);
+
+ //
+ // Have we received all of the data? If not, VerifyBurstRead will
+ // send a missing data request.
+ //
+
+ if ( VerifyBurstRead( IrpContext ) ) {
+
+ //
+ // All the data for the current burst has been received, notify
+ // the thread that is sending the data.
+ //
+
+ if (NT_SUCCESS(IrpContext->Specific.Read.Status)) {
+
+ //
+ // If Irp allocation fails then it is possible for the
+ // packet to have been recorded but not copied into the
+ // user buffer. In this case leave the failure status.
+ //
+
+ IrpContext->Specific.Read.Status = STATUS_SUCCESS;
+ }
+
+ NwSetIrpContextEvent( IrpContext );
+
+ }
+
+ DebugTrace( -1, Dbg, "BurstReadCallback -> %X\n", STATUS_SUCCESS );
+ return STATUS_SUCCESS;
+
+}
+
+VOID
+BurstReadTimeout(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine handles a burst read timeout, i.e. no immediate response
+ to the current burst read request. It request to read the packet burst
+ data from the last valid received packet.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ DebugTrace(0, Dbg, "BurstReadTimeout\n", 0 );
+
+ //
+ // Re-request the data we haven't received.
+ //
+
+ if ( !IrpContext->Specific.Read.DataReceived ) {
+
+ DebugTrace( 0, Dbg, "No packets received, retranmit\n", 0 );
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ //
+ // We never received any data. Try retransmitting the previous
+ // request.
+ //
+
+ PreparePacket( IrpContext, IrpContext->pOriginalIrp, IrpContext->TxMdl );
+ SendNow( IrpContext );
+
+ } else {
+
+ IrpContext->Specific.Read.DataReceived = FALSE;
+
+ //
+ // Verify burst read will send a missing data request if one we
+ // have not received all of the data.
+ //
+
+ if ( VerifyBurstRead( IrpContext ) ) {
+ NwSetIrpContextEvent( IrpContext );
+ }
+ }
+
+ Stats.PacketBurstReadTimeouts++;
+}
+
+NTSTATUS
+ResubmitBurstRead (
+ IN PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine handles a rerouted burst read. The burst request is
+ resubmitted on a new burst connection.
+
+Arguments:*
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG Length, DataMdlBytes = 0 ;
+ PMDL DataMdl ;
+
+ DebugTrace( 0, Dbg, "ResubmitBurstRead\n", 0 );
+
+ //
+ // Recalculate the burst size, as MaxReceiveSize may have changed.
+ //
+
+ Length = MIN( IrpContext->pNpScb->MaxReceiveSize,
+ IrpContext->Specific.Read.RemainingLength );
+
+ //
+ // Make sure we dont ask for more than bytes described by MDL
+ //
+ DataMdl = IrpContext->Specific.Read.FullMdl;
+
+ while (DataMdl) {
+
+ DataMdlBytes += MmGetMdlByteCount( DataMdl );
+ DataMdl = DataMdl->Next;
+ }
+
+ Length = MIN( Length, DataMdlBytes ) ;
+
+ DebugTrace( 0, Dbg, "Requesting another burst, length = %ld\n", Length);
+
+ ASSERT( Length != 0 );
+
+ //
+ // Free the packet list, and reset all of the current burst context
+ // information.
+ //
+
+ FreePacketList( IrpContext );
+
+ IrpContext->Specific.Read.LastReadLength = Length;
+ IrpContext->Specific.Read.BurstRequestOffset = 0;
+ IrpContext->Specific.Read.BurstSize = 0;
+ IrpContext->Specific.Read.DataReceived = FALSE;
+
+ SetConnectionTimeout( IrpContext->pNpScb, Length );
+
+ //
+ // Format and send the request.
+ //
+
+ BuildBurstReadRequest(
+ IrpContext,
+ *(ULONG UNALIGNED *)(&IrpContext->Icb->Handle[2]),
+ IrpContext->Specific.Read.FileOffset,
+ Length );
+
+ // Avoid SendNow setting the RetryCount back to the default
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ Status = PrepareAndSendPacket( IrpContext );
+
+ return Status;
+}
+
+VOID
+RecordPacketReceipt(
+ PIRP_CONTEXT IrpContext,
+ PVOID ReadData,
+ ULONG DataOffset,
+ USHORT ByteCount,
+ BOOLEAN CopyData
+ )
+/*++
+
+Routine Description:
+
+ This routine records the reciept of a burst read packet. It allocates
+ a burst read entry to record data start and length, and then inserts
+ the structure in order in the list of packets received for this burst.
+ It then copies the data to the user buffer.
+
+ BUGBUG - This routine could release the spin lock before doing the
+ data copy. Would this be useful?
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ ReadData - A pointer to the data to copy.
+
+ DataOffset - The start offset of the data in the received packet.
+
+ ByteCount - The amount of data received.
+
+ CopyData - If FALSE, don't copy the data to the user's buffer. The
+ transport will do it.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PBURST_READ_ENTRY BurstReadEntry;
+ PBURST_READ_ENTRY ThisBurstReadEntry, NextBurstReadEntry;
+ PLIST_ENTRY ListEntry;
+#if NWDBG
+ BOOLEAN Insert = FALSE;
+#endif
+ USHORT ExtraBytes;
+
+ DebugTrace(0, Dbg, "RecordPacketReceipt\n", 0 );
+
+ IrpContext->Specific.Read.DataReceived = TRUE;
+
+ //
+ // Allocate and initialize a burst read entry.
+ //
+
+ BurstReadEntry = ALLOCATE_POOL( NonPagedPool, sizeof( BURST_READ_ENTRY ) );
+ if ( BurstReadEntry == NULL ) {
+ DebugTrace(0, Dbg, "Failed to allocate BurstReadEntry\n", 0 );
+ return;
+ }
+
+ //
+ // Insert this element in the ordered list of received packets.
+ //
+
+ if ( IsListEmpty( &IrpContext->Specific.Read.PacketList ) ) {
+
+#if NWDBG
+ Insert = TRUE;
+#endif
+
+ InsertHeadList(
+ &IrpContext->Specific.Read.PacketList,
+ &BurstReadEntry->ListEntry );
+
+ DebugTrace(0, Dbg, "First packet in the list\n", 0 );
+
+ } else {
+
+ //
+ // Walk the list of received packets, looking for the place to
+ // insert this entry. Walk the list backwards, since most of
+ // the time we will be appending to the list.
+ //
+
+ ListEntry = IrpContext->Specific.Read.PacketList.Blink;
+ ThisBurstReadEntry = NULL;
+
+ while ( ListEntry != &IrpContext->Specific.Read.PacketList ) {
+
+ NextBurstReadEntry = ThisBurstReadEntry;
+ ThisBurstReadEntry = CONTAINING_RECORD(
+ ListEntry,
+ BURST_READ_ENTRY,
+ ListEntry );
+
+ if ( ThisBurstReadEntry->DataOffset <= DataOffset ) {
+
+ //
+ // Found the place in the list to insert this entry.
+ //
+
+ if ( ThisBurstReadEntry->DataOffset +
+ ThisBurstReadEntry->ByteCount > DataOffset ) {
+
+ //
+ // The start of this packet contains data which
+ // overlaps data we have received. Chuck the extra
+ // data.
+ //
+
+ ExtraBytes = (USHORT)( ThisBurstReadEntry->DataOffset +
+ ThisBurstReadEntry->ByteCount - DataOffset );
+
+ if ( ExtraBytes < ByteCount ) {
+ DataOffset += ExtraBytes;
+ (PCHAR)ReadData += ExtraBytes;
+ ByteCount -= ExtraBytes;
+ } else {
+ ByteCount = 0;
+ }
+
+ }
+
+ if ( NextBurstReadEntry != NULL &&
+ DataOffset + ByteCount > NextBurstReadEntry->DataOffset ) {
+
+ //
+ // This packet contains some new data, but some of it
+ // overlaps the NextBurstReadEntry. Simply ignore
+ // the overlap by adjusting the byte count.
+ //
+ // If the packet is all overlap, toss it.
+ //
+
+ ByteCount = (USHORT)( NextBurstReadEntry->DataOffset - DataOffset );
+ }
+
+ if ( ByteCount == 0 ) {
+ FREE_POOL( BurstReadEntry );
+ return;
+ }
+#if NWDBG
+ Insert = TRUE;
+#endif
+ InsertHeadList( ListEntry, &BurstReadEntry->ListEntry );
+ break;
+
+ } else {
+
+ ListEntry = ListEntry->Blink;
+ }
+ }
+
+ //
+ // Couldn't find the place to insert
+ //
+
+ ASSERT( Insert );
+ }
+
+ BurstReadEntry->DataOffset = DataOffset;
+ BurstReadEntry->ByteCount = ByteCount;
+
+ //
+ // Copy the data to our read buffer.
+ //
+
+ if ( CopyData ) {
+ CopyBufferToMdl(
+ IrpContext->Specific.Read.FullMdl,
+ DataOffset,
+ ReadData,
+ ByteCount );
+ }
+
+ return;
+}
+
+#include <packon.h>
+
+typedef struct _MISSING_DATA_ENTRY {
+ ULONG DataOffset;
+ USHORT ByteCount;
+} MISSING_DATA_ENTRY, *PMISSING_DATA_ENTRY;
+
+#include <packoff.h>
+
+BOOLEAN
+VerifyBurstRead(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine verifies the set of response to a burst read request.
+ If some data is missing a missing packet request is sent.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ TRUE - All the data was received.
+
+ FALSE - Some data was missing.
+
+--*/
+{
+ ULONG CurrentOffset = 0;
+ PLIST_ENTRY ListEntry;
+ PBURST_READ_ENTRY BurstReadEntry;
+ USHORT MissingFragmentCount = 0;
+ USHORT ByteCount;
+ ULONG DataOffset;
+ MISSING_DATA_ENTRY UNALIGNED *MissingDataEntry;
+ KIRQL OldIrql;
+
+ DebugTrace(+1, Dbg, "VerifyBurstRead\n", 0 );
+
+ //
+ // Acquire the SCB spin lock to protect access to the list
+ // of received data for this read.
+ //
+
+ KeAcquireSpinLock(&IrpContext->pNpScb->NpScbSpinLock, &OldIrql);
+
+#ifdef NWDBG
+ //
+ // Verify that the list is in order.
+ //
+
+ ListEntry = IrpContext->Specific.Read.PacketList.Flink;
+
+ while ( ListEntry != &IrpContext->Specific.Read.PacketList ) {
+
+ BurstReadEntry = CONTAINING_RECORD( ListEntry, BURST_READ_ENTRY, ListEntry );
+ ASSERT ( BurstReadEntry->DataOffset >= CurrentOffset);
+ CurrentOffset = BurstReadEntry->DataOffset + BurstReadEntry->ByteCount;
+ ListEntry = ListEntry->Flink;
+ }
+
+ CurrentOffset = 0;
+
+#endif
+
+ ListEntry = IrpContext->Specific.Read.PacketList.Flink;
+
+ while ( ListEntry != &IrpContext->Specific.Read.PacketList ) {
+
+ BurstReadEntry = CONTAINING_RECORD( ListEntry, BURST_READ_ENTRY, ListEntry );
+ if ( BurstReadEntry->DataOffset != CurrentOffset) {
+
+ //
+ // There is a hole in the data, fill in a missing packet entry.
+ //
+
+ MissingDataEntry = (MISSING_DATA_ENTRY UNALIGNED *)
+ &IrpContext->req[ sizeof( NCP_BURST_HEADER ) +
+ MissingFragmentCount * sizeof( MISSING_DATA_ENTRY ) ];
+
+ DataOffset = CurrentOffset + SIZE_ADJUST( IrpContext );
+ LongByteSwap( MissingDataEntry->DataOffset, DataOffset );
+
+ ByteCount = (USHORT)( BurstReadEntry->DataOffset - CurrentOffset );
+ ShortByteSwap( MissingDataEntry->ByteCount, ByteCount );
+
+ ASSERT( BurstReadEntry->DataOffset - CurrentOffset <= IrpContext->pNpScb->MaxReceiveSize );
+
+ DebugTrace(0, Dbg, "Missing data at offset %ld\n", DataOffset );
+ DebugTrace(0, Dbg, "Missing %d bytes\n", ByteCount );
+ DebugTrace(0, Dbg, "CurrentOffset: %d\n", CurrentOffset );
+
+ MissingFragmentCount++;
+ }
+
+ CurrentOffset = BurstReadEntry->DataOffset + BurstReadEntry->ByteCount;
+ ListEntry = ListEntry->Flink;
+ }
+
+ //
+ // Any data missing off the end?
+ //
+
+ if ( CurrentOffset <
+ IrpContext->Specific.Read.BurstSize ) {
+
+ //
+ // There is a hole in the data, fill in a missing packet entry.
+ //
+
+ MissingDataEntry = (PMISSING_DATA_ENTRY)
+ &IrpContext->req[ sizeof( NCP_BURST_HEADER ) +
+ MissingFragmentCount * sizeof( MISSING_DATA_ENTRY ) ];
+
+ DataOffset = CurrentOffset + SIZE_ADJUST( IrpContext );
+ LongByteSwap( MissingDataEntry->DataOffset, DataOffset );
+
+ ByteCount = (USHORT)( IrpContext->Specific.Read.BurstSize - CurrentOffset );
+ ShortByteSwap( MissingDataEntry->ByteCount, ByteCount );
+
+ ASSERT( IrpContext->Specific.Read.BurstSize - CurrentOffset < IrpContext->pNpScb->MaxReceiveSize );
+
+ DebugTrace(0, Dbg, "Missing data at offset %ld\n", MissingDataEntry->DataOffset );
+ DebugTrace(0, Dbg, "Missing %d bytes\n", MissingDataEntry->ByteCount );
+
+ MissingFragmentCount++;
+ }
+
+
+ if ( MissingFragmentCount == 0 ) {
+
+ //
+ // This read is now complete. Don't process any more packets until
+ // the next packet is sent.
+ //
+
+ IrpContext->pNpScb->OkToReceive = FALSE;
+
+ KeReleaseSpinLock(&IrpContext->pNpScb->NpScbSpinLock, OldIrql);
+
+ DebugTrace(-1, Dbg, "VerifyBurstRead -> TRUE\n", 0 );
+
+ return( TRUE );
+
+ } else {
+
+ KeReleaseSpinLock(&IrpContext->pNpScb->NpScbSpinLock, OldIrql);
+
+ //
+ // The server dropped a packet, adjust the timers.
+ //
+
+ NwProcessReceiveBurstFailure( IrpContext->pNpScb, MissingFragmentCount );
+
+ //
+ // Request the missing data.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_BURST_PACKET );
+
+ //
+ // Update burst request offset since we are about to request
+ // more data. Note that this will reset the retry count,
+ // thus giving the server full timeout time to return the
+ // missing data.
+ //
+
+ BuildRequestPacket(
+ IrpContext,
+ BurstReadCallback,
+ "Bws",
+ 0, // Frame size for this request is 0
+ 0, // Offset of data
+ BURST_FLAG_SYSTEM_PACKET,
+ MissingFragmentCount,
+ MissingFragmentCount * sizeof( MISSING_DATA_ENTRY )
+ );
+
+ PrepareAndSendPacket( IrpContext );
+
+ Stats.PacketBurstReadTimeouts++;
+
+ DebugTrace(-1, Dbg, "VerifyBurstRead -> FALSE\n", 0 );
+ return( FALSE );
+ }
+}
+
+
+VOID
+FreePacketList(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine frees the received packet list for a burst read.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PLIST_ENTRY ListHead;
+ PBURST_READ_ENTRY BurstReadEntry;
+
+ ListHead = &IrpContext->Specific.Read.PacketList;
+ while ( !IsListEmpty( ListHead ) ) {
+ BurstReadEntry = CONTAINING_RECORD( ListHead->Flink, BURST_READ_ENTRY, ListEntry );
+ RemoveHeadList( ListHead );
+ FREE_POOL( BurstReadEntry );
+ }
+}
+
+NTSTATUS
+BurstReadReceive(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PULONG BytesAccepted,
+ IN PUCHAR Response,
+ PMDL *pReceiveMdl
+ )
+/*++
+
+Routine Description:
+
+ This routine builds an MDL to receive burst read data. This routine
+ is called at data indication time.
+
+ This routine is called with the non paged SCB spin lock held.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ BytesAvailable - The number of bytes in the entire packet.
+
+ BytesAccepted - Returns the number of bytes accepted from the packet.
+
+ Response - A pointer to the indication buffer.
+
+Return Value:
+
+ Mdl - An MDL to receive the data.
+ This routine raise an exception if it cannot receive the data.
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG DataOffset;
+ ULONG TotalBytesRead;
+ PUCHAR ReadData;
+ USHORT BytesThisPacket;
+ UCHAR Flags;
+ PMDL PartialMdl;
+
+ DebugTrace(0, Dbg, "Burst read receive\n", 0);
+
+ Status = ParseBurstReadResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ &Flags,
+ &DataOffset,
+ &BytesThisPacket,
+ &ReadData,
+ &TotalBytesRead );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace(0, Dbg, "Failed to parse burst read response\n", 0);
+ return Status;
+ }
+
+ //
+ // We can accept up to the size of a burst read header, plus
+ // 3 bytes of fluff for the unaligned read case.
+ //
+
+ *BytesAccepted = ReadData - Response;
+ ASSERT( *BytesAccepted <= sizeof(NCP_BURST_READ_RESPONSE) + 3 );
+
+ RecordPacketReceipt( IrpContext, ReadData, DataOffset, BytesThisPacket, FALSE );
+
+ IrpContext->Specific.Read.Flags = Flags;
+
+ //
+ // If we did a read at EOF the netware server will return 0 bytes read,
+ // no error.
+ //
+
+ ASSERT( IrpContext->Specific.Read.FullMdl != NULL );
+
+ if ( BytesThisPacket > 0 ) {
+
+ PartialMdl = AllocateReceivePartialMdl(
+ IrpContext->Specific.Read.FullMdl,
+ DataOffset,
+ BytesThisPacket );
+
+ if ( !PartialMdl ) {
+ IrpContext->Specific.Read.Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ // Record Mdl to free when CopyIndicatedData or Irp completed.
+ IrpContext->Specific.Read.PartialMdl = PartialMdl;
+
+ } else {
+
+ PartialMdl = NULL;
+
+ }
+
+ *pReceiveMdl = PartialMdl;
+ return( STATUS_SUCCESS );
+}
+
+NTSTATUS
+ParseBurstReadResponse(
+ IN PIRP_CONTEXT IrpContext,
+ PUCHAR Response,
+ ULONG BytesAvailable,
+ PUCHAR Flags,
+ PULONG DataOffset,
+ PUSHORT BytesThisPacket,
+ PUCHAR *ReadData,
+ PULONG TotalBytesRead
+ )
+/*++
+
+Routine Description:
+
+ This routine parses a burst read response.
+
+ This routine must be called the the nonpagd SCB spinlock held.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ Response - A pointer to the response buffer.
+
+ BytesAvailable - The number of bytes in the packet.
+
+ Flags - Returns the Burst Flags
+
+ DataOffset - Returns the data offset (within the burst) of the
+ data in this packet.
+
+ BytesThisPacket - Returns the number of file data bytes in this packet.
+
+ ReadData - Returns a pointer to the start of the file data in the
+ packet buffer.
+
+ TotalBytesRead - Returns the number of byte of file data in the
+ entire burst.
+
+
+Return Value:
+
+ The status of the read.
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG Result;
+ PNCP_BURST_READ_RESPONSE ReadResponse;
+
+ DebugTrace(+1, Dbg, "ParseBurstReadResponse\n", 0);
+
+ ReadResponse = (PNCP_BURST_READ_RESPONSE)Response;
+ *Flags = ReadResponse->BurstHeader.Flags;
+
+#ifdef NWDBG
+ //
+ // Bad net simulator.
+ //
+
+ if ( DropReadPackets != 0 ) {
+ if ( ( rand() % DropReadPackets ) == 0 ) {
+
+ IrpContext->pNpScb->OkToReceive = TRUE;
+ DebugTrace( 0, Dbg, "Dropping packet\n", 0 );
+ DebugTrace( -1, Dbg, "ParseBurstReadResponse -> %X\n", STATUS_UNSUCCESSFUL );
+ return ( STATUS_UNSUCCESSFUL );
+ }
+ }
+
+#endif
+
+ //
+ // If this isn't the last packet, setup for the next burst packet.
+ //
+
+ if ( !FlagOn( *Flags, BURST_FLAG_END_OF_BURST ) ) {
+
+ DebugTrace(0, Dbg, "Waiting for another packet\n", 0);
+
+ //
+ // Once we receive the first packet in a read response be aggresive
+ // about timing out while waiting for the rest of the burst.
+ //
+
+ IrpContext->pNpScb->TimeOut = IrpContext->pNpScb->SendTimeout ;
+
+ IrpContext->pNpScb->OkToReceive = TRUE;
+ }
+
+
+ LongByteSwap( *DataOffset, ReadResponse->BurstHeader.BurstOffset );
+ ShortByteSwap( *BytesThisPacket, ReadResponse->BurstHeader.BurstLength );
+
+ //
+ // How much data was received?
+ //
+
+ if ( IsListEmpty( &IrpContext->Specific.Read.PacketList ) ) {
+
+ DebugTrace(0, Dbg, "Expecting initial response\n", 0);
+
+ //
+ // This is the initial burst response packet.
+ //
+
+ if ( *DataOffset != 0 ) {
+
+ DebugTrace(0, Dbg, "Invalid initial response tossed\n", 0);
+
+ //
+ // This is actually a subsequent response. Toss it.
+ // BUGBUG - Can we handle it?
+ //
+
+ DebugTrace( -1, Dbg, "ParseBurstReadResponse -> %X\n", STATUS_UNSUCCESSFUL );
+ IrpContext->pNpScb->OkToReceive = TRUE;
+
+ return ( STATUS_UNSUCCESSFUL );
+ }
+
+ Result = ReadResponse->Result;
+ LongByteSwap( *TotalBytesRead, ReadResponse->BytesRead );
+
+ Status = NwBurstResultToNtStatus( Result );
+ IrpContext->Specific.Read.Status = Status;
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // Update the burst request number now.
+ //
+
+ DebugTrace(0, Dbg, "Read completed, error = %X\n", Status );
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_BURST_REQUEST );
+ NwSetIrpContextEvent( IrpContext );
+
+ DebugTrace( -1, Dbg, "ParseBurstReadResponse -> %X\n", Status );
+ return( Status );
+ }
+
+ if ( Result == 3 || *BytesThisPacket < 8 ) { // No data
+ *TotalBytesRead = 0;
+ *BytesThisPacket = 8;
+ }
+
+ *ReadData = Response + sizeof(NCP_BURST_READ_RESPONSE);
+
+ IrpContext->Specific.Read.BurstSize = *TotalBytesRead;
+
+ //
+ // Bytes this packet includes a LONG status and a LONG byte total.
+ // Adjust the count to reflect the number of data bytes actually
+ // shipped.
+ //
+
+ *BytesThisPacket -= sizeof( ULONG ) + sizeof( ULONG );
+
+ //
+ // Adjust this data if the read was not DWORD aligned.
+ //
+
+ if ( (IrpContext->Specific.Read.FileOffset & 0x03) != 0
+ && *BytesThisPacket != 0 ) {
+
+ *ReadData += IrpContext->Specific.Read.FileOffset & 0x03;
+ *BytesThisPacket -= (USHORT)IrpContext->Specific.Read.FileOffset & 0x03;
+ }
+
+ DebugTrace(0, Dbg, "Initial response\n", 0);
+ DebugTrace(0, Dbg, "Result = %ld\n", Result);
+ DebugTrace(0, Dbg, "Total bytes read = %ld\n", *TotalBytesRead );
+
+ } else {
+
+ //
+ // Intermediate response packet.
+ //
+
+ *ReadData = Response + sizeof( NCP_BURST_HEADER );
+ *DataOffset -= SIZE_ADJUST( IrpContext );
+
+ }
+
+ DebugTrace(0, Dbg, "DataOffset = %ld\n", *DataOffset );
+ DebugTrace(0, Dbg, "# bytes received = %d\n", *BytesThisPacket );
+
+ if ( *DataOffset > IrpContext->Specific.Read.BurstSize ||
+ *DataOffset + *BytesThisPacket > IrpContext->Specific.Read.BurstSize ) {
+
+ DebugTrace(0, Dbg, "Invalid response tossed\n", 0);
+
+ DebugTrace( -1, Dbg, "ParseBurstReadResponse -> %X\n", STATUS_SUCCESS );
+ IrpContext->pNpScb->OkToReceive = TRUE;
+ return ( STATUS_UNSUCCESSFUL );
+ }
+
+ DebugTrace( -1, Dbg, "ParseBurstReadResponse -> %X\n", STATUS_SUCCESS );
+ return( STATUS_SUCCESS );
+}
+
+
+PMDL
+AllocateReceivePartialMdl(
+ PMDL FullMdl,
+ ULONG DataOffset,
+ ULONG BytesThisPacket
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates a partial MDL to receive read data. This
+ routine is called at receive indication time.
+
+Arguments:
+
+ FullMdl - The FullMdl for the buffer.
+
+ DataOffset - The offset into the buffer where the data is to be received.
+
+ BytesThisPacket - The number of data bytes to be received into the buffer.
+
+Return Value:
+
+ MDL - A pointer to an MDL to receive the data
+ This routine raises an exception if it cannot allocate an MDL.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PUCHAR BufferStart, BufferEnd;
+ PMDL InitialMdl, NextMdl;
+ PMDL ReceiveMdl, PreviousReceiveMdl;
+ ULONG BytesThisMdl;
+
+ BufferStart = (PUCHAR)MmGetMdlVirtualAddress( FullMdl ) + DataOffset;
+ BufferEnd = (PUCHAR)MmGetMdlVirtualAddress( FullMdl ) +
+ MmGetMdlByteCount( FullMdl );
+
+ //
+ // Walk the MDL chain look for the MDL for the actual buffer for the
+ // start of this data.
+ //
+
+ while ( BufferStart >= BufferEnd ) {
+ DataOffset -= MmGetMdlByteCount( FullMdl );
+ FullMdl = FullMdl->Next;
+
+ //
+ // if more data than expected, dont dereference NULL! see next loop.
+ //
+ if (!FullMdl) {
+ ASSERT(FALSE) ;
+ break ;
+ }
+
+ BufferStart = (PUCHAR)MmGetMdlVirtualAddress( FullMdl ) + DataOffset;
+ BufferEnd = (PUCHAR)MmGetMdlVirtualAddress( FullMdl ) +
+ MmGetMdlByteCount( FullMdl );
+ }
+
+ PreviousReceiveMdl = NULL;
+ InitialMdl = NULL;
+ BytesThisMdl = (ULONG)(BufferEnd - BufferStart);
+
+ //
+ // Check FullMdl to cover the case where the server returns more data
+ // than requested.
+ //
+
+ while (( BytesThisPacket != 0 ) &&
+ ( FullMdl != NULL )) {
+
+ BytesThisMdl = MIN( BytesThisMdl, BytesThisPacket );
+
+ //
+ // Some of the data fits in the first part of the MDL;
+ //
+
+ ReceiveMdl = ALLOCATE_MDL(
+ BufferStart,
+ BytesThisMdl,
+ FALSE,
+ FALSE,
+ NULL );
+
+ if ( ReceiveMdl == NULL ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ if ( InitialMdl == NULL ) {
+ InitialMdl = ReceiveMdl;
+ }
+
+ IoBuildPartialMdl(
+ FullMdl,
+ ReceiveMdl,
+ BufferStart,
+ BytesThisMdl );
+
+ if ( PreviousReceiveMdl != NULL ) {
+ PreviousReceiveMdl->Next = ReceiveMdl;
+ }
+
+ PreviousReceiveMdl = ReceiveMdl;
+
+ BytesThisPacket -= BytesThisMdl;
+
+ FullMdl = FullMdl->Next;
+
+ if ( FullMdl != NULL) {
+ BytesThisMdl = MmGetMdlByteCount( FullMdl );
+ BufferStart = MmGetMdlVirtualAddress( FullMdl );
+ }
+
+ }
+
+ if ( Status == STATUS_INSUFFICIENT_RESOURCES ) {
+
+ //
+ // Cleanup allocated MDLs
+ //
+
+ while ( InitialMdl != NULL ) {
+ NextMdl = InitialMdl->Next;
+ FREE_MDL( InitialMdl );
+ InitialMdl = NextMdl;
+ }
+
+ DebugTrace( 0, Dbg, "AllocateReceivePartialMdl Failed\n", 0 );
+ }
+
+ DebugTrace( 0, Dbg, "AllocateReceivePartialMdl -> %08lX\n", InitialMdl );
+ return( InitialMdl );
+}
+
+
+VOID
+SetConnectionTimeout(
+ PNONPAGED_SCB pNpScb,
+ ULONG Length
+ )
+/*++
+
+Routine Description:
+
+
+ The server will pause NwReceiveDelay between packets. Make sure we have our timeout
+ so that we will take that into account.
+
+Arguments:
+
+ pNpScb - Connection
+
+ Length - Length of the burst in bytes
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ ULONG TimeInNwUnits;
+ LONG SingleTimeInNwUnits;
+
+ SingleTimeInNwUnits = pNpScb->NwSingleBurstPacketTime + pNpScb->NwReceiveDelay;
+
+ TimeInNwUnits = SingleTimeInNwUnits * ((Length / pNpScb->MaxPacketSize) + 1) +
+ pNpScb->NwLoopTime;
+
+ //
+ // Convert to 1/18ths of a second ticks and multiply by a fudge
+ // factor. The fudge factor is expressed as a percentage. 100 will
+ // mean no fudge.
+ //
+
+ pNpScb->MaxTimeOut = (SHORT)( ((TimeInNwUnits / 555) *
+ (ULONG)ReadTimeoutMultiplier) / 100 + 1);
+
+ //
+ // Now make sure we have a meaningful lower and upper limit.
+ //
+ if (pNpScb->MaxTimeOut < 2)
+ {
+ pNpScb->MaxTimeOut = 2 ;
+ }
+
+ if (pNpScb->MaxTimeOut > (SHORT)MaxReadTimeout)
+ {
+ pNpScb->MaxTimeOut = (SHORT)MaxReadTimeout ;
+ }
+
+ pNpScb->TimeOut = pNpScb->SendTimeout = pNpScb->MaxTimeOut;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->MaxTimeout = %08lx\n", pNpScb->MaxTimeOut );
+}
+
+#if NWFASTIO
+
+BOOLEAN
+NwFastRead (
+ 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 cached read bypassing the usual file system
+ entry routine (i.e., without the Irp). It is used to do a copy read
+ of a cached file object. For a complete description of the arguments
+ see CcCopyRead.
+
+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
+
+ 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:
+
+ FALSE - if Wait was supplied as FALSE and the data was not delivered, or
+ if there is an I/O error.
+
+ TRUE - if the data is being delivered
+
+--*/
+
+{
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PFCB fcb;
+ PVOID fsContext;
+ ULONG bytesRead;
+ ULONG offset;
+
+ DebugTrace(+1, Dbg, "NwFastRead...\n", 0);
+
+ //
+ // Special case a read of zero length
+ //
+
+ if (Length == 0) {
+
+ //
+ // A zero length transfer was requested.
+ //
+
+ IoStatus->Status = STATUS_SUCCESS;
+ IoStatus->Information = 0;
+
+ DebugTrace(+1, Dbg, "NwFastRead -> TRUE\n", 0);
+ return TRUE;
+ }
+
+ //
+ // Decode the file object to figure out who we are. If the result
+ // is not FCB then its an illegal parameter.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( FileObject,
+ &fsContext,
+ (PVOID *)&icb )) != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+ DebugTrace(-1, Dbg, "NwFastRead -> FALSE\n", 0);
+ return FALSE;
+ }
+
+ fcb = (PFCB)icb->SuperType.Fcb;
+ nodeTypeCode = fcb->NodeTypeCode;
+ offset = FileOffset->LowPart;
+
+ bytesRead = CacheRead(
+ fcb->NonPagedFcb,
+ offset,
+ Length,
+ Buffer,
+ TRUE );
+
+ if ( bytesRead != 0 ) {
+
+ ASSERT( bytesRead == Length );
+ IoStatus->Status = STATUS_SUCCESS;
+ IoStatus->Information = bytesRead;
+#ifndef NT1057
+ FileObject->CurrentByteOffset.QuadPart += Length;
+#endif
+ DebugTrace(-1, Dbg, "NwFastRead -> TRUE\n", 0);
+ return( TRUE );
+
+ } else {
+
+ DebugTrace(-1, Dbg, "NwFastRead -> FALSE\n", 0);
+ return( FALSE );
+
+ }
+}
+#endif