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