summaryrefslogtreecommitdiffstats
path: root/private/nw/rdr/write.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/rdr/write.c')
-rw-r--r--private/nw/rdr/write.c3063
1 files changed, 3063 insertions, 0 deletions
diff --git a/private/nw/rdr/write.c b/private/nw/rdr/write.c
new file mode 100644
index 000000000..60d6a2cd5
--- /dev/null
+++ b/private/nw/rdr/write.c
@@ -0,0 +1,3063 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Write.c
+
+Abstract:
+
+ This module implements support for NtWriteFile for the
+ NetWare redirector called by the dispatch driver.
+
+Author:
+
+ Colin Watson [ColinW] 07-Apr-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#include <stdlib.h>
+
+//
+// The local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_WRITE)
+
+//
+// The header overhead in the first packet of a burst write.
+//
+
+#define BURST_WRITE_HEADER_SIZE \
+ ( sizeof( NCP_BURST_WRITE_REQUEST ) - sizeof( NCP_BURST_HEADER ) )
+
+//
+// Local procedure prototypes
+//
+
+NTSTATUS
+NwCommonWrite (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+WriteNcp(
+ PIRP_CONTEXT IrpContext,
+ LARGE_INTEGER ByteOffset,
+ ULONG BufferLength,
+ PVOID WriteBuffer,
+ PMDL WriteMdl
+ );
+
+NTSTATUS
+QueryEofForWriteCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+WriteNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+BurstWrite(
+ PIRP_CONTEXT IrpContext,
+ LARGE_INTEGER ByteOffset,
+ ULONG BufferLength,
+ PVOID WriteBuffer,
+ PMDL WriteMdl
+ );
+
+NTSTATUS
+SendWriteBurst(
+ PIRP_CONTEXT IrpContext,
+ ULONG Offset,
+ USHORT Length,
+ BOOLEAN EndOfBurst,
+ BOOLEAN Retransmission
+ );
+
+VOID
+BuildBurstWriteFirstReq(
+ PIRP_CONTEXT IrpContext,
+ PVOID Buffer,
+ ULONG DataSize,
+ PMDL BurstMdl,
+ UCHAR Flags,
+ ULONG Handle,
+ ULONG FileOffset
+ );
+
+VOID
+BuildBurstWriteNextReq(
+ PIRP_CONTEXT IrpContext,
+ PVOID Buffer,
+ ULONG DataSize,
+ UCHAR BurstFlags,
+ ULONG BurstOffset,
+ PMDL BurstHeaderMdl,
+ PMDL BurstDataMdl
+ );
+
+NTSTATUS
+BurstWriteCompletionSend(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+NTSTATUS
+BurstWriteCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+VOID
+BurstWriteTimeout(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+BurstWriteReconnect(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+NwCommonFlushBuffers (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+FlushBuffersCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+SendSecondaryPacket(
+ PIRP_CONTEXT IrpContext,
+ PIRP Irp
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdWrite )
+#pragma alloc_text( PAGE, NwCommonWrite )
+#pragma alloc_text( PAGE, DoWrite )
+#pragma alloc_text( PAGE, WriteNcp )
+#pragma alloc_text( PAGE, BurstWrite )
+#pragma alloc_text( PAGE, SendWriteBurst )
+#pragma alloc_text( PAGE, ResubmitBurstWrite )
+#pragma alloc_text( PAGE, NwFsdFlushBuffers )
+#pragma alloc_text( PAGE, NwCommonFlushBuffers )
+#pragma alloc_text( PAGE, BuildBurstWriteFirstReq )
+#pragma alloc_text( PAGE, BuildBurstWriteNextReq )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, WriteNcpCallback )
+#pragma alloc_text( PAGE1, BurstWriteCompletionSend )
+#pragma alloc_text( PAGE1, BurstWriteCallback )
+#pragma alloc_text( PAGE1, BurstWriteTimeout )
+#pragma alloc_text( PAGE1, FlushBuffersCallback )
+#pragma alloc_text( PAGE1, SendSecondaryPacket )
+#pragma alloc_text( PAGE1, BurstWriteReconnect )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+NwFsdWrite(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the FSD routine that handles NtWriteFile.
+
+Arguments:
+
+ NwfsDeviceObject - Supplies the device object for the write 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, "NwFsdWrite\n", 0);
+
+ //
+ // Call the common write routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonWrite( 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, "NwFsdWrite -> %08lx\n", status );
+
+ Stats.WriteOperations++;
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonWrite (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine does the common code for NtWriteFile.
+
+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;
+ PNONPAGED_FCB pNpFcb;
+ PVOID fsContext;
+
+ BOOLEAN WroteToCache;
+ LARGE_INTEGER ByteOffset;
+ LARGE_INTEGER PreviousByteOffset;
+ ULONG BufferLength;
+
+ PULONG pFileSize;
+
+ // ULONG FileLength;
+
+ PAGED_CODE();
+
+ //
+ // Get the current stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "CommonWrite...\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, "CommonWrite -> %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;
+ pFileSize = &icb->NpFcb->Header.FileSize.LowPart;
+
+ } else if ( fcb->NodeTypeCode == NW_NTC_SCB ) {
+
+ IrpContext->pScb = icb->SuperType.Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = icb;
+ fcb = NULL;
+ pFileSize = &icb->FileSize;
+
+ } else {
+
+ DebugTrace(0, Dbg, "Not a file or a server\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonWrite -> %08lx\n", status );
+ return status;
+ }
+
+ ByteOffset = irpSp->Parameters.Write.ByteOffset;
+ BufferLength = irpSp->Parameters.Write.Length;
+
+ //
+ // Can't handle large byte offset, but write to EOF is okay.
+ //
+
+ if ( ByteOffset.HighPart != 0 ) {
+
+ if ( ByteOffset.HighPart != 0xFFFFFFFF ||
+ ByteOffset.LowPart != 0xFFFFFFFF ) {
+
+ return( STATUS_INVALID_PARAMETER );
+ }
+ }
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
+
+ PreviousByteOffset.QuadPart = irpSp->FileObject->CurrentByteOffset.QuadPart;
+ irpSp->FileObject->CurrentByteOffset.QuadPart = ByteOffset.QuadPart;
+ }
+
+ //
+ // Paging I/O is not allowed to extend the file
+ //
+
+ if ((FlagOn(Irp->Flags, IRP_PAGING_IO)) &&
+ (ByteOffset.LowPart + BufferLength > *pFileSize )) {
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ if ( ByteOffset.LowPart + BufferLength <= *pFileSize ) {
+
+ //
+ // Someone else extended the file. Do nothing.
+ //
+
+ // continue;
+
+ } else if ( ByteOffset.LowPart > *pFileSize ) {
+
+ //
+ // Whole write is off the end of the buffer
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ Irp->IoStatus.Information = 0;
+ return( STATUS_SUCCESS );
+
+ } else {
+
+ //
+ // Truncate request to size of file
+ //
+
+ BufferLength = *pFileSize - ByteOffset.LowPart;
+
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+
+ //
+ // Special case 0 length write.
+ //
+
+ if ( BufferLength == 0 ) {
+ Irp->IoStatus.Information = 0;
+ return( STATUS_SUCCESS );
+ }
+
+ //
+ // Remember the original MDL, so that we can restore it when we are done.
+ //
+
+ IrpContext->pOriginalMdlAddress = Irp->MdlAddress;
+
+ //
+ // Attempt to write this data to our private cache
+ //
+ // BUGBUG - Cheap fix, don't process MDL based writes. Fix up
+ // post Daytona beta.
+ //
+
+ if ( fcb != NULL && Irp->UserBuffer != NULL ) {
+
+ WroteToCache = CacheWrite(
+ IrpContext,
+ fcb->NonPagedFcb,
+ ByteOffset.LowPart,
+ BufferLength,
+ Irp->UserBuffer );
+
+ if ( WroteToCache ) {
+
+ Irp->IoStatus.Information = BufferLength;
+
+ //
+ // 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 += BufferLength;
+ }
+
+ //
+ // Record write offset and size to discover a sequential write pattern.
+ //
+
+ fcb->LastReadOffset = irpSp->Parameters.Write.ByteOffset.LowPart;
+ fcb->LastReadSize = irpSp->Parameters.Write.Length;
+
+ //
+ // If the file was extended, record the new file size.
+ //
+
+ if ( fcb->LastReadOffset + fcb->LastReadSize >
+ fcb->NonPagedFcb->Header.FileSize.LowPart ) {
+
+ fcb->NonPagedFcb->Header.FileSize.LowPart =
+ fcb->LastReadOffset + fcb->LastReadSize;
+ }
+
+ DebugTrace(-1, Dbg, "NwCommonWrite -> %08lx\n", STATUS_SUCCESS );
+ return( STATUS_SUCCESS );
+ }
+
+ }
+
+ status = DoWrite(
+ IrpContext,
+ ByteOffset,
+ BufferLength,
+ Irp->UserBuffer,
+ IrpContext->pOriginalMdlAddress );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ //
+ // We actually wrote something out to the wire. If there was a read
+ // cache and this write overlapped it, invalidate the read cache data
+ // so that we get good data on future reads.
+ //
+
+ if ( fcb != NULL ) {
+
+ pNpFcb = fcb->NonPagedFcb;
+
+ if ( ( pNpFcb->CacheBuffer != NULL ) &&
+ ( pNpFcb->CacheSize != 0 ) &&
+ ( pNpFcb->CacheType == ReadAhead ) ) {
+
+ //
+ // Two cases: (1) offset is less than cache offset
+ // (2) offset is inside cached region
+ //
+
+ if ( ByteOffset.LowPart < pNpFcb->CacheFileOffset ) {
+
+ //
+ // Did we run into the read cache?
+ //
+
+ if ( BufferLength >
+ (pNpFcb->CacheFileOffset - ByteOffset.LowPart) ) {
+
+ DebugTrace( 0, Dbg, "Invalidated read cache for %08lx.\n", pNpFcb );
+ pNpFcb->CacheDataSize = 0;
+
+ }
+
+ } else {
+
+ //
+ // Did we write over any of the cached region.
+ //
+
+ if ( ByteOffset.LowPart <= ( pNpFcb->CacheFileOffset + pNpFcb->CacheDataSize ) ) {
+
+ DebugTrace( 0, Dbg, "Invalidated read cache for %08lx.\n", pNpFcb );
+ pNpFcb->CacheDataSize = 0;
+
+ }
+ }
+ }
+
+ }
+
+ Irp->IoStatus.Information = IrpContext->Specific.Write.WriteOffset;
+
+ //
+ // 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 += BufferLength;
+ }
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ if (ByteOffset.LowPart + BufferLength > *pFileSize ) {
+
+ *pFileSize = ByteOffset.LowPart + BufferLength;
+
+ }
+
+ } else {
+
+ //
+ // The request failed, don't move the file pointer.
+ //
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
+
+ irpSp->FileObject->CurrentByteOffset.QuadPart = PreviousByteOffset.QuadPart;
+ }
+
+ }
+
+ DebugTrace(-1, Dbg, "CommonWrite -> %08lx\n", status);
+
+ return status;
+}
+
+NTSTATUS
+DoWrite(
+ PIRP_CONTEXT IrpContext,
+ LARGE_INTEGER ByteOffset,
+ ULONG BufferLength,
+ PVOID WriteBuffer,
+ PMDL WriteMdl OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This routine does a write to the network via the most efficient
+ available protocol.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ ByteOffset - The file offset to write.
+
+ BufferLength - The number of bytes to write.
+
+ WriteBuffer - A pointer to the source buffer.
+
+ WriteMdl = An optional MDL for the write buffer.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ if ( IrpContext->pNpScb->SendBurstModeEnabled &&
+ BufferLength > IrpContext->pNpScb->BufferSize ) {
+ status = BurstWrite( IrpContext, ByteOffset, BufferLength, WriteBuffer, WriteMdl );
+ } else {
+ status = WriteNcp( IrpContext, ByteOffset, BufferLength, WriteBuffer, WriteMdl );
+ }
+
+ //
+ // Reset IrpContext parameters
+ //
+
+ IrpContext->TxMdl->Next = NULL;
+ IrpContext->CompletionSendRoutine = NULL;
+ IrpContext->TimeoutRoutine = NULL;
+ IrpContext->Flags &= ~(IRP_FLAG_RETRY_SEND | IRP_FLAG_BURST_REQUEST | IRP_FLAG_BURST_PACKET |
+ IRP_FLAG_BURST_WRITE | IRP_FLAG_NOT_SYSTEM_PACKET );
+ IrpContext->pTdiStruct = NULL;
+
+ IrpContext->pOriginalIrp->MdlAddress = IrpContext->pOriginalMdlAddress;
+ IrpContext->pOriginalIrp->AssociatedIrp.SystemBuffer = IrpContext->pOriginalSystemBuffer;
+
+ return( status );
+}
+
+NTSTATUS
+WriteNcp(
+ PIRP_CONTEXT IrpContext,
+ LARGE_INTEGER ByteOffset,
+ ULONG BufferLength,
+ PVOID WriteBuffer,
+ PMDL WriteMdl
+ )
+/*++
+
+Routine Description:
+
+ This routine exchanges a series of write 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.
+
+--*/
+{
+ PICB Icb;
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG Length; // Size we will send to the server
+ ULONG FileLength;
+
+ PSCB pScb;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PMDL DataMdl;
+ BOOLEAN Done;
+
+ PAGED_CODE();
+
+ Icb = IrpContext->Icb;
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ DebugTrace(+1, Dbg, "WriteNcp...\n", 0);
+ DebugTrace( 0, Dbg, "irp = %08lx\n", (ULONG)irp);
+ DebugTrace( 0, Dbg, "WriteLen= %ld\n", BufferLength);
+ DebugTrace( 0, Dbg, "HOffset = %lx\n", ByteOffset.HighPart);
+ DebugTrace( 0, Dbg, "LOffset = %lx\n", ByteOffset.LowPart);
+
+ if (Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_FCB) {
+ pScb = Icb->SuperType.Fcb->Scb;
+ DebugTrace( 0, Dbg, "File = %wZ\n", &Icb->SuperType.Fcb->FullFileName);
+ } else {
+
+ //
+ // Write to a queue
+ //
+
+ pScb = Icb->SuperType.Scb;
+
+ }
+
+ ASSERT (pScb->NodeTypeCode == NW_NTC_SCB);
+
+ if ( ByteOffset.HighPart == 0xFFFFFFFF &&
+ ByteOffset.LowPart == FILE_WRITE_TO_END_OF_FILE ) {
+
+ //
+ // Write relative to end of file. Find the end of file.
+ //
+
+ status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_GET_FILE_SIZE,
+ &Icb->Handle, sizeof( Icb->Handle ) );
+
+ if ( NT_SUCCESS( status ) ) {
+ status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nd",
+ &FileLength );
+
+ if ( !NT_SUCCESS( status ) ) {
+ return status;
+ }
+
+ }
+
+ IrpContext->Specific.Write.FileOffset = FileLength;
+ }
+
+ Length = MIN( (ULONG)IrpContext->pNpScb->BufferSize, BufferLength );
+ DebugTrace( 0, Dbg, "Length = %ld\n", Length);
+
+ //
+ // The server will not accept writes that cross 4k boundaries in the file
+ //
+
+ if ((IrpContext->pNpScb->PageAlign) &&
+ (DIFFERENT_PAGES( ByteOffset.LowPart, Length ))) {
+ Length = 4096 -
+ ((ULONG)ByteOffset.LowPart & (4096-1));
+ }
+
+ IrpContext->Specific.Write.Buffer = WriteBuffer;
+ IrpContext->Specific.Write.WriteOffset = 0;
+ IrpContext->Specific.Write.RemainingLength = BufferLength;
+ IrpContext->Specific.Write.LastWriteLength = Length;
+ IrpContext->Specific.Write.FileOffset = ByteOffset.LowPart;
+ IrpContext->Specific.Write.PartialMdl = NULL;
+
+ Done = FALSE;
+
+ while ( !Done ) {
+
+ //
+ // Setup to do at most 64K of i/o asynchronously, or buffer length.
+ //
+
+ IrpContext->Specific.Write.BurstLength =
+ MIN( 64 * 1024, IrpContext->Specific.Write.RemainingLength );
+ IrpContext->Specific.Write.BurstOffset = 0;
+
+ //
+ // Try to allocate an MDL for this i/o.
+ //
+
+ DataMdl = ALLOCATE_MDL(
+ (PCHAR)IrpContext->Specific.Write.Buffer +
+ IrpContext->Specific.Write.WriteOffset,
+ IrpContext->Specific.Write.BurstLength,
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL);
+
+ if ( DataMdl == NULL ) {
+ if ( IrpContext->Specific.Write.PartialMdl != NULL ) {
+ FREE_MDL( IrpContext->Specific.Write.PartialMdl );
+ }
+ DebugTrace(-1, Dbg, "WriteNcp -> %X\n", STATUS_INSUFFICIENT_RESOURCES );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ IrpContext->Specific.Write.FullMdl = DataMdl;
+
+
+ //
+ // If there is no MDL for this write probe the data MDL to
+ // lock it's pages down. Otherwise, use the data MDL as
+ // a partial MDL.
+ //
+
+ if ( WriteMdl == NULL ) {
+
+ //
+ // The Probe may cause us to page in some data. If the data is from
+ // the same server we are writing to then we had better not be at
+ // the front of the queue otherwise it will wait indefinitely behind us.
+ // Its a good idea to Dequeue ourselves after each burst anyway because
+ // its a quick operation and it alow smaller requests to overtake a very
+ // large series of bursts.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ try {
+ MmProbeAndLockPages( DataMdl, irp->RequestorMode, IoReadAccess);
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ FREE_MDL( DataMdl );
+ DebugTrace(-1, Dbg, "WriteNcp -> %X\n", GetExceptionCode() );
+ return GetExceptionCode();
+ }
+
+ } else {
+ IoBuildPartialMdl(
+ WriteMdl,
+ DataMdl,
+ (PCHAR)IrpContext->Specific.Write.Buffer,
+ IrpContext->Specific.Write.BurstLength );
+ }
+
+ //
+ // Allocate a partial Mdl for the worst possible case of alignment
+ //
+
+ IrpContext->Specific.Write.PartialMdl =
+ ALLOCATE_MDL( 0 , IrpContext->pNpScb->BufferSize + PAGE_SIZE-1, FALSE, FALSE, NULL);
+
+ if ( IrpContext->Specific.Write.PartialMdl == NULL ) {
+
+ if ( WriteMdl == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+ DebugTrace(-1, Dbg, "WriteNcp -> %X\n", STATUS_INSUFFICIENT_RESOURCES );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Build a partial MDL for this write NCP.
+ //
+
+ IoBuildPartialMdl(
+ DataMdl,
+ IrpContext->Specific.Write.PartialMdl,
+ MmGetMdlVirtualAddress( DataMdl ),
+ Length );
+
+ if ( IrpContext->Specific.Write.BurstLength ==
+ IrpContext->Specific.Write.RemainingLength ) {
+ Done = TRUE;
+ }
+
+ //
+ // Send the request.
+ //
+
+ status = ExchangeWithWait(
+ IrpContext,
+ WriteNcpCallback,
+ "F-rdwf",
+ NCP_WRITE_FILE,
+ &Icb->Handle, sizeof( Icb->Handle ),
+ IrpContext->Specific.Write.FileOffset,
+ Length,
+ IrpContext->Specific.Write.PartialMdl );
+
+ Stats.WriteNcps+=2;
+
+ FREE_MDL( IrpContext->Specific.Write.PartialMdl );
+
+ //
+ // Unlock locked pages, and free our MDL.
+ //
+
+ if ( WriteMdl == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+
+ //
+ // If we had a failure, we need to terminate this loop.
+ // The only status that is set is the Specific->Write
+ // status. We can not trust what comes back from the
+ // ExchangeWithWait by design.
+ //
+
+ if ( !NT_SUCCESS( IrpContext->Specific.Write.Status ) ) {
+ Done = TRUE;
+ }
+
+ //
+ // Reset the packet length since we may have less than
+ // a packet to send.
+ //
+
+ Length = MIN( (ULONG)IrpContext->pNpScb->BufferSize,
+ IrpContext->Specific.Write.RemainingLength );
+ IrpContext->Specific.Write.LastWriteLength = Length;
+
+ }
+
+ status = IrpContext->Specific.Write.Status;
+
+ DebugTrace(-1, Dbg, "WriteNcp -> %08lx\n", status );
+ return status;
+}
+
+
+NTSTATUS
+WriteNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG Length;
+ ULONG LastLength;
+
+ DebugTrace(0, Dbg, "WriteNcpCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ IrpContext->Specific.Write.Status = STATUS_REMOTE_NOT_LISTENING;
+
+ NwSetIrpContextEvent( IrpContext );
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ LastLength = IrpContext->Specific.Write.LastWriteLength;
+ Status = ParseResponse( IrpContext, Response, BytesAvailable, "N" );
+
+ if ( NT_SUCCESS(Status) ) {
+
+ // If the last write worked then move the pointers appropriately
+
+ IrpContext->Specific.Write.RemainingLength -= LastLength;
+ IrpContext->Specific.Write.BurstLength -= LastLength;
+ IrpContext->Specific.Write.WriteOffset += LastLength;
+ IrpContext->Specific.Write.FileOffset += LastLength;
+ IrpContext->Specific.Write.BurstOffset += LastLength;
+
+ // If this is a print job, remember that we actually wrote data
+
+ if ( IrpContext->Icb->IsPrintJob ) {
+ IrpContext->Icb->ActuallyPrinted = TRUE;
+ }
+
+ } else {
+
+ //
+ // Abandon this request
+ //
+
+ IrpContext->Specific.Write.Status = Status;
+ NwSetIrpContextEvent( IrpContext );
+ DebugTrace( 0, Dbg, "WriteNcpCallback -> %08lx\n", Status );
+ return Status;
+ }
+
+
+ if ( IrpContext->Specific.Write.BurstLength != 0 ) {
+
+ // Write the next packet.
+
+ DebugTrace( 0, Dbg, "RemainingLength = %ld\n", IrpContext->Specific.Write.RemainingLength);
+ DebugTrace( 0, Dbg, "FileOffset = %ld\n", IrpContext->Specific.Write.FileOffset);
+ DebugTrace( 0, Dbg, "WriteOffset = %ld\n", IrpContext->Specific.Write.WriteOffset);
+ DebugTrace( 0, Dbg, "BurstOffset = %ld\n", IrpContext->Specific.Write.BurstOffset);
+
+
+ Length = MIN( (ULONG)IrpContext->pNpScb->BufferSize,
+ IrpContext->Specific.Write.BurstLength );
+
+ //
+ // The server will not accept writes that cross 4k boundaries
+ // in the file.
+ //
+
+ if ((IrpContext->pNpScb->PageAlign) &&
+ (DIFFERENT_PAGES( IrpContext->Specific.Write.FileOffset, Length ))) {
+
+ Length = 4096 -
+ ((ULONG)IrpContext->Specific.Write.FileOffset & (4096-1));
+
+ }
+
+ IrpContext->Specific.Write.LastWriteLength = Length;
+
+ DebugTrace( 0, Dbg, "Length = %ld\n", Length);
+
+ MmPrepareMdlForReuse( IrpContext->Specific.Write.PartialMdl );
+
+ IoBuildPartialMdl(
+ IrpContext->Specific.Write.FullMdl,
+ IrpContext->Specific.Write.PartialMdl,
+ (PUCHAR)MmGetMdlVirtualAddress( IrpContext->Specific.Write.FullMdl ) +
+ IrpContext->Specific.Write.BurstOffset,
+ Length );
+
+ //
+ // Send the request.
+ //
+
+ BuildRequestPacket(
+ IrpContext,
+ WriteNcpCallback,
+ "F-rdwf",
+ NCP_WRITE_FILE,
+ &IrpContext->Icb->Handle, sizeof( IrpContext->Icb->Handle ),
+ IrpContext->Specific.Write.FileOffset,
+ Length,
+ IrpContext->Specific.Write.PartialMdl );
+
+ Status = PrepareAndSendPacket( IrpContext );
+
+ Stats.WriteNcps+=2;
+
+ DebugTrace(-1, Dbg, "WriteNcbCallBack -> %08lx\n", Status );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ //
+ // Abandon this request
+ //
+
+ IrpContext->Specific.Write.Status = Status;
+ NwSetIrpContextEvent( IrpContext );
+ DebugTrace( 0, Dbg, "WriteNcpCallback -> %08lx\n", Status );
+ return Status;
+ }
+
+
+ } else {
+
+ //
+ // We're done with this request, signal the writing thread.
+ //
+
+ IrpContext->Specific.Write.Status = STATUS_SUCCESS;
+ NwSetIrpContextEvent( IrpContext );
+ }
+
+ DebugTrace( 0, Dbg, "WriteNcpCallback -> %08lx\n", Status );
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+BurstWrite(
+ PIRP_CONTEXT IrpContext,
+ LARGE_INTEGER ByteOffset,
+ ULONG BufferLength,
+ PVOID WriteBuffer,
+ PMDL WriteMdl
+ )
+/*++
+
+Routine Description:
+
+ This routine exchanges a series of burst write NCPs with the server.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ Status of the transfer.
+
+--*/
+{
+ PICB Icb;
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG Length; // Size we will send to the server
+
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PMDL DataMdl;
+ BOOLEAN Done;
+ BOOLEAN MissingData;
+
+ ULONG TimeInNwUnits;
+
+ ULONG LastLength;
+ ULONG Result;
+ UCHAR BurstFlags;
+ USHORT MissingFragmentCount;
+ USHORT i;
+ ULONG FragmentOffset;
+ USHORT FragmentLength;
+
+ Icb = IrpContext->Icb;
+ pNpScb = IrpContext->pNpScb;
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ IrpContext->Specific.Write.WriteOffset = 0;
+ IrpContext->Specific.Write.RemainingLength = BufferLength;
+
+ IrpContext->Specific.Write.TotalWriteLength = BufferLength;
+ IrpContext->Specific.Write.TotalWriteOffset = ByteOffset.LowPart;
+
+ DebugTrace(+1, Dbg, "BurstWrite...\n", 0);
+ DebugTrace( 0, Dbg, "irp = %08lx\n", (ULONG)irp);
+ DebugTrace( 0, Dbg, "WriteLen= %ld\n", BufferLength);
+ DebugTrace( 0, Dbg, "HOffset = %lx\n", ByteOffset.HighPart);
+ DebugTrace( 0, Dbg, "LOffset = %lx\n", ByteOffset.LowPart);
+
+ //
+ // Renegotiate burst mode, if necessary
+ //
+
+ if ( pNpScb->BurstRenegotiateReqd ) {
+ pNpScb->BurstRenegotiateReqd = FALSE;
+
+ RenegotiateBurstMode( IrpContext, pNpScb );
+ }
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_BURST_WRITE );
+
+ if (Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_FCB) {
+
+ pScb = Icb->SuperType.Fcb->Scb;
+ DebugTrace( 0, Dbg, "File = %wZ\n", &Icb->SuperType.Fcb->FullFileName);
+
+ } else {
+
+ //
+ // Write to a queue
+ //
+
+ pScb = Icb->SuperType.Scb;
+
+ }
+
+ ASSERT (pScb->NodeTypeCode == NW_NTC_SCB);
+
+ //
+ // Calculate the length of the burst to send.
+ //
+
+ Length = MIN( (ULONG)pNpScb->MaxSendSize, BufferLength );
+ DebugTrace( 0, Dbg, "Length = %ld\n", Length);
+
+ if ( ByteOffset.HighPart == 0xFFFFFFFF &&
+ ByteOffset.LowPart == FILE_WRITE_TO_END_OF_FILE ) {
+
+ ULONG FileLength;
+
+ //
+ // Write relative to end of file. Find the end of file.
+ //
+
+ status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_GET_FILE_SIZE,
+ &Icb->Handle, sizeof(Icb->Handle) );
+
+ if ( NT_SUCCESS( status ) ) {
+ status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nd",
+ &FileLength );
+ }
+
+ if ( !NT_SUCCESS( status ) ) {
+ return( status );
+ }
+
+ IrpContext->Specific.Write.FileOffset = FileLength;
+
+ } else {
+
+ IrpContext->Specific.Write.FileOffset = ByteOffset.LowPart;
+
+ }
+
+ //
+ // Setup context parameters for burst write.
+ //
+
+ IrpContext->Specific.Write.LastWriteLength = Length;
+ IrpContext->Destination = pNpScb->RemoteAddress;
+
+ IrpContext->Specific.Write.Buffer = WriteBuffer;
+
+ //
+ // Set the timeout to be the time for all te burst packets to be sent plus a round
+ // trip delay plus a second.
+ //
+
+ TimeInNwUnits = pNpScb->NwSingleBurstPacketTime * ((Length / IrpContext->pNpScb->MaxPacketSize) + 1) +
+ IrpContext->pNpScb->NwLoopTime;
+
+ IrpContext->pNpScb->SendTimeout =
+ (SHORT)(((TimeInNwUnits / 555) *
+ (ULONG)WriteTimeoutMultiplier) / 100 + 1) ;
+
+ if (IrpContext->pNpScb->SendTimeout < 2)
+ {
+ IrpContext->pNpScb->SendTimeout = 2 ;
+ }
+
+ if (IrpContext->pNpScb->SendTimeout > (SHORT)MaxWriteTimeout)
+ {
+ IrpContext->pNpScb->SendTimeout = (SHORT)MaxWriteTimeout ;
+ }
+
+ IrpContext->pNpScb->TimeOut = IrpContext->pNpScb->SendTimeout;
+
+ pNpScb->RetryCount = 20;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->SendTimeout = %08lx\n", IrpContext->pNpScb->SendTimeout );
+
+ Done = FALSE;
+
+ do {
+
+ DataMdl = ALLOCATE_MDL(
+ (PCHAR)IrpContext->Specific.Write.Buffer +
+ IrpContext->Specific.Write.WriteOffset,
+ Length,
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL);
+
+ if ( DataMdl == NULL ) {
+ return ( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // If there is no MDL for this write, 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 ( WriteMdl == NULL ) {
+
+ //
+ // The Probe may cause us to page in some data. If the data is from
+ // the same server we are writing to then we had better not be at
+ // the front of the queue otherwise it will wait indefinitely behind us.
+ // Its a good idea to Dequeue ourselves after each burst anyway because
+ // its a quick operation and it alow smaller requests to overtake a very
+ // large series of bursts.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ try {
+ MmProbeAndLockPages( DataMdl, irp->RequestorMode, IoReadAccess);
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ FREE_MDL( DataMdl );
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ IoBuildPartialMdl(
+ WriteMdl,
+ DataMdl,
+ (PCHAR)IrpContext->Specific.Write.Buffer +
+ IrpContext->Specific.Write.WriteOffset,
+ Length );
+ }
+
+ pNpScb->BurstDataWritten += Length;
+
+ if (( SendExtraNcp ) &&
+ ( pNpScb->BurstDataWritten >= 0x0000ffff )) {
+
+
+ ULONG Flags;
+
+ //
+ // VLM client sends an NCP when starting a burst mode request
+ // if the last request was not a write. It also does this every
+ // 0xfe00 bytes written
+ //
+ // When going to a queue we will use handle 2. This is what the vlm
+ // client always seems to do.
+ //
+
+ Flags = IrpContext->Flags;
+
+ //
+ // Reset IrpContext parameters
+ //
+
+ IrpContext->TxMdl->Next = NULL;
+ IrpContext->CompletionSendRoutine = NULL;
+ IrpContext->TimeoutRoutine = NULL;
+ IrpContext->Flags &= ~(IRP_FLAG_RETRY_SEND | IRP_FLAG_BURST_REQUEST | IRP_FLAG_BURST_PACKET |
+ IRP_FLAG_BURST_WRITE | IRP_FLAG_NOT_SYSTEM_PACKET );
+ IrpContext->pTdiStruct = NULL;
+
+ ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sb", // NCP Get Directory Path
+ NCP_DIR_FUNCTION, NCP_GET_DIRECTORY_PATH,
+ (Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_FCB)?
+ Icb->SuperType.Fcb->Vcb->Specific.Disk.Handle : 2 );
+
+ pNpScb->BurstDataWritten = Length;
+
+ IrpContext->Flags = Flags;
+ SetFlag( IrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+ }
+
+ IrpContext->TimeoutRoutine = BurstWriteTimeout;
+ IrpContext->CompletionSendRoutine = BurstWriteCompletionSend;
+ IrpContext->pTdiStruct = &IrpContext->pNpScb->Burst;
+ IrpContext->PacketType = NCP_BURST;
+ IrpContext->pEx = BurstWriteCallback;
+
+ IrpContext->Specific.Write.FullMdl = DataMdl;
+
+ MmGetSystemAddressForMdl( DataMdl );
+
+ //
+ // Allocate a partial Mdl for the worst possible case of alignment
+ //
+
+ IrpContext->Specific.Write.PartialMdl =
+ ALLOCATE_MDL( 0, IrpContext->pNpScb->MaxPacketSize + PAGE_SIZE - 1, FALSE, FALSE, NULL);
+
+ if ( IrpContext->Specific.Write.PartialMdl == NULL ) {
+
+ if ( WriteMdl == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Get to the front of the SCB queue, if we are not already there.
+ // Note that can't append this IrpContext to the SCB until after
+ // the probe and lock, since the probe and lock may cause a paging
+ // read on this SCB.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ status = SendWriteBurst(
+ IrpContext,
+ BURST_WRITE_HEADER_SIZE,
+ (USHORT)Length,
+ TRUE,
+ FALSE );
+
+ MissingData = TRUE;
+ while ( MissingData ) {
+
+ KeWaitForSingleObject( &IrpContext->Event, Executive, KernelMode, FALSE, NULL );
+ MmPrepareMdlForReuse( IrpContext->Specific.Write.PartialMdl );
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_RETRY_SEND ) ) {
+
+ //
+ // This burst has timed out, simply resend the burst.
+ //
+
+ NwProcessSendBurstFailure( pNpScb, 1 );
+
+ status = SendWriteBurst(
+ IrpContext,
+ BURST_WRITE_HEADER_SIZE,
+ (USHORT)Length,
+ TRUE,
+ TRUE );
+ continue;
+ }
+
+ if ( !NT_SUCCESS( IrpContext->Specific.Write.Status ) ) {
+
+ status = IrpContext->Specific.Write.Status;
+ Done = TRUE;
+
+ goto EndOfLoop;
+
+ } else {
+
+ status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "B_d",
+ &BurstFlags,
+ 8,
+ &Result );
+
+ }
+
+ if ( BurstFlags & BURST_FLAG_SYSTEM_PACKET ) {
+
+ //
+ // The server dropped at least one packet.
+ //
+
+ MissingData = TRUE;
+ DebugTrace( 0, Dbg, "Received system packet\n", 0 );
+
+ //
+ // This is a missing fragment request.
+ //
+
+ status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "G_w",
+ 34,
+ &MissingFragmentCount );
+
+ ASSERT( NT_SUCCESS( status ) );
+ ASSERT( MissingFragmentCount != 0 );
+
+ NwProcessSendBurstFailure( pNpScb, MissingFragmentCount );
+
+ DebugTrace( 0, Dbg, "Received request for %d missing fragment\n", MissingFragmentCount );
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ //
+ // Walk the missing fragment list and send the missing fragments.
+ //
+
+ for ( i = 0; i < MissingFragmentCount && NT_SUCCESS( status ); i++ ) {
+
+ status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "G_dw",
+ 34 + 2 + 6 * i,
+ &FragmentOffset,
+ &FragmentLength
+ );
+
+ ASSERT( NT_SUCCESS( status ) );
+
+ if ( FragmentOffset < Length + BURST_WRITE_HEADER_SIZE &&
+ FragmentOffset + FragmentLength <=
+ Length + BURST_WRITE_HEADER_SIZE ) {
+
+ //
+ // Send a burst with the missing data. Do no set the
+ // end of burst bit until we have sent the last
+ // missing fragment packet.
+ //
+
+ status = SendWriteBurst(
+ IrpContext,
+ FragmentOffset,
+ FragmentLength,
+ (BOOLEAN)( i == (MissingFragmentCount - 1)),
+ FALSE );
+ } else {
+
+ //
+ // Received a bogus missing fragment request.
+ // Ignore the remainder of the request.
+ //
+
+ status = STATUS_INVALID_NETWORK_RESPONSE;
+ Done = TRUE;
+
+ goto EndOfLoop;
+
+ }
+ }
+
+ Stats.PacketBurstWriteTimeouts++;
+
+ } else {
+
+ NwProcessSendBurstSuccess( pNpScb );
+
+ MissingData = FALSE;
+
+ //
+ // This is not a system packets, check the response.
+ //
+
+ if ( Result == 0 ) {
+
+ //
+ // If the last write worked then move the pointers appropriately
+ //
+
+ LastLength = IrpContext->Specific.Write.LastWriteLength;
+
+ IrpContext->Specific.Write.RemainingLength -= LastLength;
+ IrpContext->Specific.Write.WriteOffset += LastLength;
+ IrpContext->Specific.Write.FileOffset += LastLength;
+
+ //
+ // If this is a print job, remember that we actually wrote data
+ //
+
+ if ( IrpContext->Icb->IsPrintJob ) {
+ IrpContext->Icb->ActuallyPrinted = TRUE;
+ }
+
+ } else {
+
+ //
+ // Abandon this request
+ //
+
+ Done = TRUE;
+ }
+
+
+ //
+ // Do we need to send another burst to satisfy the write IRP?
+ //
+
+ if ( IrpContext->Specific.Write.RemainingLength != 0 ) {
+
+ //
+ // Write the next packet.
+ //
+
+ DebugTrace( 0, Dbg, "RemainingLength = %ld\n", IrpContext->Specific.Write.RemainingLength);
+ DebugTrace( 0, Dbg, "FileOffset = %ld\n", IrpContext->Specific.Write.FileOffset);
+ DebugTrace( 0, Dbg, "WriteOffset = %ld\n", IrpContext->Specific.Write.WriteOffset);
+
+ Length = MIN( (ULONG)IrpContext->pNpScb->MaxSendSize,
+ IrpContext->Specific.Write.RemainingLength );
+
+ IrpContext->Specific.Write.LastWriteLength = Length;
+
+ } else {
+ Done = TRUE;
+ }
+
+ } // else ( not a system packet )
+
+ } // while ( missing data )
+
+ //
+ // Update the burst request number now.
+ //
+
+ if ( status != STATUS_REMOTE_NOT_LISTENING ) {
+ IrpContext->pNpScb->BurstRequestNo++;
+ }
+
+ //
+ // If we need to reconnect, do it now.
+ //
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
+ BurstWriteReconnect( IrpContext );
+ }
+
+ //
+ // Dequeue this Irp context in preparation for the next run
+ // through the loop.
+ //
+
+EndOfLoop:
+ ASSERT( status != STATUS_PENDING );
+
+ FREE_MDL( IrpContext->Specific.Write.PartialMdl );
+
+ //
+ // Unlock locked pages, and free our MDL.
+ //
+
+ if ( WriteMdl == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+
+ } while ( !Done );
+
+ DebugTrace(-1, Dbg, "BurstWrite -> %08lx\n", status );
+ return status;
+}
+
+#ifdef NWDBG
+int DropWritePackets;
+#endif
+
+
+NTSTATUS
+BurstWriteCompletionSend(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine handles completion of a burst write send. If the sending
+ thread is waiting for send completion notification, it signals the
+ IrpContext Event.
+
+ Note that this routine can be called from SendWriteBurst (i.e. not
+ at DPC level), if an allocation fails.
+
+Arguments:
+
+ DeviceObject - unused.
+
+ Irp - Supplies Irp that the transport has finished processing.
+
+ Context - Supplies the IrpContext associated with the Irp.
+
+Return Value:
+
+ The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops
+ processing Irp stack locations at this point.
+
+--*/
+{
+ PIRP_CONTEXT pIrpContext = (PIRP_CONTEXT) Context;
+ INTERLOCKED_RESULT Result;
+ KIRQL OldIrql;
+ NTSTATUS Status;
+
+ //
+ // Avoid completing the Irp because the Mdl etc. do not contain
+ // their original values.
+ //
+
+ DebugTrace( +1, Dbg, "BurstWriteCompletionSend\n", 0);
+ DebugTrace( +0, Dbg, "Irp %X\n", Irp);
+ DebugTrace( +0, Dbg, "pIrpC %X\n", pIrpContext);
+
+ if ( Irp != NULL ) {
+
+ DebugTrace( 0, Dbg, "Burst Write Send = %08lx\n", Irp->IoStatus.Status );
+
+ Status = Irp->IoStatus.Status;
+
+ } else {
+
+ Status = STATUS_SUCCESS;
+
+ }
+
+ //
+ // If this was a secondary IRP, free it now.
+ //
+
+ if ( pIrpContext->NodeTypeCode == NW_NTC_MINI_IRP_CONTEXT ) {
+ PMINI_IRP_CONTEXT MiniIrpContext;
+
+ MiniIrpContext = (PMINI_IRP_CONTEXT)pIrpContext;
+
+ ASSERT( MiniIrpContext->Mdl2->Next == NULL );
+
+ pIrpContext = MiniIrpContext->IrpContext;
+ FreeMiniIrpContext( MiniIrpContext );
+
+ }
+
+ //
+ // Nothing to do unless the last send has completed.
+ //
+
+ Result = ExInterlockedDecrementLong(
+ &pIrpContext->Specific.Write.PacketCount,
+ &pIrpContext->pNpScb->NpScbInterLock );
+
+ if ( Result != RESULT_ZERO ) {
+ DebugTrace( 0, Dbg, "Packets to go = %d\n", pIrpContext->Specific.Write.PacketCount );
+
+ if (Status == STATUS_BAD_NETWORK_PATH) {
+
+ //
+ // IPX has ripped for the destination but failed to find the net. Minimise the
+ // difference between this case and sending a normal burst by completing the
+ // transmission as soon as possible.
+ //
+
+ pIrpContext->pNpScb->NwSendDelay = 0;
+
+ }
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+ }
+
+ KeAcquireSpinLock( &pIrpContext->pNpScb->NpScbSpinLock, &OldIrql );
+
+ ASSERT( pIrpContext->pNpScb->Sending == TRUE );
+ pIrpContext->pNpScb->Sending = FALSE;
+
+ //
+ // Signal to the writing thread that the send has completed, if it
+ // is waiting.
+ //
+
+ if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_SIGNAL_EVENT ) ) {
+ ClearFlag( pIrpContext->Flags, IRP_FLAG_SIGNAL_EVENT );
+ NwSetIrpContextEvent( pIrpContext );
+ }
+
+ //
+ // If we processed a receive while waiting for send
+ // completion call the receive handler routine now.
+ //
+
+ if ( pIrpContext->pNpScb->Received ) {
+
+ pIrpContext->pNpScb->Receiving = FALSE;
+ pIrpContext->pNpScb->Received = FALSE;
+
+ KeReleaseSpinLock( &pIrpContext->pNpScb->NpScbSpinLock, OldIrql );
+
+ pIrpContext->pEx(
+ pIrpContext,
+ pIrpContext->ResponseLength,
+ pIrpContext->rsp );
+
+ } else {
+ if ((Status == STATUS_BAD_NETWORK_PATH) &&
+ (pIrpContext->pNpScb->Receiving == FALSE)) {
+
+ //
+ // Usually means a ras connection has gone down during the burst.
+ // Go through the timeout logic now because the ras timeouts take
+ // a long time and unless we re rip things won't get better.
+ //
+
+ pIrpContext->Specific.Write.Status = STATUS_REMOTE_NOT_LISTENING;
+ ClearFlag( pIrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ NwSetIrpContextEvent( pIrpContext );
+
+ }
+
+ KeReleaseSpinLock( &pIrpContext->pNpScb->NpScbSpinLock, OldIrql );
+ }
+
+ DebugTrace( -1, Dbg, "BurstWriteCompletionSend -> STATUS_MORE_PROCESSING_REQUIRED\n", 0);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+}
+
+
+NTSTATUS
+BurstWriteCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine receives the response a burst write.
+
+Arguments:
+
+ IrpContext - A pointer to the context information for this IRP.
+
+ BytesAvailable - Actual number of bytes in the received message.
+
+ Response - Points to the receive buffer.
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ DebugTrace(0, Dbg, "BurstWriteCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->Write.Status
+ // Clear the retry send bit so we don't keep retrying.
+ //
+
+ IrpContext->Specific.Write.Status = STATUS_REMOTE_NOT_LISTENING;
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ NwSetIrpContextEvent( IrpContext );
+
+ DebugTrace(-1, Dbg, "BurstWriteCallback -> %X\n", STATUS_REMOTE_NOT_LISTENING );
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ IrpContext->Specific.Write.Status = STATUS_SUCCESS;
+ ASSERT( BytesAvailable < MAX_RECV_DATA );
+ ++Stats.PacketBurstWriteNcps;
+
+ //
+ // Clear the retry send bit, since we have a response.
+ //
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ //
+ // Copy the burst write response, and signal the users thread
+ // to continue.
+ //
+
+ TdiCopyLookaheadData(
+ IrpContext->rsp,
+ Response,
+ BytesAvailable < MAX_RECV_DATA ? BytesAvailable : MAX_RECV_DATA,
+ 0
+ );
+
+ IrpContext->ResponseLength = BytesAvailable;
+
+ NwSetIrpContextEvent( IrpContext );
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+SendWriteBurst(
+ PIRP_CONTEXT IrpContext,
+ ULONG BurstOffset,
+ USHORT Length,
+ BOOLEAN EndOfBurst,
+ BOOLEAN Retransmission
+ )
+/*++
+
+Routine Description:
+
+ This routine does the actual work of sending a series of burst write
+ NCPs to the server.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ BurstOffset - The offset in the burst to start sending. If BurstOffset
+ equals BURST_WRITE_HEADER_SIZE, start from the beginning of the burst.
+
+ Length - The length of the burst.
+
+ EndOfBurst - If TRUE set the end of burst bit when sending the last
+ frame. Otherwise there is more (discontiguous) data to come in
+ the current burst.
+
+ Retransmission - If TRUE, this is a burst write timeout retransmission.
+ Send the first packet only.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ UCHAR BurstFlags;
+ NTSTATUS Status;
+ BOOLEAN MoreData;
+ PIRP SendIrp;
+ PMINI_IRP_CONTEXT MiniIrpContext;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "SendWriteBurst...\n", 0);
+
+ DebugTrace( 0, Dbg, "Data offset = %d\n", BurstOffset );
+ DebugTrace( 0, Dbg, "Data length = %d\n", Length );
+ DebugTrace( 0, Dbg, "End of burst = %d\n", EndOfBurst );
+
+ //
+ // Send the request.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_BURST_REQUEST | IRP_FLAG_BURST_PACKET );
+
+ //
+ // Set the burst flags
+ //
+
+ IrpContext->Specific.Write.BurstLength =
+ MIN( IrpContext->pNpScb->MaxPacketSize, Length );
+
+ //
+ // Set the end-of-burst bit (and enable receiving the response), if this
+ // is the last packet we expect to send.
+ //
+
+ if ( ( !EndOfBurst || IrpContext->Specific.Write.BurstLength < Length )
+ && !Retransmission ) {
+
+ IrpContext->pNpScb->OkToReceive = FALSE;
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE );
+ BurstFlags = 0;
+
+ } else {
+
+ DebugTrace( 0, Dbg, "Last packet in the burst\n", 0);
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE );
+ BurstFlags = BURST_FLAG_END_OF_BURST;
+
+ }
+
+ if ( !EndOfBurst ) {
+ SetFlag( IrpContext->Flags, IRP_FLAG_SIGNAL_EVENT );
+ }
+
+ //
+ // Build the partial MDL for the first packet in the burst.
+ //
+
+ IoBuildPartialMdl(
+ IrpContext->Specific.Write.FullMdl,
+ IrpContext->Specific.Write.PartialMdl,
+ (PUCHAR)MmGetMdlVirtualAddress( IrpContext->Specific.Write.FullMdl ) +
+ BurstOffset - BURST_WRITE_HEADER_SIZE,
+ IrpContext->Specific.Write.BurstLength );
+
+ //
+ // Set the burst flags
+ //
+
+ if ( BurstOffset == BURST_WRITE_HEADER_SIZE ) {
+ SetFlag( IrpContext->Flags, IRP_FLAG_BURST_REQUEST | IRP_FLAG_BURST_PACKET );
+ }
+
+ if ( ( IrpContext->Specific.Write.BurstLength < Length ) &&
+ !Retransmission ) {
+ MoreData = TRUE;
+ } else {
+ MoreData = FALSE;
+ }
+
+ if ( BurstOffset == BURST_WRITE_HEADER_SIZE ) {
+
+ BuildBurstWriteFirstReq(
+ IrpContext,
+ IrpContext->req,
+ Length,
+ IrpContext->Specific.Write.PartialMdl,
+ BurstFlags,
+ *(ULONG UNALIGNED *)(&IrpContext->Icb->Handle[2]),
+ IrpContext->Specific.Write.FileOffset );
+
+ } else {
+
+ BuildBurstWriteNextReq(
+ IrpContext,
+ IrpContext->req,
+ IrpContext->Specific.Write.LastWriteLength + BURST_WRITE_HEADER_SIZE,
+ BurstFlags,
+ BurstOffset,
+ IrpContext->TxMdl,
+ IrpContext->Specific.Write.PartialMdl
+ );
+
+ }
+
+ if ( !Retransmission ) {
+ IrpContext->Specific.Write.PacketCount =
+ ( Length + IrpContext->pNpScb->MaxPacketSize - 1 ) /
+ IrpContext->pNpScb->MaxPacketSize;
+
+ } else {
+ IrpContext->Specific.Write.PacketCount = 1;
+ }
+
+ DebugTrace( 0, Dbg, "Packet count = %d\n", IrpContext->Specific.Write.PacketCount );
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Send delay = %d\n", IrpContext->pNpScb->NwSendDelay );
+
+ //
+ // Use the original IRP context to format the first packet.
+ //
+
+ ++Stats.PacketBurstWriteNcps;
+ PreparePacket( IrpContext, IrpContext->pOriginalIrp, IrpContext->TxMdl );
+
+ Status = SendPacket( IrpContext, IrpContext->pNpScb );
+
+ while ( MoreData ) {
+
+ if ( IrpContext->pNpScb->NwSendDelay > 0 ) {
+
+ //
+ // Introduce a send delay between packets.
+ //
+
+ KeDelayExecutionThread(
+ KernelMode,
+ FALSE,
+ &IrpContext->pNpScb->NtSendDelay );
+ }
+
+ MiniIrpContext = AllocateMiniIrpContext( IrpContext );
+
+ DebugTrace( 0, Dbg, "Allocated mini IrpContext = %X\n", MiniIrpContext );
+
+ //
+ // Calculate the total number of bytes to send during this burst. Do this before
+ // checking to see if MiniIrpContext is NULL so that we skip the packet rather
+ // than sitting in a tight loop.
+ //
+
+ BurstOffset += IrpContext->Specific.Write.BurstLength;
+
+ //
+ // Do we need to send another burst write packet?
+ //
+
+ Length -= (USHORT)IrpContext->Specific.Write.BurstLength;
+
+ ASSERT ( Length > 0 );
+
+ IrpContext->Specific.Write.BurstLength =
+ MIN( IrpContext->pNpScb->MaxPacketSize, (ULONG)Length );
+
+ DebugTrace( +0, Dbg, "More data, sending %d bytes\n", IrpContext->Specific.Write.BurstLength );
+
+ //
+ // If we can't allocate a mini irp context to send the packet,
+ // just skip it and wait for the server to ask a retranmit. At
+ // this point performance isn't exactly stellar, so don't worry
+ // about having to wait for a timeout.
+ //
+
+ if ( MiniIrpContext == NULL ) {
+
+ ExInterlockedDecrementLong(
+ &IrpContext->Specific.Write.PacketCount,
+ &IrpContext->pNpScb->NpScbInterLock );
+
+ continue;
+ }
+
+#ifdef NWDBG
+
+ //
+ // If DropWritePackets is enabled, simulate missing packets, by
+ // occasionally dropping 500 bytes of data.
+ //
+
+ if ( DropWritePackets != 0 ) {
+ if ( ( rand() % DropWritePackets ) == 0 &&
+ Length != IrpContext->Specific.Write.BurstLength ) {
+
+ FreeMiniIrpContext( MiniIrpContext );
+
+ ExInterlockedDecrementLong(
+ &IrpContext->Specific.Write.PacketCount,
+ &IrpContext->pNpScb->NpScbInterLock );
+
+ continue;
+ }
+ }
+#endif
+
+ //
+ // Build the MDL for the data to send.
+ //
+
+ IoBuildPartialMdl(
+ IrpContext->Specific.Write.FullMdl,
+ MiniIrpContext->Mdl2,
+ (PUCHAR)MmGetMdlVirtualAddress( IrpContext->Specific.Write.FullMdl ) +
+ BurstOffset - BURST_WRITE_HEADER_SIZE,
+ IrpContext->Specific.Write.BurstLength );
+
+ //
+ // Set the burst flags
+ //
+
+ if ( !EndOfBurst || IrpContext->Specific.Write.BurstLength < Length ) {
+
+ IrpContext->pNpScb->OkToReceive = FALSE;
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE );
+ BurstFlags = 0;
+ } else {
+ DebugTrace( 0, Dbg, "Last packet in the burst\n", 0);
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE );
+ BurstFlags = BURST_FLAG_END_OF_BURST;
+ }
+
+ if ( IrpContext->Specific.Write.BurstLength == Length ) {
+ MoreData = FALSE;
+ }
+
+ BuildBurstWriteNextReq(
+ IrpContext,
+ MiniIrpContext->Mdl1->MappedSystemVa,
+ IrpContext->Specific.Write.LastWriteLength +
+ BURST_WRITE_HEADER_SIZE,
+ BurstFlags,
+ BurstOffset,
+ MiniIrpContext->Mdl1,
+ MiniIrpContext->Mdl2
+ );
+
+ ++Stats.PacketBurstWriteNcps;
+
+ SendIrp = MiniIrpContext->Irp;
+
+ PreparePacket( IrpContext, SendIrp, MiniIrpContext->Mdl1 );
+
+ // BUGBUG Clean this up
+ IoSetCompletionRoutine( SendIrp, BurstWriteCompletionSend, MiniIrpContext, TRUE, TRUE, TRUE);
+
+ ASSERT( MiniIrpContext->Mdl2->Next == NULL );
+
+ Status = SendSecondaryPacket( IrpContext, SendIrp );
+ }
+
+ //
+ // If this is not the end-of-burst, wait for send completion here,
+ // since the caller is about to send more data.
+ //
+
+ if ( !EndOfBurst ) {
+ KeWaitForSingleObject( &IrpContext->Event, Executive, KernelMode, FALSE, NULL );
+ }
+
+ DebugTrace( -1, Dbg, "SendWriteBurst -> %X\n", Status );
+ return( Status );
+}
+
+
+VOID
+BurstWriteTimeout(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine handles a burst write timeout.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ None
+
+--*/
+{
+ NTSTATUS Status = STATUS_UNSUCCESSFUL;
+ PIRP Irp;
+
+ DebugTrace(0, Dbg, "BurstWriteTimeout\n", 0 );
+
+ Irp = IrpContext->pOriginalIrp;
+
+ //
+ // Set the RetrySend flag, so that we know to retransmit the request.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ //
+ // Signal the write thread to wakeup and resend the burst.
+ //
+
+ NwSetIrpContextEvent( IrpContext );
+
+ Stats.PacketBurstWriteTimeouts++;
+
+ return;
+}
+
+
+NTSTATUS
+ResubmitBurstWrite(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine resubmits a burst write over a new burst connection.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ None
+
+--*/
+{
+
+ PNONPAGED_SCB pNpScb = IrpContext->pNpScb;
+
+ PAGED_CODE();
+
+ //
+ // Remember that we need to establish a new burst connection.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+
+ //
+ // Set the packet size down the largest packet we can use, that
+ // is guaranteed to be routable.
+ //
+
+ pNpScb->MaxPacketSize = DEFAULT_PACKET_SIZE;
+
+ //
+ // Crank the delay times down so we give the new connection a chance.
+ //
+
+ pNpScb->NwGoodSendDelay = pNpScb->NwBadSendDelay = pNpScb->NwSendDelay = MinSendDelay;
+ pNpScb->NwGoodReceiveDelay = pNpScb->NwBadReceiveDelay = pNpScb->NwReceiveDelay = MinReceiveDelay;
+
+ pNpScb->SendBurstSuccessCount = 0;
+ pNpScb->ReceiveBurstSuccessCount = 0;
+
+ pNpScb->NtSendDelay.QuadPart = MinSendDelay;
+
+ //
+ // Signal the write thread to wakeup and resend the burst.
+ //
+
+ NwSetIrpContextEvent( IrpContext );
+
+ return( STATUS_PENDING );
+}
+
+
+NTSTATUS
+BurstWriteReconnect(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates a new IRP context and renegotiates burst mode.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ None
+
+--*/
+{
+ PIRP_CONTEXT pNewIrpContext;
+ PNONPAGED_SCB pNpScb = IrpContext->pNpScb;
+ BOOLEAN LIPNegotiated ;
+
+ PAGED_CODE();
+
+ //
+ // Attempt to allocate an extra IRP context.
+ //
+
+ if ( !NwAllocateExtraIrpContext( &pNewIrpContext, pNpScb ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ pNewIrpContext->Specific.Create.UserUid = IrpContext->Specific.Create.UserUid;
+
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+ pNewIrpContext->pNpScb = pNpScb;
+
+ //
+ // Insert this new IrpContext to the head of
+ // the SCB queue for processing. We can get away with this
+ // because we own the IRP context currently at the front of
+ // the queue.
+ //
+
+ ExInterlockedInsertHeadList(
+ &pNpScb->Requests,
+ &pNewIrpContext->NextRequest,
+ &pNpScb->NpScbSpinLock );
+
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+
+ //
+ // Renegotiate the burst connection, this will automatically re-sync
+ // the burst connection.
+ //
+
+ NegotiateBurstMode( pNewIrpContext, pNpScb, &LIPNegotiated );
+
+ //
+ // Reset the sequence numbers.
+ //
+
+ pNpScb->BurstSequenceNo = 0;
+ pNpScb->BurstRequestNo = 0;
+
+ //
+ // Dequeue and free the bonus IRP context.
+ //
+
+ ExInterlockedRemoveHeadList(
+ &pNpScb->Requests,
+ &pNpScb->NpScbSpinLock );
+
+ ClearFlag( pNewIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+
+ NwFreeExtraIrpContext( pNewIrpContext );
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+NwFsdFlushBuffers(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine is the FSD routine that handles NtFlushBuffersFile.
+
+Arguments:
+
+ DeviceObject - Supplies the device object for the write 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, "NwFsdFlushBuffers\n", 0);
+
+ //
+ // Call the common write routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonFlushBuffers( 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 ) {
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdFlushBuffers -> %08lx\n", status );
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonFlushBuffers (
+ IN PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine requests all dirty cache buffers to be flushed for a
+ given file.
+
+Arguments:
+
+ IrpContext - Supplies the request being processed.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+
+ NTSTATUS Status;
+ PFCB Fcb;
+ PICB Icb;
+ NODE_TYPE_CODE NodeTypeCode;
+ PVOID FsContext;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "NwCommonFlushBuffers...\n", 0);
+
+ //
+ // Get the current stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
+
+ //
+ // Decode the file object to figure out who we are. If the result
+ // is not the a file then its an illegal parameter.
+ //
+
+ if (( NodeTypeCode = NwDecodeFileObject( IrpSp->FileObject,
+ &FsContext,
+ (PVOID *)&Icb )) != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "NwCommonFlushBuffers -> %08lx\n", Status );
+ return Status;
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcbSpecial( Icb );
+
+ Fcb = (PFCB)Icb->SuperType.Fcb;
+ NodeTypeCode = Fcb->NodeTypeCode;
+
+ if ( NodeTypeCode != NW_NTC_FCB ) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonFlushBuffers -> %08lx\n", Status );
+ return Status;
+ }
+
+ //
+ // Set up the IRP context to do an exchange
+ //
+
+ IrpContext->pScb = Fcb->Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = Icb;
+
+ //
+ // Send any user data to the server. Note we must not be on the
+ // queue when we do this.
+ //
+
+ MmFlushImageSection(&Icb->NpFcb->SegmentObject, MmFlushForWrite);
+
+ //
+ // Flush our dirty data.
+ //
+
+ Status = AcquireFcbAndFlushCache( IrpContext, Fcb->NonPagedFcb );
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ //
+ // Send a flush NCP
+ //
+
+ Status = Exchange (
+ IrpContext,
+ FlushBuffersCallback,
+ "F-r",
+ NCP_FLUSH_FILE,
+ &Icb->Handle, sizeof( Icb->Handle ) );
+
+ return( Status );
+}
+
+
+NTSTATUS
+FlushBuffersCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine receives the flush file size response and completes the
+ flush IRP.
+
+Arguments:
+
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ DebugTrace(0, Dbg, "FlushBuffersCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, STATUS_REMOTE_NOT_LISTENING );
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ DebugTrace( 0, Dbg, "Timeout\n", 0);
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ //
+ // Get the data from the response.
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ "N" );
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, Status );
+
+ return Status;
+}
+
+
+VOID
+BuildBurstWriteFirstReq(
+ PIRP_CONTEXT IrpContext,
+ PVOID Buffer,
+ ULONG DataSize,
+ PMDL BurstMdl,
+ UCHAR Flags,
+ ULONG Handle,
+ ULONG FileOffset
+ )
+{
+ PNCP_BURST_WRITE_REQUEST BurstWrite;
+ PNONPAGED_SCB pNpScb;
+ ULONG RealDataLength;
+ USHORT RealBurstLength;
+
+ PAGED_CODE();
+
+ BurstWrite = (PNCP_BURST_WRITE_REQUEST)Buffer;
+ pNpScb = IrpContext->pNpScb;
+
+ RealDataLength = DataSize + sizeof( *BurstWrite ) - sizeof( NCP_BURST_HEADER );
+ RealBurstLength = (USHORT)MdlLength( BurstMdl ) + sizeof( *BurstWrite ) - sizeof( NCP_BURST_HEADER );
+
+ BurstWrite->BurstHeader.Command = PEP_COMMAND_BURST;
+ BurstWrite->BurstHeader.Flags = Flags;
+ BurstWrite->BurstHeader.StreamType = 0x02;
+ BurstWrite->BurstHeader.SourceConnection = pNpScb->SourceConnectionId;
+ BurstWrite->BurstHeader.DestinationConnection = pNpScb->DestinationConnectionId;
+
+
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_RETRY_SEND ) ) {
+
+ //
+ // Use the same delay on all retransmissions of the burst. Save
+ // the current time.
+ //
+
+ pNpScb->CurrentBurstDelay = pNpScb->NwSendDelay;
+
+ //
+ // Send system packet next retransmission.
+ //
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ } else {
+
+ //
+ // This is a retransmission. Alternate between sending a system
+ // packet and the first write.
+ //
+
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET ) ) {
+
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ BurstWrite->BurstHeader.Flags = BURST_FLAG_SYSTEM_PACKET;
+
+ LongByteSwap( BurstWrite->BurstHeader.SendDelayTime, pNpScb->CurrentBurstDelay );
+
+ BurstWrite->BurstHeader.DataSize = 0;
+ BurstWrite->BurstHeader.BurstOffset = 0;
+ BurstWrite->BurstHeader.BurstLength = 0;
+ BurstWrite->BurstHeader.MissingFragmentCount = 0;
+
+ IrpContext->TxMdl->ByteCount = sizeof( NCP_BURST_HEADER );
+ IrpContext->TxMdl->Next = NULL;
+
+ return;
+
+ }
+
+ //
+ // Send system packet next retransmission.
+ //
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ }
+
+ LongByteSwap( BurstWrite->BurstHeader.SendDelayTime, pNpScb->CurrentBurstDelay );
+
+ LongByteSwap( BurstWrite->BurstHeader.DataSize, RealDataLength );
+ BurstWrite->BurstHeader.BurstOffset = 0;
+ ShortByteSwap( BurstWrite->BurstHeader.BurstLength, RealBurstLength );
+ BurstWrite->BurstHeader.MissingFragmentCount = 0;
+
+ BurstWrite->Function = BURST_REQUEST_WRITE;
+ BurstWrite->Handle = Handle;
+ LongByteSwap( BurstWrite->TotalWriteOffset, IrpContext->Specific.Write.TotalWriteOffset );
+ LongByteSwap( BurstWrite->TotalWriteLength, IrpContext->Specific.Write.TotalWriteLength );
+ LongByteSwap( BurstWrite->Offset, FileOffset );
+ LongByteSwap( BurstWrite->Length, DataSize );
+
+ IrpContext->TxMdl->ByteCount = sizeof( *BurstWrite );
+ IrpContext->TxMdl->Next = BurstMdl;
+
+ return;
+}
+
+VOID
+BuildBurstWriteNextReq(
+ PIRP_CONTEXT IrpContext,
+ PVOID Buffer,
+ ULONG DataSize,
+ UCHAR BurstFlags,
+ ULONG BurstOffset,
+ PMDL BurstHeaderMdl,
+ PMDL BurstDataMdl
+ )
+{
+ PNCP_BURST_HEADER BurstHeader;
+ PNONPAGED_SCB pNpScb;
+ USHORT BurstLength;
+
+ PAGED_CODE();
+
+ BurstHeader = (PNCP_BURST_HEADER)Buffer;
+ pNpScb = IrpContext->pNpScb;
+
+ BurstLength = (USHORT)MdlLength( BurstDataMdl );
+
+ BurstHeader->Command = PEP_COMMAND_BURST;
+ BurstHeader->Flags = BurstFlags;
+ BurstHeader->StreamType = 0x02;
+ BurstHeader->SourceConnection = pNpScb->SourceConnectionId;
+ BurstHeader->DestinationConnection = pNpScb->DestinationConnectionId;
+
+ LongByteSwap( BurstHeader->SendDelayTime, pNpScb->CurrentBurstDelay );
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_RETRY_SEND ) ) {
+
+ //
+ // This is a retransmission. Alternate between sending a system
+ // packet and the first write.
+ //
+
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET ) ) {
+
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ BurstHeader->Flags = BURST_FLAG_SYSTEM_PACKET;
+
+ LongByteSwap( BurstHeader->SendDelayTime, pNpScb->CurrentBurstDelay );
+
+ BurstHeader->DataSize = 0;
+ BurstHeader->BurstOffset = 0;
+ BurstHeader->BurstLength = 0;
+ BurstHeader->MissingFragmentCount = 0;
+
+ IrpContext->TxMdl->ByteCount = sizeof( NCP_BURST_HEADER );
+ IrpContext->TxMdl->Next = NULL;
+
+ return;
+
+ }
+
+ //
+ // Send system packet next retransmission.
+ //
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ } else {
+
+ //
+ // Send system packet next retransmission.
+ //
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ }
+
+ LongByteSwap( BurstHeader->DataSize, DataSize );
+ LongByteSwap( BurstHeader->BurstOffset, BurstOffset );
+ ShortByteSwap( BurstHeader->BurstLength, BurstLength );
+ BurstHeader->MissingFragmentCount = 0;
+
+ BurstHeaderMdl->ByteCount = sizeof( *BurstHeader );
+ BurstHeaderMdl->Next = BurstDataMdl;
+
+ return;
+}
+
+
+NTSTATUS
+SendSecondaryPacket(
+ PIRP_CONTEXT IrpContext,
+ PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine submits a TDI send request to the tranport layer.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for the request
+ being processed.
+
+ Irp - The IRP for the packet to send.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PNONPAGED_SCB pNpScb;
+ NTSTATUS Status;
+ PNCP_BURST_HEADER BurstHeader;
+ pNpScb = IrpContext->pNpScb;
+
+ DebugTrace( 0, Dbg, "SendSecondaryPacket\n", 0 );
+
+ BurstHeader = (PNCP_BURST_HEADER)( MmGetMdlVirtualAddress( Irp->MdlAddress ) );
+
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE ) ) {
+ pNpScb->OkToReceive = TRUE;
+ }
+
+ LongByteSwap( BurstHeader->PacketSequenceNo, pNpScb->BurstSequenceNo );
+ pNpScb->BurstSequenceNo++;
+
+ ShortByteSwap( BurstHeader->BurstSequenceNo, pNpScb->BurstRequestNo );
+ ShortByteSwap( BurstHeader->AckSequenceNo, pNpScb->BurstRequestNo );
+
+ DebugTrace( +0, Dbg, "Irp %X\n", Irp );
+ DebugTrace( +0, Dbg, "pIrpC %X\n", IrpContext);
+
+#if NWDBG
+ dumpMdl( Dbg, IrpContext->TxMdl);
+#endif
+
+ Stats.BytesTransmitted.QuadPart += MdlLength( Irp->MdlAddress );
+ Stats.NcpsTransmitted.QuadPart += 1;
+
+ Status = IoCallDriver( pNpScb->Server.pDeviceObject, Irp );
+ DebugTrace( -1, Dbg, " %X\n", Status );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ Error( EVENT_NWRDR_NETWORK_ERROR, Status, NULL, 0, 0 );
+ }
+
+ return Status;
+}
+
+#if NWFASTIO
+
+BOOLEAN
+NwFastWrite (
+ 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 offset;
+ BOOLEAN wroteToCache;
+
+ DebugTrace(+1, Dbg, "NwFastWrite...\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, "NwFastWrite -> 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, "NwFastWrite -> FALSE\n", 0);
+ return FALSE;
+ }
+
+ fcb = (PFCB)icb->SuperType.Fcb;
+ nodeTypeCode = fcb->NodeTypeCode;
+ offset = FileOffset->LowPart;
+
+ IoStatus->Status = STATUS_SUCCESS;
+ IoStatus->Information = Length;
+
+ wroteToCache = CacheWrite(
+ NULL,
+ fcb->NonPagedFcb,
+ offset,
+ Length,
+ Buffer );
+
+ DebugTrace(-1, Dbg, "NwFastWrite -> %s\n", wroteToCache ? "TRUE" : "FALSE" );
+
+ if ( wroteToCache ) {
+
+ //
+ // If the file was extended, record the new file size.
+ //
+
+ if ( ( offset + Length ) > fcb->NonPagedFcb->Header.FileSize.LowPart ) {
+ fcb->NonPagedFcb->Header.FileSize.LowPart = ( offset + Length );
+ }
+ }
+
+#ifndef NT1057
+
+ //
+ // Update the file object if we succeeded. We know that this
+ // is synchronous and not paging io because it's coming in through
+ // the cache.
+ //
+
+ if ( wroteToCache ) {
+ FileObject->CurrentByteOffset.QuadPart += Length;
+ }
+
+#endif
+
+ return( wroteToCache );
+
+}
+#endif