You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3099 lines
81 KiB
3099 lines
81 KiB
/*++
|
|
|
|
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_PTR)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
|
|
//
|
|
|
|
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_PTR)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 ) {
|
|
|
|
//
|
|
// Ensure that you are at the head of the queue
|
|
//
|
|
|
|
NwAppendToQueueAndWait( IrpContext );
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// Ensure that you are at the head of the queue
|
|
//
|
|
|
|
NwAppendToQueueAndWait( IrpContext );
|
|
|
|
//
|
|
// 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_PTR)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;
|
|
|
|
//
|
|
// Ensure that you are at the head of the queue
|
|
//
|
|
|
|
NwAppendToQueueAndWait( IrpContext );
|
|
|
|
//
|
|
// 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;
|
|
|
|
//
|
|
// tommye - MS bug 2743 changed the RetryCount from 20 to be based off the
|
|
// default retry count, nudged up a little.
|
|
//
|
|
|
|
pNpScb->RetryCount = DefaultRetryCount * 2;
|
|
|
|
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;
|
|
|
|
//
|
|
// Ensure that you are at the head of the queue
|
|
//
|
|
|
|
NwAppendToQueueAndWait( IrpContext );
|
|
|
|
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;
|
|
|
|
MmGetSystemAddressForMdlSafe( DataMdl, NormalPagePriority );
|
|
|
|
//
|
|
// 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 = InterlockedDecrement(
|
|
&pIrpContext->Specific.Write.PacketCount );
|
|
|
|
if ( Result ) {
|
|
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 ) {
|
|
|
|
InterlockedDecrement(
|
|
&IrpContext->Specific.Write.PacketCount );
|
|
|
|
if ( Length == IrpContext->Specific.Write.BurstLength ) {
|
|
MoreData = FALSE;
|
|
break;
|
|
}
|
|
|
|
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 );
|
|
|
|
InterlockedDecrement(
|
|
&IrpContext->Specific.Write.PacketCount );
|
|
|
|
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 );
|
|
|
|
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_PTR)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;
|
|
|
|
try {
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
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 = FileOffset->QuadPart + Length;
|
|
}
|
|
|
|
#endif
|
|
|
|
return( wroteToCache );
|
|
|
|
} finally {
|
|
|
|
FsRtlExitFileSystem();
|
|
}
|
|
|
|
}
|
|
#endif
|