diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/nw/rdr/read.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to '')
-rw-r--r-- | private/nw/rdr/read.c | 2838 |
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 |