mirror of https://github.com/lianthony/NT4.0
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.
3742 lines
103 KiB
3742 lines
103 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
tranfile.c
|
|
|
|
Abstract:
|
|
|
|
This module contains support for fast kernel-level file transmission
|
|
over a socket handle.
|
|
|
|
Author:
|
|
|
|
David Treadwell (davidtr) 3-Aug-1994
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "afdp.h"
|
|
|
|
//
|
|
// Context structure for deferring a MDL read completion.
|
|
//
|
|
|
|
typedef struct _AFD_MDL_COMPLETION_CONTEXT {
|
|
|
|
AFD_WORK_ITEM WorkItem;
|
|
PFILE_OBJECT FileObject;
|
|
PMDL MdlChain;
|
|
LONGLONG FileOffset;
|
|
ULONG Length;
|
|
|
|
} AFD_MDL_COMPLETION_CONTEXT, *PAFD_MDL_COMPLETION_CONTEXT;
|
|
|
|
//
|
|
// A structure for tracking info on checked builds.
|
|
//
|
|
|
|
#if DBG
|
|
typedef struct _AFD_TRANSMIT_TRACE_INFO {
|
|
PVOID Caller;
|
|
PVOID CallersCaller;
|
|
ULONG Reason;
|
|
PVOID OtherInfo;
|
|
} AFD_TRANSMIT_TRACE_INFO, *PAFD_TRANSMIT_TRACE_INFO;
|
|
#endif
|
|
|
|
NTSTATUS
|
|
AfdBuildReadIrp (
|
|
IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo
|
|
);
|
|
|
|
ULONG
|
|
AfdBuildTransmitSendMdls (
|
|
IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo,
|
|
IN PMDL FileMdlChain,
|
|
IN ULONG FileDataLength,
|
|
IN PAFD_TRANSMIT_IRP_INFO IrpInfo,
|
|
OUT PIRP *DisconnectIrp
|
|
);
|
|
|
|
BOOLEAN
|
|
AfdCancelIrp (
|
|
IN PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
AfdCompleteTransmit (
|
|
IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo,
|
|
IN NTSTATUS Status,
|
|
IN KIRQL OldIrql
|
|
);
|
|
|
|
VOID
|
|
AfdQueueTransmitRead (
|
|
IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdRestartTransmitRead (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdRestartTransmitSend (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
VOID
|
|
AfdStartTransmitIo (
|
|
IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo
|
|
);
|
|
|
|
VOID
|
|
AfdStartNextQueuedTransmitFile(
|
|
VOID
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdRestartMdlReadComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
VOID
|
|
AfdDeferredMdlReadComplete(
|
|
IN PVOID Context
|
|
);
|
|
|
|
#define AfdAllocateMdlCompletionContext() \
|
|
AFD_ALLOCATE_POOL( \
|
|
NonPagedPool, \
|
|
sizeof(AFD_MDL_COMPLETION_CONTEXT), \
|
|
AFD_MDL_COMPLETION_CONTEXT_POOL_TAG \
|
|
)
|
|
|
|
#define AfdFreeMdlCompletionContext(context) \
|
|
AFD_FREE_POOL( \
|
|
(context), \
|
|
AFD_MDL_COMPLETION_CONTEXT_POOL_TAG \
|
|
)
|
|
|
|
|
|
//
|
|
// Macros to control the status of an IRP.
|
|
//
|
|
|
|
#define SET_IRP_BUSY(_irp) ((_irp)->UserIosb = (PVOID)0xFFFFFFFF)
|
|
#define SET_IRP_FREE(_irp) ((_irp)->UserIosb = (PVOID)0)
|
|
#define IS_IRP_BUSY(_irp) ((_irp)->UserIosb != 0)
|
|
|
|
//
|
|
// A macro to rebuild a send IRP to contain the appropriate information.
|
|
// This is required because the IO system zeros each IRP stack location
|
|
// whenever an IRP completes.
|
|
//
|
|
|
|
|
|
#define AfdRebuildSend(Irp, FileObj, SendLen)\
|
|
{ \
|
|
PTDI_REQUEST_KERNEL_SEND p; \
|
|
PIO_STACK_LOCATION _IRPSP; \
|
|
_IRPSP = IoGetNextIrpStackLocation (Irp); \
|
|
ASSERT( _IRPSP->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL ); \
|
|
_IRPSP->MinorFunction = TDI_SEND; \
|
|
_IRPSP->FileObject = FileObj; \
|
|
_IRPSP->Control = SL_INVOKE_ON_SUCCESS | \
|
|
SL_INVOKE_ON_ERROR | \
|
|
SL_INVOKE_ON_CANCEL; \
|
|
p = (PTDI_REQUEST_KERNEL_SEND)&_IRPSP->Parameters; \
|
|
p->SendLength = SendLen; \
|
|
}
|
|
|
|
//
|
|
// Debugging macros.
|
|
//
|
|
|
|
#if DBG
|
|
|
|
#define MAX_TRACE 255
|
|
|
|
#if defined(_X86_)
|
|
#define UPDATE_TRANSMIT_DEBUG_INFO( _ti, _reason, _other ) \
|
|
{ \
|
|
PAFD_TRANSMIT_TRACE_INFO _trace; \
|
|
PULONG _index = (PULONG)(&((_ti)->Debug2)); \
|
|
\
|
|
*_index += 1; \
|
|
\
|
|
if ( *_index == MAX_TRACE ) { \
|
|
*_index = 0; \
|
|
} \
|
|
\
|
|
_trace = (PAFD_TRANSMIT_TRACE_INFO)(_ti)->Debug1 + *_index; \
|
|
\
|
|
RtlGetCallersAddress( &_trace->Caller, &_trace->CallersCaller ); \
|
|
\
|
|
_trace->Reason = _reason; \
|
|
_trace->OtherInfo = _other; \
|
|
}
|
|
#else
|
|
#define UPDATE_TRANSMIT_DEBUG_INFO( _ti, _reason, _other )
|
|
#endif \
|
|
|
|
#define SET_READ_PENDING(_ti, _value) \
|
|
{ \
|
|
ASSERT( KeGetCurrentIrql( ) >= DISPATCH_LEVEL ); \
|
|
if (_value) { \
|
|
ASSERT( !(_ti)->ReadPending ); \
|
|
(_ti)->ReadPendingLastSetTrueLine = __LINE__; \
|
|
} else { \
|
|
ASSERT( (_ti)->ReadPending ); \
|
|
(_ti)->ReadPendingLastSetFalseLine = __LINE__; \
|
|
} \
|
|
(_ti)->ReadPending = (_value); \
|
|
}
|
|
|
|
#else
|
|
#define UPDATE_TRANSMIT_DEBUG_INFO( _ti, _reason, _other )
|
|
#define SET_READ_PENDING(_ti,_value) (_ti)->ReadPending = (_value)
|
|
#endif
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGEAFD, AfdTransmitFile )
|
|
#pragma alloc_text( PAGEAFD, AfdStartTransmitIo )
|
|
#pragma alloc_text( PAGEAFD, AfdRestartTransmitRead )
|
|
#pragma alloc_text( PAGEAFD, AfdRestartTransmitSend )
|
|
#pragma alloc_text( PAGEAFD, AfdCompleteTransmit )
|
|
#pragma alloc_text( PAGEAFD, AfdBuildReadIrp )
|
|
#pragma alloc_text( PAGEAFD, AfdBuildTransmitSendMdls )
|
|
#pragma alloc_text( PAGEAFD, AfdCancelIrp )
|
|
#pragma alloc_text( PAGEAFD, AfdCancelTransmit )
|
|
#pragma alloc_text( PAGEAFD, AfdQueueTransmitRead )
|
|
#pragma alloc_text( PAGEAFD, AfdCompleteClosePendedTransmit )
|
|
#pragma alloc_text( PAGEAFD, AfdStartNextQueuedTransmitFile )
|
|
#pragma alloc_text( PAGEAFD, AfdFastTransmitFile )
|
|
#pragma alloc_text( PAGEAFD, AfdMdlReadComplete )
|
|
#pragma alloc_text( PAGEAFD, AfdRestartMdlReadComplete )
|
|
#pragma alloc_text( PAGEAFD, AfdDeferredMdlReadComplete )
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
AfdTransmitFile (
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initial entrypoint for handling transmit file IRPs. This routine
|
|
verifies parameters, initializes data structures to be used for
|
|
the request, and initiates the I/O.
|
|
|
|
Arguments:
|
|
|
|
Irp - a pointer to a transmit file IRP.
|
|
|
|
IrpSp - Our stack location for this IRP.
|
|
|
|
Return Value:
|
|
|
|
STATUS_PENDING if the request was initiated successfully, or a
|
|
failure status code if there was an error.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAFD_ENDPOINT endpoint;
|
|
PAFD_CONNECTION connection;
|
|
NTSTATUS status;
|
|
PAFD_TRANSMIT_FILE_INFO_INTERNAL transmitInfo = NULL;
|
|
KIRQL oldIrql;
|
|
BOOLEAN lockedHead;
|
|
BOOLEAN lockedTail;
|
|
BOOLEAN clearTransmitIrpOnFailure = FALSE;
|
|
|
|
//
|
|
// Initial request validity checks: is the endpoint connected, is
|
|
// the input buffer large enough, etc.
|
|
//
|
|
|
|
endpoint = IrpSp->FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
if ( endpoint->Type != AfdBlockTypeVcConnecting ||
|
|
endpoint->State != AfdEndpointStateConnected ) {
|
|
status = STATUS_INVALID_CONNECTION;
|
|
goto complete;
|
|
}
|
|
|
|
connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint );
|
|
ASSERT( connection->Type == AfdBlockTypeConnection );
|
|
|
|
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(AFD_TRANSMIT_FILE_INFO) ) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Ensure there are no other TransmitFile IRPs pending on this
|
|
// endpoint. Only allowing one at a time makes completion and
|
|
// cancellation much simpler.
|
|
//
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
|
|
|
if( endpoint->TransmitIrp != NULL ) {
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
|
|
}
|
|
|
|
endpoint->TransmitIrp = Irp;
|
|
clearTransmitIrpOnFailure = TRUE;
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
|
|
//
|
|
// If this endpoint already has an internal info buffer, use it.
|
|
// Otherwise, allocate a new one from nonpaged pool. We will use
|
|
// this structure to track the request. We'll also store a pointer
|
|
// to the structure in the IRP so we can access it whenever we
|
|
// have a pointer to the IRP.
|
|
//
|
|
|
|
transmitInfo = endpoint->TransmitInfo;
|
|
|
|
if( transmitInfo == NULL ) {
|
|
|
|
transmitInfo = AFD_ALLOCATE_POOL(
|
|
NonPagedPool,
|
|
sizeof(*transmitInfo),
|
|
AFD_TRANSMIT_INFO_POOL_TAG
|
|
);
|
|
|
|
if ( transmitInfo == NULL ) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto complete;
|
|
|
|
}
|
|
|
|
endpoint->TransmitInfo = transmitInfo;
|
|
|
|
}
|
|
|
|
Irp->AssociatedIrp.SystemBuffer = transmitInfo;
|
|
|
|
//
|
|
// NULL the head and tail MDL pointers so that we know whether
|
|
// we need to unlock and free them on exit.
|
|
//
|
|
|
|
transmitInfo->HeadMdl = NULL;
|
|
lockedHead = FALSE;
|
|
transmitInfo->TailMdl = NULL;
|
|
lockedTail = FALSE;
|
|
|
|
//
|
|
// Set up the rest of the transmit info structure. input buffer,
|
|
// then set up defaults.
|
|
//
|
|
|
|
transmitInfo->FileObject = NULL;
|
|
transmitInfo->TdiFileObject = connection->FileObject;
|
|
transmitInfo->TdiDeviceObject = connection->DeviceObject;
|
|
transmitInfo->BytesRead = 0;
|
|
transmitInfo->BytesSent = 0;
|
|
|
|
transmitInfo->TransmitIrp = Irp;
|
|
transmitInfo->Endpoint = endpoint;
|
|
transmitInfo->FileMdl = NULL;
|
|
transmitInfo->ReadPending = TRUE;
|
|
transmitInfo->CompletionPending = FALSE;
|
|
|
|
transmitInfo->FirstFileMdlAfterHead = NULL;
|
|
transmitInfo->LastFileMdlBeforeTail = NULL;
|
|
transmitInfo->IrpUsedToSendTail = NULL;
|
|
|
|
transmitInfo->Read.Irp = NULL;
|
|
transmitInfo->Read.AfdBuffer = NULL;
|
|
|
|
transmitInfo->Send1.Irp = NULL;
|
|
transmitInfo->Send1.AfdBuffer = NULL;
|
|
|
|
transmitInfo->Send2.Irp = NULL;
|
|
transmitInfo->Send2.AfdBuffer = NULL;
|
|
|
|
transmitInfo->Queued = FALSE;
|
|
|
|
#if DBG
|
|
transmitInfo->Debug1 = NULL;
|
|
#endif
|
|
|
|
//
|
|
// Because we're using type 3 (neither) I/O for this IRP, the I/O
|
|
// system does no verification on the user buffer. Therefore, we
|
|
// must manually check it for validity inside a try-except block.
|
|
// We also leverage the try-except to validate and lock down the
|
|
// head and/or tail buffers specified by the caller.
|
|
//
|
|
|
|
try {
|
|
|
|
if( Irp->RequestorMode != KernelMode ) {
|
|
|
|
//
|
|
// Validate the control buffer.
|
|
//
|
|
|
|
ProbeForRead(
|
|
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
|
|
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Copy over the user's buffer into our information structure.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
transmitInfo,
|
|
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
|
|
sizeof(AFD_TRANSMIT_FILE_INFO)
|
|
);
|
|
|
|
//
|
|
// If the caller specified head and/or tail buffers, probe and
|
|
// lock the buffers so that we have MDLs we can use to send the
|
|
// buffers.
|
|
//
|
|
|
|
if ( transmitInfo->HeadLength > 0 ) {
|
|
|
|
transmitInfo->HeadMdl = IoAllocateMdl(
|
|
transmitInfo->Head,
|
|
transmitInfo->HeadLength,
|
|
FALSE, // SecondaryBuffer
|
|
TRUE, // ChargeQuota
|
|
NULL // Irp
|
|
);
|
|
if ( transmitInfo->HeadMdl == NULL ) {
|
|
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
|
|
MmProbeAndLockPages( transmitInfo->HeadMdl, UserMode, IoReadAccess );
|
|
lockedHead = TRUE;
|
|
|
|
//
|
|
// Remember that we will need to send this head buffer.
|
|
// This flag will be reset as soon as the head buffer is
|
|
// given to the TDI provider.
|
|
//
|
|
|
|
transmitInfo->NeedToSendHead = TRUE;
|
|
|
|
} else {
|
|
|
|
transmitInfo->NeedToSendHead = FALSE;
|
|
}
|
|
|
|
if ( transmitInfo->TailLength > 0 ) {
|
|
|
|
transmitInfo->TailMdl = IoAllocateMdl(
|
|
transmitInfo->Tail,
|
|
transmitInfo->TailLength,
|
|
FALSE, // SecondaryBuffer
|
|
TRUE, // ChargeQuota
|
|
NULL // Irp
|
|
);
|
|
if ( transmitInfo->TailMdl == NULL ) {
|
|
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
|
|
MmProbeAndLockPages( transmitInfo->TailMdl, UserMode, IoReadAccess );
|
|
lockedTail = TRUE;
|
|
}
|
|
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
status = GetExceptionCode();
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Set up some tracking information for debugging purposes.
|
|
//
|
|
|
|
#if DBG
|
|
transmitInfo->Completed = FALSE;
|
|
|
|
transmitInfo->ReadPendingLastSetTrueLine = 0xFFFFFFFF;
|
|
transmitInfo->ReadPendingLastSetFalseLine = 0xFFFFFFFF;
|
|
|
|
transmitInfo->Debug1 = AFD_ALLOCATE_POOL(
|
|
NonPagedPool,
|
|
sizeof(AFD_TRANSMIT_TRACE_INFO) * MAX_TRACE,
|
|
AFD_TRANSMIT_DEBUG_POOL_TAG
|
|
);
|
|
|
|
if ( transmitInfo->Debug1 == NULL ) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto complete;
|
|
|
|
}
|
|
|
|
transmitInfo->Debug2 = (PVOID)0;
|
|
|
|
RtlZeroMemory(
|
|
transmitInfo->Debug1,
|
|
sizeof(AFD_TRANSMIT_TRACE_INFO) * MAX_TRACE
|
|
);
|
|
#endif
|
|
|
|
//
|
|
// Validate any flags specified in the request.
|
|
//
|
|
|
|
if ( (transmitInfo->Flags &
|
|
~(AFD_TF_WRITE_BEHIND | AFD_TF_DISCONNECT | AFD_TF_REUSE_SOCKET) )
|
|
!= 0 ) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Setting AFD_TF_REUSE_SOCKET implies that a disconnect is desired.
|
|
// Also, setting this flag means that no more I/O is legal on the
|
|
// endpoint until the transmit request has been completed, so
|
|
// set up the endpoint's state so that I/O fails.
|
|
//
|
|
|
|
if ( (transmitInfo->Flags & AFD_TF_REUSE_SOCKET) != 0 ) {
|
|
transmitInfo->Flags |= AFD_TF_DISCONNECT;
|
|
endpoint->State = AfdEndpointStateTransmitClosing;
|
|
}
|
|
|
|
//
|
|
// Get a referenced pointer to the file object for the file that
|
|
// we're going to transmit. This call will fail if the file handle
|
|
// specified by the caller is invalid.
|
|
//
|
|
|
|
status = ObReferenceObjectByHandle(
|
|
transmitInfo->FileHandle,
|
|
FILE_READ_DATA,
|
|
*IoFileObjectType,
|
|
UserMode,
|
|
(PVOID *)&transmitInfo->FileObject,
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto complete;
|
|
}
|
|
|
|
AfdRecordFileRef();
|
|
|
|
//
|
|
// Get the device object for the file system that supports this
|
|
// file. We can't just use the device object stored in the file
|
|
// object because that device object typically refers to the disk
|
|
// driver device and we want the file system device object.
|
|
//
|
|
|
|
transmitInfo->DeviceObject =
|
|
IoGetRelatedDeviceObject( transmitInfo->FileObject );
|
|
|
|
//
|
|
// If this is a synchronous file object AND the offset specified
|
|
// by the caller is zero, then start the transmission from the
|
|
// current byte offset in the file.
|
|
//
|
|
|
|
if ( transmitInfo->FileObject->Flags & FO_SYNCHRONOUS_IO &&
|
|
transmitInfo->Offset == 0 ) {
|
|
|
|
transmitInfo->Offset = transmitInfo->FileObject->CurrentByteOffset.QuadPart;
|
|
}
|
|
|
|
//
|
|
// If the caller requested that we transmit the entire file,
|
|
// determine current file length so that we know when to quit.
|
|
//
|
|
|
|
if ( transmitInfo->FileWriteLength == 0 ) {
|
|
|
|
FILE_STANDARD_INFORMATION fileInfo;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
|
|
status = ZwQueryInformationFile(
|
|
transmitInfo->FileHandle,
|
|
&ioStatusBlock,
|
|
&fileInfo,
|
|
sizeof(fileInfo),
|
|
FileStandardInformation
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Calculate the number of bytes to send from the file.
|
|
// This is the total file length less the specified offset.
|
|
//
|
|
|
|
transmitInfo->FileWriteLength =
|
|
fileInfo.EndOfFile.QuadPart - transmitInfo->Offset;
|
|
}
|
|
|
|
//
|
|
// Determine the total number of bytes we will send, including head
|
|
// and tail buffers.
|
|
//
|
|
|
|
transmitInfo->TotalBytesToSend =
|
|
transmitInfo->FileWriteLength +
|
|
transmitInfo->HeadLength + transmitInfo->TailLength;
|
|
|
|
//
|
|
// Allocate and initialize the IRPs we'll give to the TDI provider
|
|
// to actually send data. We allocate them with a stack size one
|
|
// larger than requested by the TDI provider so that we have a stack
|
|
// location for ourselves.
|
|
//
|
|
// !!! It would be good not to allocate the second send IRP unless
|
|
// we knew it was really necessary.
|
|
//
|
|
|
|
transmitInfo->Send1.Irp =
|
|
IoAllocateIrp( connection->DeviceObject->StackSize, TRUE );
|
|
|
|
if ( transmitInfo->Send1.Irp == NULL ) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto complete;
|
|
}
|
|
|
|
TdiBuildSend(
|
|
transmitInfo->Send1.Irp,
|
|
connection->DeviceObject,
|
|
connection->FileObject,
|
|
AfdRestartTransmitSend,
|
|
transmitInfo,
|
|
NULL,
|
|
0,
|
|
0,
|
|
);
|
|
|
|
transmitInfo->Send2.Irp =
|
|
IoAllocateIrp( connection->DeviceObject->StackSize, TRUE );
|
|
|
|
if ( transmitInfo->Send2.Irp == NULL ) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto complete;
|
|
}
|
|
|
|
TdiBuildSend(
|
|
transmitInfo->Send2.Irp,
|
|
connection->DeviceObject,
|
|
connection->FileObject,
|
|
AfdRestartTransmitSend,
|
|
transmitInfo,
|
|
NULL,
|
|
0,
|
|
0,
|
|
);
|
|
|
|
//
|
|
// Determine the maximum read size that we'll use. If specified by
|
|
// the caller, respect it. Otherwise, choose an intelligent default
|
|
// based on whether the file system of interest supports the cache
|
|
// manager routines for fast file I/O. If the file system does not
|
|
// support the cache manager interfaces, we will need to allocate
|
|
// our own buffers so use a smaller, more sensible size.
|
|
//
|
|
|
|
if ( transmitInfo->SendPacketLength == 0 ) {
|
|
if ( (transmitInfo->FileObject->Flags & FO_CACHE_SUPPORTED) != 0 ) {
|
|
transmitInfo->SendPacketLength = AfdTransmitIoLength;
|
|
} else {
|
|
transmitInfo->SendPacketLength = AfdLargeBufferSize;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if the transmit IRP has been cancelled. If so, quit
|
|
// now.
|
|
//
|
|
|
|
IoAcquireCancelSpinLock( &oldIrql );
|
|
|
|
if ( Irp->Cancel ) {
|
|
IoReleaseCancelSpinLock( oldIrql );
|
|
status = STATUS_CANCELLED;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Mark the transmit IRP as pending and set up the status field
|
|
// in the IRP. We leave it as STATUS_SUCCESS until some sort of
|
|
// failure occurs, at which point we modify to the failure
|
|
// code.
|
|
//
|
|
|
|
IoMarkIrpPending( Irp );
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Set up the cancel routine in the IRP. The IRP pointer in the endpoint
|
|
// should already point to the current IRP.
|
|
//
|
|
|
|
IoSetCancelRoutine( Irp, AfdCancelTransmit );
|
|
ASSERT( endpoint->TransmitIrp == Irp );
|
|
|
|
//
|
|
// Determine if we can really start this file transmit now. If we've
|
|
// exceeded the configured maximum number of active TransmitFile
|
|
// requests, then append this IRP to the TransmitFile queue and set
|
|
// a flag in the transmit info structure to indicate that this IRP
|
|
// is queued.
|
|
//
|
|
|
|
if( AfdMaxActiveTransmitFileCount > 0 ) {
|
|
|
|
if( AfdActiveTransmitFileCount >= AfdMaxActiveTransmitFileCount ) {
|
|
|
|
InsertTailList(
|
|
&AfdQueuedTransmitFileListHead,
|
|
&Irp->Tail.Overlay.ListEntry
|
|
);
|
|
|
|
transmitInfo->Queued = TRUE;
|
|
IoReleaseCancelSpinLock( oldIrql );
|
|
return STATUS_PENDING;
|
|
|
|
} else {
|
|
|
|
AfdActiveTransmitFileCount++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
IoReleaseCancelSpinLock( oldIrql );
|
|
|
|
//
|
|
// Start I/O for the file transmit.
|
|
//
|
|
|
|
AfdStartTransmitIo( transmitInfo );
|
|
|
|
//
|
|
// Everything looks good so far. Indicate to the caller that we
|
|
// successfully pended their request.
|
|
//
|
|
|
|
return STATUS_PENDING;
|
|
|
|
complete:
|
|
|
|
//
|
|
// There was a failure of some sort. Clean up and exit.
|
|
//
|
|
|
|
if ( transmitInfo != NULL ) {
|
|
|
|
if ( transmitInfo->HeadMdl != NULL ) {
|
|
if ( lockedHead ) {
|
|
MmUnlockPages( transmitInfo->HeadMdl );
|
|
}
|
|
IoFreeMdl( transmitInfo->HeadMdl );
|
|
}
|
|
|
|
if ( transmitInfo->TailMdl != NULL ) {
|
|
if ( lockedTail ) {
|
|
MmUnlockPages( transmitInfo->TailMdl );
|
|
}
|
|
IoFreeMdl( transmitInfo->TailMdl );
|
|
}
|
|
|
|
if ( transmitInfo->FileObject != NULL ) {
|
|
ObDereferenceObject( transmitInfo->FileObject );
|
|
AfdRecordFileDeref();
|
|
}
|
|
|
|
if ( transmitInfo->Read.Irp != NULL ) {
|
|
IoFreeIrp( transmitInfo->Read.Irp );
|
|
}
|
|
|
|
if ( transmitInfo->Send1.Irp != NULL ) {
|
|
IoFreeIrp( transmitInfo->Send1.Irp );
|
|
}
|
|
|
|
if ( transmitInfo->Send2.Irp != NULL ) {
|
|
IoFreeIrp( transmitInfo->Send2.Irp );
|
|
}
|
|
|
|
#if DBG
|
|
if ( transmitInfo->Debug1 != NULL ) {
|
|
AFD_FREE_POOL(
|
|
transmitInfo->Debug1,
|
|
AFD_TRANSMIT_DEBUG_POOL_TAG
|
|
);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Don't free the transmit info structure here, leave
|
|
// it attached to the endpoint. The structure will get
|
|
// freed when we free the endpoint.
|
|
//
|
|
|
|
}
|
|
|
|
//
|
|
// Zap the IRP pointer from the endpoint if necessary.
|
|
//
|
|
|
|
if( clearTransmitIrpOnFailure ) {
|
|
endpoint->TransmitIrp = NULL;
|
|
}
|
|
|
|
//
|
|
// Complete the request.
|
|
//
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest( Irp, 0 );
|
|
|
|
return status;
|
|
|
|
} // AfdTransmitFile
|
|
|
|
|
|
VOID
|
|
AfdStartTransmitIo (
|
|
IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the key routine for initiating transmit I/O. In the high-
|
|
performance case of reading a file from the system cache, this
|
|
routine gets file data MDLs and passes them off to the TDI provider.
|
|
If it cannot get file data inline, it pends a read IRP to get the
|
|
file data.
|
|
|
|
Arguments:
|
|
|
|
TransmitInfo - a pointer to the TransmitInfo structure which
|
|
contains information about the request to process.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
Because it is illegal to call the FsRtl fast read routines at raised
|
|
IRQL or from within a completion routine, this routine must be
|
|
called from a thread whose context we own. This routine cannot be
|
|
called at distapch level or from a completion routine.
|
|
|
|
--*/
|
|
|
|
{
|
|
IO_STATUS_BLOCK ioStatus;
|
|
BOOLEAN success;
|
|
PMDL fileMdl;
|
|
KIRQL oldIrql;
|
|
KIRQL cancelIrql;
|
|
PAFD_ENDPOINT endpoint;
|
|
PAFD_TRANSMIT_IRP_INFO irpInfo;
|
|
ULONG readLength;
|
|
ULONG sendLength;
|
|
LONGLONG readOffset;
|
|
NTSTATUS status;
|
|
PIRP disconnectIrp;
|
|
PDEVICE_OBJECT tdiDeviceObject;
|
|
|
|
//
|
|
// It is illegal to call the fast I/O routines or to submit MDL read
|
|
// IRPs at raised IRQL.
|
|
//
|
|
|
|
ASSERT( KeGetCurrentIrql( ) < DISPATCH_LEVEL );
|
|
|
|
//
|
|
// Initialize local variables.
|
|
//
|
|
|
|
endpoint = TransmitInfo->Endpoint;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
ASSERT( endpoint->TransmitInfo == TransmitInfo );
|
|
|
|
ASSERT( TransmitInfo->FileMdl == NULL );
|
|
ASSERT( TransmitInfo->ReadPending );
|
|
ASSERT( TransmitInfo->TransmitIrp != NULL );
|
|
|
|
//
|
|
// We have to have a special case to handle a file of length 0.
|
|
//
|
|
|
|
if ( TransmitInfo->FileWriteLength == 0 ) {
|
|
|
|
//
|
|
// Remember that there is not a read pending.
|
|
//
|
|
|
|
TransmitInfo->ReadPending = FALSE;
|
|
|
|
//
|
|
// If there was neither a head nor tail, just complete the
|
|
// transmit request now.
|
|
//
|
|
|
|
if ( TransmitInfo->TotalBytesToSend == 0 ) {
|
|
|
|
//
|
|
// If we need to initiate a disconnect on the endpoint, do
|
|
// so.
|
|
//
|
|
|
|
if ( (TransmitInfo->Flags & AFD_TF_DISCONNECT) != 0 ) {
|
|
(VOID)AfdBeginDisconnect( endpoint, NULL, NULL );
|
|
}
|
|
|
|
//
|
|
// We must hold the cancel spin lock when completing the
|
|
// transmit IRP.
|
|
//
|
|
|
|
IoAcquireCancelSpinLock( &oldIrql );
|
|
AfdCompleteTransmit( TransmitInfo, STATUS_SUCCESS, oldIrql );
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// There is a head and/or a tail buffer to send. Build an IRP
|
|
// and send the buffer(s).
|
|
//
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
|
|
|
sendLength = AfdBuildTransmitSendMdls(
|
|
TransmitInfo,
|
|
NULL,
|
|
0,
|
|
&TransmitInfo->Send1,
|
|
&disconnectIrp
|
|
);
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
|
|
//
|
|
// Remember the TDI device object in a local variable. This is
|
|
// necessary because as soon as we call IoCallDriver for the
|
|
// send, the TransmitInfo structure may be freed up and have
|
|
// been reset.
|
|
//
|
|
|
|
tdiDeviceObject = TransmitInfo->TdiDeviceObject;
|
|
|
|
//
|
|
// We'll use the first send IRP to send the head/tail buffers.
|
|
//
|
|
|
|
SET_IRP_BUSY( TransmitInfo->Send1.Irp );
|
|
|
|
AfdRebuildSend(
|
|
TransmitInfo->Send1.Irp,
|
|
TransmitInfo->TdiFileObject,
|
|
sendLength
|
|
);
|
|
|
|
//
|
|
// Send the head and/or tail data.
|
|
//
|
|
|
|
IoCallDriver( tdiDeviceObject, TransmitInfo->Send1.Irp );
|
|
|
|
//
|
|
// If appropriate, submit a graceful disconnect on the endpoint.
|
|
//
|
|
|
|
if ( disconnectIrp != NULL ) {
|
|
IoCallDriver( tdiDeviceObject, disconnectIrp );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If the file system supports the fast cache manager interface,
|
|
// attempt to use the fast path to get file data MDLs.
|
|
//
|
|
|
|
if ( (TransmitInfo->FileObject->Flags & FO_CACHE_SUPPORTED) != 0 ) {
|
|
|
|
while ( TRUE ) {
|
|
|
|
//
|
|
// Set fileMdl to NULL because FsRtlMdlRead attempts to
|
|
// chain the MDLs it returns off the input MDL variable.
|
|
//
|
|
|
|
fileMdl = NULL;
|
|
|
|
//
|
|
// Determine how many bytes we will attempt to read. This
|
|
// is either the send packet size or the remaining bytes in
|
|
// the file, whichever is less.
|
|
//
|
|
|
|
if ( TransmitInfo->FileWriteLength -
|
|
TransmitInfo->BytesRead >
|
|
TransmitInfo->SendPacketLength ) {
|
|
readLength = TransmitInfo->SendPacketLength;
|
|
} else {
|
|
readLength = (ULONG)(TransmitInfo->FileWriteLength -
|
|
TransmitInfo->BytesRead);
|
|
}
|
|
|
|
TransmitInfo->Read.Length = readLength;
|
|
|
|
//
|
|
// If we have read everything we needed to read from the file,
|
|
// quit reading.
|
|
//
|
|
|
|
if ( readLength == 0 ) {
|
|
|
|
//
|
|
// Note that we're no longer in the process of reading
|
|
// file data.
|
|
//
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
|
SET_READ_PENDING( TransmitInfo, FALSE );
|
|
|
|
//
|
|
// If we are attempting to process completion of the
|
|
// transmit IRP, pass control to AfdCompleteTransmit()
|
|
// so that it can continue completion processing with
|
|
// the read pending flag turned off.
|
|
//
|
|
|
|
if ( TransmitInfo->CompletionPending ) {
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
IoAcquireCancelSpinLock( &oldIrql );
|
|
AfdCompleteTransmit( TransmitInfo, STATUS_SUCCESS, oldIrql );
|
|
|
|
} else {
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Determine the offset in the file at which to start
|
|
// reading. Because all reads are funnelled through this
|
|
// function, and because this function can run in only one
|
|
// thread at a time, there isn't a synchronization issue on
|
|
// these fields of the TransmitInfo structure.
|
|
//
|
|
|
|
readOffset =
|
|
TransmitInfo->Offset + TransmitInfo->BytesRead;
|
|
|
|
//
|
|
// Attempt to use the fast path to get file data MDLs
|
|
// directly from the cache manager.
|
|
//
|
|
|
|
success = FsRtlMdlRead(
|
|
TransmitInfo->FileObject,
|
|
(PLARGE_INTEGER)&readOffset,
|
|
readLength,
|
|
0,
|
|
&fileMdl,
|
|
&ioStatus
|
|
);
|
|
|
|
//
|
|
// If the fast path succeeded and we have a send IRP available,
|
|
// give the send IRP to the TDI provider.
|
|
//
|
|
|
|
if ( success ) {
|
|
|
|
//
|
|
// If we read less information than was requested, we
|
|
// must have hit the end of the file. Fail the transmit
|
|
// request, since this can only happen if the caller
|
|
// requested that we send more data than the file
|
|
// currently contains.
|
|
//
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
|
|
|
if ( ioStatus.Information < readLength ) {
|
|
|
|
TransmitInfo->CompletionPending = TRUE;
|
|
TransmitInfo->TransmitIrp->IoStatus.Status =
|
|
STATUS_END_OF_FILE;
|
|
|
|
//
|
|
// If we got some MDLs from the file system, return
|
|
// them.
|
|
//
|
|
|
|
if ( fileMdl != NULL ) {
|
|
status = AfdMdlReadComplete(
|
|
TransmitInfo->FileObject,
|
|
fileMdl,
|
|
readOffset,
|
|
readLength
|
|
);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
|
|
fileMdl = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the count of bytes read from the file.
|
|
//
|
|
|
|
TransmitInfo->BytesRead =
|
|
TransmitInfo->BytesRead + ioStatus.Information;
|
|
|
|
//
|
|
// If we're in the process of completing the transmit
|
|
// IRP, do not submit a send. Instead call
|
|
// AfdCompleteTransmit() so that it can continue
|
|
// completing the transmit IRP.
|
|
//
|
|
|
|
if ( TransmitInfo->CompletionPending ) {
|
|
|
|
//
|
|
// Release the endpoint spin lock, grab the cancel
|
|
// spin lock, then reacquire the endpoint lock.
|
|
// This is required in order to preserve lock
|
|
// acquisition ordering.
|
|
//
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
IoAcquireCancelSpinLock( &cancelIrql );
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
|
|
|
//
|
|
// Remember the file MDL and the fact that a read
|
|
// is no longer pending.
|
|
//
|
|
|
|
TransmitInfo->FileMdl = fileMdl;
|
|
SET_READ_PENDING( TransmitInfo, FALSE );
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
|
|
//
|
|
// Now continue completing the transmit IRP.
|
|
//
|
|
|
|
AfdCompleteTransmit(
|
|
TransmitInfo,
|
|
STATUS_SUCCESS,
|
|
cancelIrql
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
ASSERT( fileMdl != NULL );
|
|
ASSERT( NT_SUCCESS(ioStatus.Status) );
|
|
ASSERT( ioStatus.Information == readLength );
|
|
|
|
//
|
|
// Attempt to find a send IRP which is available to use.
|
|
//
|
|
|
|
if ( !IS_IRP_BUSY( TransmitInfo->Send1.Irp ) ) {
|
|
|
|
irpInfo = &TransmitInfo->Send1;
|
|
SET_IRP_BUSY( TransmitInfo->Send1.Irp );
|
|
|
|
} else if ( !IS_IRP_BUSY( TransmitInfo->Send2.Irp ) ) {
|
|
|
|
irpInfo = &TransmitInfo->Send2;
|
|
SET_IRP_BUSY( TransmitInfo->Send2.Irp );
|
|
|
|
} else {
|
|
|
|
//
|
|
// There are no send IRPs that we can use to send
|
|
// this file data. Just hang on to the data until
|
|
// a send IRP completes.
|
|
//
|
|
|
|
TransmitInfo->FileMdl = fileMdl;
|
|
TransmitInfo->FileMdlLength = readLength;
|
|
SET_READ_PENDING( TransmitInfo, FALSE );
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Finish building the send IRP and give it to the TDI
|
|
// provider.
|
|
//
|
|
|
|
sendLength = AfdBuildTransmitSendMdls(
|
|
TransmitInfo,
|
|
fileMdl,
|
|
readLength,
|
|
irpInfo,
|
|
&disconnectIrp
|
|
);
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
|
|
//
|
|
// Remember the TDI device object in a local variable.
|
|
// This is necessary because as soon as we call
|
|
// IoCallDriver for the send, the TransmitInfo structure
|
|
// may be freed up and have been reset.
|
|
//
|
|
|
|
tdiDeviceObject = TransmitInfo->TdiDeviceObject;
|
|
|
|
AfdRebuildSend(
|
|
irpInfo->Irp,
|
|
TransmitInfo->TdiFileObject,
|
|
sendLength
|
|
);
|
|
|
|
IoCallDriver( tdiDeviceObject, irpInfo->Irp );
|
|
|
|
//
|
|
// If appropriate, submit a graceful disconnect on the
|
|
// endpoint.
|
|
//
|
|
|
|
if ( disconnectIrp != NULL ) {
|
|
IoCallDriver( tdiDeviceObject, disconnectIrp );
|
|
}
|
|
|
|
//
|
|
// Try to get more file data to send out.
|
|
//
|
|
|
|
} else {
|
|
|
|
ASSERT( fileMdl == NULL );
|
|
|
|
//
|
|
// Drop through to build an IRP to retrieve the file
|
|
// data.
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Either the file system does not support the fast cache manager
|
|
// interface or else fast I/O failed. We'll have to build a read
|
|
// IRP and submit it to the file system
|
|
//
|
|
|
|
status = AfdBuildReadIrp( TransmitInfo );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
|
SET_READ_PENDING( TransmitInfo, FALSE );
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
|
|
IoAcquireCancelSpinLock( &TransmitInfo->TransmitIrp->CancelIrql );
|
|
AfdCancelTransmit( NULL, TransmitInfo->TransmitIrp );
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If the transmit IRP is in the process of being completed, do not
|
|
// submit the read IRP. Instead, call AfdCompleteTransmit() after
|
|
// turning off the ReadPending bit so that it can continue
|
|
// completion of the transmit IRP.
|
|
//
|
|
|
|
IoAcquireCancelSpinLock( &cancelIrql );
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
|
|
|
if ( TransmitInfo->CompletionPending ) {
|
|
|
|
ASSERT( !IS_IRP_BUSY( TransmitInfo->Read.Irp ) );
|
|
SET_READ_PENDING( TransmitInfo, FALSE );
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
|
|
AfdCompleteTransmit( TransmitInfo, STATUS_SUCCESS, cancelIrql );
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We're committed to posting the read IRP. Remember that it is
|
|
// busy. Note that there is a small window between setting the IRP
|
|
// busy and the actual submission where we may need to cancel the
|
|
// IRP. This is handled by the fact that if we attempt to cancel
|
|
// the IRP before the driver gets it, the Cencel flag will be set in
|
|
// the IRP and the driver should fail it immediately.
|
|
//
|
|
|
|
SET_IRP_BUSY( TransmitInfo->Read.Irp );
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
IoReleaseCancelSpinLock( cancelIrql );
|
|
|
|
IoCallDriver( TransmitInfo->DeviceObject, TransmitInfo->Read.Irp );
|
|
|
|
//
|
|
// Leave the ReadPending flag set in the TransmitInfo structure
|
|
// until the read IRP completes.
|
|
//
|
|
|
|
return;
|
|
|
|
} // AfdStartTransmitIo
|
|
|
|
|
|
NTSTATUS
|
|
AfdRestartTransmitRead (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the completion routine for transmit read IRPs. It updates the
|
|
context based on the result of the read operation and attempts to find
|
|
a send IRP to use to give the read data to the TDI provider. If no
|
|
send IRP is available, it just holds on to the read data until a send
|
|
IRP completes.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - ignored.
|
|
|
|
Irp - the read IRP that is completing.
|
|
|
|
Context - a pointer to the TransmitInfo structure that describes
|
|
the transmit file request corresponding to the IRP that is
|
|
completing.
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED which indicates to the I/O system
|
|
that it should stop completion processing of this IRP. We handle
|
|
all completion from this point forward.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAFD_TRANSMIT_FILE_INFO_INTERNAL transmitInfo;
|
|
PAFD_TRANSMIT_IRP_INFO irpInfo;
|
|
KIRQL oldIrql;
|
|
PAFD_ENDPOINT endpoint;
|
|
PAFD_CONNECTION connection;
|
|
BOOLEAN readMore;
|
|
ULONG sendLength;
|
|
PIRP disconnectIrp;
|
|
PDEVICE_OBJECT tdiDeviceObject;
|
|
|
|
//
|
|
// Initialize local variables.
|
|
//
|
|
|
|
transmitInfo = Context;
|
|
ASSERT( transmitInfo->Read.Irp == Irp );
|
|
|
|
endpoint = transmitInfo->Endpoint;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
ASSERT( endpoint->TransmitInfo == transmitInfo );
|
|
|
|
connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint );
|
|
ASSERT( connection->Type == AfdBlockTypeConnection );
|
|
|
|
ASSERT( IS_IRP_BUSY( Irp ) );
|
|
ASSERT( transmitInfo->ReadPending );
|
|
|
|
//
|
|
// If the read failed or we're in the process of completing the
|
|
// transmit IRP, start/continue the process of completing the
|
|
// transmit IRP.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(Irp->IoStatus.Status) || transmitInfo->CompletionPending ) {
|
|
|
|
KIRQL cancelIrql;
|
|
|
|
IoAcquireCancelSpinLock( &cancelIrql );
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
|
SET_IRP_FREE( Irp );
|
|
SET_READ_PENDING( transmitInfo, FALSE );
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
|
|
KdPrint(( "AfdRestartTransmitRead: failed, status == %lx\n",
|
|
Irp->IoStatus.Status ));
|
|
|
|
AfdCompleteTransmit( transmitInfo, Irp->IoStatus.Status, cancelIrql );
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
//
|
|
// Update the count of bytes we have read from the file so far.
|
|
//
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
|
|
|
transmitInfo->BytesRead =
|
|
transmitInfo->BytesRead + Irp->IoStatus.Information;
|
|
|
|
//
|
|
// If this is a non-cached read, update the MDL byte count in the
|
|
// AFD buffer to reflect the count of bytes actually read.
|
|
//
|
|
|
|
if ( transmitInfo->Read.AfdBuffer != NULL ) {
|
|
transmitInfo->Read.AfdBuffer->Mdl->ByteCount = Irp->IoStatus.Information;
|
|
}
|
|
|
|
//
|
|
// Remember that the read IRP is no longer in use.
|
|
//
|
|
|
|
SET_IRP_FREE( Irp );
|
|
|
|
//
|
|
// Check whether one of the send IRPs is waiting for data.
|
|
//
|
|
|
|
irpInfo = NULL;
|
|
|
|
if ( !IS_IRP_BUSY( transmitInfo->Send1.Irp ) ) {
|
|
irpInfo = &transmitInfo->Send1;
|
|
} else if ( !IS_IRP_BUSY( transmitInfo->Send2.Irp ) ) {
|
|
irpInfo = &transmitInfo->Send2;
|
|
}
|
|
|
|
//
|
|
// If both send IRPs are in the TDI provider, reset the read IRP to
|
|
// "free", remember that there is read data available, and wait for
|
|
// a send to complete.
|
|
//
|
|
|
|
if ( irpInfo == NULL ) {
|
|
|
|
ASSERT( transmitInfo->FileMdl == NULL );
|
|
|
|
transmitInfo->FileMdl = Irp->MdlAddress;
|
|
Irp->MdlAddress = NULL;
|
|
transmitInfo->FileMdlLength = Irp->IoStatus.Information;
|
|
SET_IRP_FREE( Irp );
|
|
SET_READ_PENDING( transmitInfo, FALSE );
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
//
|
|
// There is a send IRP that we'll give to the TDI provider. Move
|
|
// the buffers from the read IRP to the send IRP. If we need to
|
|
// send the head buffer, do so.
|
|
//
|
|
|
|
sendLength = AfdBuildTransmitSendMdls(
|
|
transmitInfo,
|
|
Irp->MdlAddress,
|
|
Irp->IoStatus.Information,
|
|
irpInfo,
|
|
&disconnectIrp
|
|
);
|
|
|
|
//
|
|
// If we have read all of the file data we were requested to read,
|
|
// there is no need to submit another read IRP. Check for this
|
|
// condition before releasing the lock in order to synchronize with
|
|
// send completion.
|
|
//
|
|
|
|
if ( transmitInfo->BytesRead == transmitInfo->FileWriteLength ) {
|
|
|
|
readMore = FALSE;
|
|
|
|
//
|
|
// Note that there is no longer a read pending for this transmit
|
|
// IRP.
|
|
//
|
|
|
|
SET_READ_PENDING( transmitInfo, FALSE );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Leave the ReadPending flag set so that completion does not
|
|
// occur between our releasing the spin lock and attempting to
|
|
// queue another read.
|
|
//
|
|
|
|
readMore = TRUE;
|
|
}
|
|
|
|
//
|
|
// Remember that this send IRP is in use and release the lock.
|
|
//
|
|
|
|
SET_IRP_BUSY( irpInfo->Irp );
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
|
|
//
|
|
// Remember the TDI device object in a local variable. This is
|
|
// necessary because as soon as we call IoCallDriver for the send,
|
|
// the TransmitInfo structure may be freed up and have been reset.
|
|
//
|
|
|
|
tdiDeviceObject = transmitInfo->TdiDeviceObject;
|
|
|
|
//
|
|
// Finish setting up the send IRP and hand it off to the TDI
|
|
// provider.
|
|
//
|
|
|
|
AfdRebuildSend( irpInfo->Irp, transmitInfo->TdiFileObject, sendLength );
|
|
|
|
IoCallDriver( tdiDeviceObject, irpInfo->Irp );
|
|
|
|
//
|
|
// If appropriate, submit a graceful disconnect on the endpoint.
|
|
//
|
|
|
|
if ( disconnectIrp != NULL ) {
|
|
IoCallDriver( tdiDeviceObject, disconnectIrp );
|
|
}
|
|
|
|
//
|
|
// If there's more file data to read, queue work to a thread so that
|
|
// we can perform another read on the file. It is illegal to do a
|
|
// cache read in a completion routine because completion routines
|
|
// may be called at raised IRQL.
|
|
//
|
|
|
|
if ( readMore ) {
|
|
AfdQueueTransmitRead( transmitInfo );
|
|
}
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // AfdRestartTransmitRead
|
|
|
|
|
|
NTSTATUS
|
|
AfdRestartTransmitSend (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the completion routine for TDI send IRPs used for transmit
|
|
file requests. This routine updates information based on the
|
|
results of the send and checks whether there is more file data
|
|
available to be sent. If there is more data, it is placed into the
|
|
send IRP and the IRP is resubmitted to the TDI provider. If no data
|
|
is available, the send IRP is simply held until file data becomes
|
|
available.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - ignored.
|
|
|
|
Irp - the send IRP that is completing.
|
|
|
|
Context - a pointer to the TransmitInfo structure that describes
|
|
the transmit file request corresponding to the IRP that is
|
|
completing.
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED which indicates to the I/O system
|
|
that it should stop completion processing of this IRP. We handle
|
|
all completion from this point forward.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAFD_TRANSMIT_FILE_INFO_INTERNAL transmitInfo;
|
|
PAFD_TRANSMIT_IRP_INFO irpInfo;
|
|
KIRQL oldIrql;
|
|
KIRQL cancelIrql;
|
|
PAFD_ENDPOINT endpoint;
|
|
PAFD_CONNECTION connection;
|
|
ULONG sendLength;
|
|
BOOLEAN readMore;
|
|
BOOLEAN sendAgain;
|
|
PIRP disconnectIrp;
|
|
PDEVICE_OBJECT tdiDeviceObject;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Initialize local variables.
|
|
//
|
|
|
|
transmitInfo = Context;
|
|
|
|
endpoint = transmitInfo->Endpoint;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
ASSERT( endpoint->TransmitInfo == transmitInfo );
|
|
|
|
connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint );
|
|
ASSERT( connection->Type == AfdBlockTypeConnection );
|
|
|
|
ASSERT( IS_IRP_BUSY( Irp ) );
|
|
ASSERT( transmitInfo->Send1.Irp == Irp || transmitInfo->Send2.Irp == Irp );
|
|
ASSERT( !transmitInfo->NeedToSendHead );
|
|
|
|
//
|
|
// Determine which of the two send IRPs completed. This completion
|
|
// routine is used for both of the TDI send IRPs. We use two send
|
|
// IRPs in order to ensure that the TDI provider always has enough
|
|
// data to send at any given time. This prevents Nagling delays and
|
|
// sub-MTU packets.
|
|
//
|
|
|
|
if ( transmitInfo->Send1.Irp == Irp ) {
|
|
irpInfo = &transmitInfo->Send1;
|
|
} else {
|
|
irpInfo = &transmitInfo->Send2;
|
|
}
|
|
|
|
//
|
|
// If we didn't send any file data, there is no need to return file
|
|
// MDLs or AFD buffers. The FileWriteLength will be nonzero if we
|
|
// sent file data.
|
|
//
|
|
|
|
if ( transmitInfo->FileWriteLength != 0 ) {
|
|
|
|
//
|
|
// Free the AFD buffer if we allocated one for this request, or
|
|
// else release the MDLs to the file system.
|
|
//
|
|
|
|
if ( irpInfo->AfdBuffer != NULL ) {
|
|
|
|
//
|
|
// If this send IRP was used to send the tail buffer, reset
|
|
// the Next pointer of the last file MDL to NULL.
|
|
//
|
|
|
|
if ( transmitInfo->IrpUsedToSendTail == Irp ) {
|
|
transmitInfo->LastFileMdlBeforeTail->Next = NULL;
|
|
}
|
|
|
|
//
|
|
// Free the AFD buffer used for the request.
|
|
//
|
|
|
|
irpInfo->AfdBuffer->Mdl->ByteCount = irpInfo->AfdBuffer->BufferLength;
|
|
AfdReturnBuffer( irpInfo->AfdBuffer );
|
|
irpInfo->AfdBuffer = NULL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We need to be careful to return only file system MDLs to
|
|
// the file system. We cannot give head or tail buffer MDLs
|
|
// to the file system. If the head buffer MDL was at the
|
|
// front of this IRP's MDL chain, use the first actual file
|
|
// MDL.
|
|
//
|
|
|
|
if ( Irp->MdlAddress == transmitInfo->HeadMdl ) {
|
|
Irp->MdlAddress = transmitInfo->FirstFileMdlAfterHead;
|
|
}
|
|
|
|
//
|
|
// If this send IRP was used to send the tail buffer, reset
|
|
// the Next pointer of the last file MDL to NULL.
|
|
//
|
|
|
|
if ( transmitInfo->IrpUsedToSendTail == Irp ) {
|
|
transmitInfo->LastFileMdlBeforeTail->Next = NULL;
|
|
}
|
|
|
|
//
|
|
// Now we can return the file data MDLs to the file system.
|
|
//
|
|
|
|
status = AfdMdlReadComplete(
|
|
transmitInfo->FileObject,
|
|
Irp->MdlAddress,
|
|
transmitInfo->Offset + transmitInfo->BytesRead,
|
|
irpInfo->Length
|
|
);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the send failed or we're in the process of completing the
|
|
// transmit IRP, start/continue the process of completing the
|
|
// transmit IRP.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(Irp->IoStatus.Status) || transmitInfo->CompletionPending ) {
|
|
|
|
KIRQL cancelIrql;
|
|
|
|
IoAcquireCancelSpinLock( &cancelIrql );
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
|
SET_IRP_FREE( Irp );
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
|
|
KdPrint(( "AfdRestartTransmitSend: failed, status == %lx\n",
|
|
Irp->IoStatus.Status ));
|
|
|
|
AfdCompleteTransmit( transmitInfo, Irp->IoStatus.Status, cancelIrql );
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
//
|
|
// Update the count of file data bytes we've sent so far.
|
|
//
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
|
|
|
transmitInfo->BytesSent =
|
|
transmitInfo->BytesSent + Irp->IoStatus.Information;
|
|
|
|
ASSERT( transmitInfo->BytesSent <= transmitInfo->TotalBytesToSend );
|
|
|
|
//
|
|
// If we have sent all of the file data, then we've done all the
|
|
// necessary work for the transmit file IRP. Complete the IRP.
|
|
//
|
|
|
|
if ( transmitInfo->BytesSent == transmitInfo->TotalBytesToSend ) {
|
|
|
|
ASSERT( transmitInfo->BytesSent ==
|
|
transmitInfo->BytesRead + transmitInfo->HeadLength +
|
|
transmitInfo->TailLength );
|
|
|
|
//
|
|
// Release the endpoint lock, then reacquire both the cancel
|
|
// lock and the endpoint lock in the correct lock acquisition
|
|
// order. We must hold the cancel spin lock when calling
|
|
// AfdCompleteTransmit() and we must hold the endpoint lock to
|
|
// set the send IRP as free.
|
|
//
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
|
|
IoAcquireCancelSpinLock( &cancelIrql );
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
|
SET_IRP_FREE( Irp );
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
|
|
AfdCompleteTransmit( transmitInfo, STATUS_SUCCESS, cancelIrql );
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
//
|
|
// Check whether there is more file data waiting to be sent. If
|
|
// there is more data we can send it immediately.
|
|
//
|
|
|
|
if ( transmitInfo->FileMdl != NULL ) {
|
|
|
|
//
|
|
// There is data available in the read IRP. Move the buffers
|
|
// to this send IRP.
|
|
//
|
|
|
|
sendLength = AfdBuildTransmitSendMdls(
|
|
transmitInfo,
|
|
transmitInfo->FileMdl,
|
|
transmitInfo->FileMdlLength,
|
|
irpInfo,
|
|
&disconnectIrp
|
|
);
|
|
|
|
transmitInfo->FileMdl = NULL;
|
|
sendAgain = TRUE;
|
|
|
|
//
|
|
// If we have read all of the file data we were requested to
|
|
// read, there is no need to submit another read IRP. Check for
|
|
// this condition before releasing the lock in order to
|
|
// synchronize with send completion. Note that we will not
|
|
// attempt to submit another read if a read is already pending.
|
|
//
|
|
|
|
if ( transmitInfo->BytesRead == transmitInfo->FileWriteLength ||
|
|
transmitInfo->ReadPending ) {
|
|
readMore = FALSE;
|
|
} else {
|
|
readMore = TRUE;
|
|
SET_READ_PENDING( transmitInfo, TRUE );
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// There is no read data available now, so there must already be
|
|
// a read pending or else we've sent all the file data.
|
|
//
|
|
|
|
readMore = FALSE;
|
|
ASSERT( transmitInfo->BytesRead == transmitInfo->FileWriteLength ||
|
|
transmitInfo->ReadPending );
|
|
|
|
//
|
|
// This send IRP is now available for use as soon as a read
|
|
// completes.
|
|
//
|
|
|
|
sendAgain = FALSE;
|
|
SET_IRP_FREE( Irp );
|
|
}
|
|
|
|
//
|
|
// Release the lock. We cannot submit IRPs while holding a lock.
|
|
//
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
|
|
//
|
|
// If there was file data immediately available to be sent, finish
|
|
// setting up the send IRP and hand it off to the TDI provider.
|
|
//
|
|
|
|
if ( sendAgain ) {
|
|
|
|
//
|
|
// Remember the TDI device object in a local variable. This is
|
|
// necessary because as soon as we call IoCallDriver for the
|
|
// send, the TransmitInfo structure may be freed up and have
|
|
// been reset.
|
|
//
|
|
|
|
tdiDeviceObject = transmitInfo->TdiDeviceObject;
|
|
|
|
AfdRebuildSend( irpInfo->Irp, transmitInfo->TdiFileObject, sendLength );
|
|
IoCallDriver( tdiDeviceObject, irpInfo->Irp );
|
|
|
|
//
|
|
// If appropriate, submit a graceful disconnect on the endpoint.
|
|
//
|
|
|
|
if ( disconnectIrp != NULL ) {
|
|
IoCallDriver( tdiDeviceObject, disconnectIrp );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If there is more file data to read, queue work to a thread so
|
|
// that we can perform another read on the file. It is illegal to
|
|
// do a cache read in a completion routine.
|
|
//
|
|
|
|
if ( readMore ) {
|
|
AfdQueueTransmitRead( transmitInfo );
|
|
}
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // AfdRestartTransmitSend
|
|
|
|
|
|
VOID
|
|
AfdCompleteTransmit (
|
|
IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo,
|
|
IN NTSTATUS Status,
|
|
IN KIRQL OldIrql
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is responsible for handling all aspects of completing a
|
|
transmit file request. First it checks to make sure that all
|
|
aspects of the request (read IRPs, send IRPs, queued reads to
|
|
threads, etc.) have completed. If any are still pending, they
|
|
are cancelled and this routine exits until they complete.
|
|
|
|
When everything has finished, this routine cleans up the resources
|
|
allocated for the request and completed the transmit file IRP.
|
|
|
|
Arguments:
|
|
|
|
TransmitInfo - a pointer to the TransmitInfo structure which
|
|
contains information about the request to process.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL endpointOldIrql;
|
|
PAFD_ENDPOINT endpoint;
|
|
PAFD_CONNECTION connection;
|
|
NTSTATUS status;
|
|
BOOLEAN reuseSocket;
|
|
PMDL fileMdl;
|
|
PIRP transmitIrp;
|
|
|
|
endpoint = TransmitInfo->Endpoint;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
ASSERT( endpoint->TransmitInfo == TransmitInfo );
|
|
|
|
connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint );
|
|
ASSERT( connection != NULL );
|
|
ASSERT( connection->Type == AfdBlockTypeConnection );
|
|
|
|
//
|
|
// Make sure that we hold the cancel spin lock for the followiung
|
|
// tests. Then acquire the endpoint spin lock for proper
|
|
// synchronization.
|
|
//
|
|
|
|
ASSERT( KeGetCurrentIrql( ) == DISPATCH_LEVEL );
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &endpointOldIrql );
|
|
|
|
transmitIrp = TransmitInfo->TransmitIrp;
|
|
|
|
ASSERT( endpoint->TransmitIrp == NULL || endpoint->TransmitIrp == transmitIrp );
|
|
|
|
//
|
|
// If this transmit IRP is on the TransmitFile queue, remove it.
|
|
//
|
|
|
|
if( TransmitInfo->Queued ) {
|
|
|
|
RemoveEntryList(
|
|
&transmitIrp->Tail.Overlay.ListEntry
|
|
);
|
|
|
|
TransmitInfo->Queued = FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// Remember the completion status in the transmit IRP. Note that we
|
|
// use the first failure status code as the completion status code.
|
|
// Subsequent failures (for example STATUS_CANCELLED) are ignored.
|
|
//
|
|
|
|
if ( NT_SUCCESS(transmitIrp->IoStatus.Status) ) {
|
|
transmitIrp->IoStatus.Status = Status;
|
|
}
|
|
|
|
//
|
|
// Note that we only attempt to do the disconnect and socket reuse
|
|
// if the transmit request was successful. If it fails for any
|
|
// reason, we do not begin disconnecting the connection.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(transmitIrp->IoStatus.Status) ||
|
|
(TransmitInfo->Flags & AFD_TF_REUSE_SOCKET) == 0 ) {
|
|
|
|
endpoint->TransmitIrp = NULL;
|
|
reuseSocket = FALSE;
|
|
|
|
//
|
|
// We're doing our best to complete the transmit IRP, so remove
|
|
// the cancel routine from the transmit IRP. Also remove the
|
|
// transmit IRP pointer from the endpoint structure in order to
|
|
// synchronize with cleanup IRPs.
|
|
//
|
|
|
|
IoSetCancelRoutine( transmitIrp, NULL );
|
|
|
|
} else {
|
|
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
reuseSocket = TRUE;
|
|
|
|
//
|
|
// Reset the control code in our stack location in the IRP
|
|
// so that the cancel routine knows about the special state
|
|
// of this IRP and cancels it appropriately.
|
|
//
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( transmitIrp );
|
|
irpSp->Parameters.DeviceIoControl.IoControlCode = 0;
|
|
}
|
|
|
|
//
|
|
// Remember that we are in the process of completing this IRP.
|
|
// Other steps in transmit IRP processing use this flag to know
|
|
// whether to bail out immediately instead of continuing to attempt
|
|
// transmit processing.
|
|
//
|
|
|
|
TransmitInfo->CompletionPending = TRUE;
|
|
|
|
//
|
|
// Determine whether any of the child IRPs are still outstanding.
|
|
// If any are, cancel them. Note that if any of these IRPs was not
|
|
// in a cancellable state (IoCancelIrp() returns FALSE) there's
|
|
// nothing we can do, so just plow on and wait for the IRPs to
|
|
// complete.
|
|
//
|
|
// As soon as we hit an active IRP quit processing. We must quit
|
|
// because the cancel spin lock gets released by IoCancelIrp() and
|
|
// the IRP of interest could get completed immediately and reenter
|
|
// AfdCompleteTransmit(), which could complete the transmit IRP
|
|
// before we continue executing.
|
|
//
|
|
|
|
if ( TransmitInfo->Read.Irp != NULL && IS_IRP_BUSY(TransmitInfo->Read.Irp) ) {
|
|
|
|
TransmitInfo->Read.Irp->CancelIrql = OldIrql;
|
|
UPDATE_TRANSMIT_DEBUG_INFO( TransmitInfo, 1, 0 );
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, endpointOldIrql );
|
|
|
|
AfdCancelIrp( TransmitInfo->Read.Irp );
|
|
|
|
return;
|
|
}
|
|
|
|
if ( IS_IRP_BUSY(TransmitInfo->Send1.Irp) ) {
|
|
|
|
TransmitInfo->Send1.Irp->CancelIrql = OldIrql;
|
|
UPDATE_TRANSMIT_DEBUG_INFO( TransmitInfo, 2, 0 );
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, endpointOldIrql );
|
|
|
|
AfdCancelIrp( TransmitInfo->Send1.Irp );
|
|
|
|
return;
|
|
}
|
|
|
|
if ( IS_IRP_BUSY(TransmitInfo->Send2.Irp) ) {
|
|
|
|
TransmitInfo->Send2.Irp->CancelIrql = OldIrql;
|
|
UPDATE_TRANSMIT_DEBUG_INFO( TransmitInfo, 3, 0 );
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, endpointOldIrql );
|
|
|
|
AfdCancelIrp( TransmitInfo->Send2.Irp );
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If there is a read about to happen, bail. When the read
|
|
// completes the appropriate routine will check the
|
|
// CompletionPending flag and call this routine again.
|
|
//
|
|
|
|
if ( TransmitInfo->ReadPending ) {
|
|
|
|
UPDATE_TRANSMIT_DEBUG_INFO( TransmitInfo, 4, 0 );
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, endpointOldIrql );
|
|
IoReleaseCancelSpinLock( OldIrql );
|
|
|
|
return;
|
|
}
|
|
|
|
#if DBG
|
|
TransmitInfo->Completed = TRUE;
|
|
#endif
|
|
|
|
//
|
|
// Everything is on track to complete the transmit IRP, so we're
|
|
// full steam ahead. Free the resources allocated to service this
|
|
// request.
|
|
//
|
|
|
|
if ( TransmitInfo->Read.Irp != NULL ) {
|
|
IoFreeIrp( TransmitInfo->Read.Irp );
|
|
}
|
|
|
|
IoFreeIrp( TransmitInfo->Send1.Irp );
|
|
IoFreeIrp( TransmitInfo->Send2.Irp );
|
|
|
|
//
|
|
// If there was an AFD buffer with read data, free it. If there was
|
|
// no AFD buffer but FileMdl is non-NULL, then we have some file
|
|
// system MDLs which we need to free.
|
|
//
|
|
|
|
if ( TransmitInfo->Read.AfdBuffer != NULL ) {
|
|
|
|
TransmitInfo->Read.AfdBuffer->Mdl->ByteCount =
|
|
TransmitInfo->Read.AfdBuffer->BufferLength;
|
|
AfdReturnBuffer( TransmitInfo->Read.AfdBuffer );
|
|
|
|
TransmitInfo->Read.AfdBuffer = NULL;
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, endpointOldIrql );
|
|
IoReleaseCancelSpinLock( OldIrql );
|
|
|
|
} else if ( TransmitInfo->FileMdl != NULL ) {
|
|
|
|
fileMdl = TransmitInfo->FileMdl;
|
|
TransmitInfo->FileMdl = NULL;
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, endpointOldIrql );
|
|
IoReleaseCancelSpinLock( OldIrql );
|
|
|
|
status = AfdMdlReadComplete(
|
|
TransmitInfo->FileObject,
|
|
fileMdl,
|
|
TransmitInfo->Offset + TransmitInfo->BytesRead,
|
|
TransmitInfo->Read.Length
|
|
);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
|
|
} else {
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, endpointOldIrql );
|
|
IoReleaseCancelSpinLock( OldIrql );
|
|
}
|
|
|
|
if ( TransmitInfo->Send1.AfdBuffer != NULL ) {
|
|
TransmitInfo->Send1.AfdBuffer->Mdl->ByteCount =
|
|
TransmitInfo->Send1.AfdBuffer->BufferLength;
|
|
AfdReturnBuffer( TransmitInfo->Send1.AfdBuffer );
|
|
}
|
|
|
|
if ( TransmitInfo->Send2.AfdBuffer != NULL ) {
|
|
TransmitInfo->Send2.AfdBuffer->Mdl->ByteCount =
|
|
TransmitInfo->Send2.AfdBuffer->BufferLength;
|
|
AfdReturnBuffer( TransmitInfo->Send2.AfdBuffer );
|
|
}
|
|
|
|
if ( TransmitInfo->HeadMdl != NULL ) {
|
|
MmUnlockPages( TransmitInfo->HeadMdl );
|
|
IoFreeMdl( TransmitInfo->HeadMdl );
|
|
}
|
|
|
|
if ( TransmitInfo->TailMdl != NULL ) {
|
|
MmUnlockPages( TransmitInfo->TailMdl );
|
|
IoFreeMdl( TransmitInfo->TailMdl );
|
|
}
|
|
|
|
#if DBG
|
|
if ( TransmitInfo->Debug1 != NULL ) {
|
|
AFD_FREE_POOL(
|
|
TransmitInfo->Debug1,
|
|
AFD_TRANSMIT_DEBUG_POOL_TAG
|
|
);
|
|
TransmitInfo->Debug1 = NULL;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Update the file position and clean up the reference count.
|
|
//
|
|
|
|
TransmitInfo->FileObject->CurrentByteOffset.QuadPart += TransmitInfo->BytesRead;
|
|
|
|
ObDereferenceObject( TransmitInfo->FileObject );
|
|
AfdRecordFileDeref();
|
|
|
|
//
|
|
// Remember how many bytes we sent with this request.
|
|
//
|
|
|
|
transmitIrp->IoStatus.Information = (ULONG)TransmitInfo->BytesSent;
|
|
|
|
//
|
|
// Begin socket reuse if so requested. This causes the transmit
|
|
// not to complete until the connection is fully disconnected
|
|
// and the endpoint is ready for reuse, i.e. it is in the same
|
|
// state as if it had just been opened.
|
|
//
|
|
|
|
if ( reuseSocket ) {
|
|
|
|
//
|
|
// Remember that there is a transmit IRP pended on the endpoint,
|
|
// so that when we're freeing up the connection we also complete
|
|
// the transmit IRP.
|
|
//
|
|
|
|
connection->ClosePendedTransmit = TRUE;
|
|
|
|
//
|
|
// Since we are going to effectively close this connection,
|
|
// remember that we have started cleanup on this connection.
|
|
// This allows AfdDeleteConnectedReference to remove the
|
|
// connected reference when appropriate.
|
|
//
|
|
|
|
connection->CleanupBegun = TRUE;
|
|
|
|
//
|
|
// Attempt to remove the connected reference.
|
|
//
|
|
|
|
AfdDeleteConnectedReference( connection, FALSE );
|
|
|
|
//
|
|
// Delete the endpoint's reference to the connection in
|
|
// preparation for reusing this endpoint.
|
|
//
|
|
|
|
endpoint->Common.VcConnecting.Connection = NULL;
|
|
DEREFERENCE_CONNECTION( connection );
|
|
|
|
//
|
|
// DO NOT access the IRP after this point, since it may have
|
|
// been completed inside AfdDereferenceConnection!
|
|
//
|
|
}
|
|
|
|
//
|
|
// Complete the actual transmit file IRP, unless we need to pend it
|
|
// until the connection is fully disconnected as requested by the
|
|
// AFD_TF_REUSE_SOCKET flag.
|
|
//
|
|
// If we are going to wait on completion until the connection object
|
|
// is disconnected, note that we do not have a cancellation routine
|
|
// in the transmit IRP any more. This generally shouldn't cause a
|
|
// problem because the time for a cancellation is bounded.
|
|
//
|
|
|
|
if ( !reuseSocket ) {
|
|
|
|
IoCompleteRequest( transmitIrp, AfdPriorityBoost );
|
|
|
|
//
|
|
// If we're enforcing a maximum active TransmitFile count, then
|
|
// check the list of queued TransmitFile requests and start the
|
|
// next one.
|
|
//
|
|
|
|
if( AfdMaxActiveTransmitFileCount > 0 ) {
|
|
|
|
AfdStartNextQueuedTransmitFile();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Don't free the transmit info structure here, leave
|
|
// it attached to the endpoint. The structure will get
|
|
// freed when we free the endpoint.
|
|
//
|
|
|
|
} // AfdCompleteTransmit
|
|
|
|
|
|
NTSTATUS
|
|
AfdBuildReadIrp (
|
|
IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine builds a read file data IRP for a transmit file
|
|
request. It determines how much data needs to be read and builds an
|
|
IRP appropriate to the functionality supported by the file system.
|
|
|
|
Arguments:
|
|
|
|
TransmitInfo - a pointer to the TransmitInfo structure which
|
|
contains information about the request to process.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - indicates whether the IRP was built successfully.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG readLength;
|
|
PIO_STACK_LOCATION readIrpSp;
|
|
|
|
ASSERT( TransmitInfo->Endpoint->TransmitInfo == TransmitInfo );
|
|
|
|
//
|
|
// If we haven't already done so, allocate and initialize the IRP
|
|
// that we'll use to read file data.
|
|
//
|
|
|
|
if ( TransmitInfo->Read.Irp == NULL ) {
|
|
|
|
TransmitInfo->Read.Irp =
|
|
IoAllocateIrp( TransmitInfo->DeviceObject->StackSize, TRUE );
|
|
if ( TransmitInfo->Read.Irp == NULL ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
readIrpSp = IoGetNextIrpStackLocation( TransmitInfo->Read.Irp );
|
|
readIrpSp->MajorFunction = IRP_MJ_READ;
|
|
}
|
|
|
|
//
|
|
// We have to rebuild parts of the stack location because
|
|
// the IO system zeros them out on every I/O completion.
|
|
//
|
|
|
|
readIrpSp = IoGetNextIrpStackLocation( TransmitInfo->Read.Irp );
|
|
readIrpSp->FileObject = TransmitInfo->FileObject;
|
|
|
|
IoSetCompletionRoutine(
|
|
TransmitInfo->Read.Irp,
|
|
AfdRestartTransmitRead,
|
|
TransmitInfo,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
ASSERT( readIrpSp->MajorFunction == IRP_MJ_READ );
|
|
ASSERT( readIrpSp->Parameters.Read.Key == 0 );
|
|
ASSERT( !IS_IRP_BUSY(TransmitInfo->Read.Irp) );
|
|
|
|
//
|
|
// Determine how many bytes we will attempt to read. This is
|
|
// either the send packet size or the remaining bytes in the file.
|
|
//
|
|
|
|
if ( TransmitInfo->FileWriteLength - TransmitInfo->BytesRead >
|
|
TransmitInfo->SendPacketLength ) {
|
|
readLength = TransmitInfo->SendPacketLength;
|
|
} else {
|
|
readLength =
|
|
(ULONG)(TransmitInfo->FileWriteLength - TransmitInfo->BytesRead);
|
|
}
|
|
|
|
TransmitInfo->Read.Length = readLength;
|
|
|
|
readIrpSp = IoGetNextIrpStackLocation(TransmitInfo->Read.Irp );
|
|
|
|
//
|
|
// If supported by the file system, try to perform MDL I/O to get
|
|
// MDLs that we can pass to the TDI provider. This is by far the
|
|
// fastest way to get the file data because it avoids all file
|
|
// copies.
|
|
//
|
|
|
|
if ( (TransmitInfo->FileObject->Flags & FO_CACHE_SUPPORTED) != 0 ) {
|
|
|
|
readIrpSp->MinorFunction = IRP_MN_MDL;
|
|
TransmitInfo->Read.Irp->MdlAddress = NULL;
|
|
|
|
//
|
|
// Set the synchronous flag in the IRP to tell the file system
|
|
// that we are aware of the fact that this IRP will be completed
|
|
// synchronously. This means that we must supply our own thread
|
|
// for the operation and that the disk read will occur
|
|
// synchronously in this thread if the data is not cached.
|
|
//
|
|
|
|
TransmitInfo->Read.Irp->Flags |= IRP_SYNCHRONOUS_API;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The file system does not support the special cache manager
|
|
// routines. Allocate an AFD buffer for the request.
|
|
//
|
|
|
|
TransmitInfo->Read.AfdBuffer = AfdGetBuffer( readLength, 0 );
|
|
if ( TransmitInfo->Read.AfdBuffer == NULL ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
readIrpSp->MinorFunction = IRP_MN_NORMAL;
|
|
|
|
TransmitInfo->Read.Irp->MdlAddress = TransmitInfo->Read.AfdBuffer->Mdl;
|
|
TransmitInfo->Read.Irp->AssociatedIrp.SystemBuffer =
|
|
TransmitInfo->Read.AfdBuffer->Buffer;
|
|
TransmitInfo->Read.Irp->UserBuffer =
|
|
TransmitInfo->Read.AfdBuffer->Buffer;
|
|
}
|
|
|
|
//
|
|
// Finish building the read IRP.
|
|
//
|
|
|
|
readIrpSp->Parameters.Read.Length = readLength;
|
|
readIrpSp->Parameters.Read.ByteOffset.QuadPart =
|
|
TransmitInfo->Offset + TransmitInfo->BytesRead;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // AfdBuildReadIrp
|
|
|
|
|
|
ULONG
|
|
AfdBuildTransmitSendMdls (
|
|
IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo,
|
|
IN PMDL FileMdlChain,
|
|
IN ULONG FileDataLength,
|
|
IN PAFD_TRANSMIT_IRP_INFO IrpInfo,
|
|
OUT PIRP *DisconnectIrp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets up the MDL chain in a send IRP. Typically this
|
|
just means moving file data MDLs to the send IRP, but it may also
|
|
involve chaining a head buffer MDL at the beginning of the chain
|
|
and/or putting a tail buffer MDL at the end of the chain.
|
|
|
|
Arguments:
|
|
|
|
TransmitInfo - a pointer to the TransmitInfo structure which
|
|
contains information about the request to process.
|
|
|
|
FileMdlChain - a pointer to the first MDL in a chain of file data
|
|
MDLs.
|
|
|
|
FileDataLength - the number of bytes in the file data MDL chain.
|
|
|
|
IrpInfo - a pointer to the IrpInfo structure which contains the
|
|
send IRP of interest.
|
|
|
|
DisconnectIrp - set on output to indicate that this is the final
|
|
send of the TransmitFile request, and that the caller should
|
|
submit the disconnect IRP after submitting the send IRP.
|
|
|
|
Return Value:
|
|
|
|
The total number of bytes represented by the MDLs in the send IRP.
|
|
|
|
Notes:
|
|
|
|
In order for this routine to synchronize properly its access to the
|
|
TransmitInfo structure is MUST be called with the endpoint spin lock
|
|
held.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG sendLength;
|
|
PMDL lastMdl;
|
|
|
|
ASSERT( TransmitInfo->Endpoint->TransmitInfo == TransmitInfo );
|
|
|
|
//
|
|
// This routine MUST be called with the endpoint spin lock held.
|
|
//
|
|
|
|
ASSERT( KeGetCurrentIrql( ) >= DISPATCH_LEVEL );
|
|
|
|
//
|
|
// If we need to send the head buffer, chain file data MDLs after
|
|
// the head buffer MDLs.
|
|
//
|
|
|
|
if ( TransmitInfo->NeedToSendHead ) {
|
|
|
|
ASSERT( TransmitInfo->HeadMdl != NULL );
|
|
|
|
//
|
|
// Now that we're about to send the head buffer, there is no
|
|
// need to send it again.
|
|
//
|
|
|
|
TransmitInfo->NeedToSendHead = FALSE;
|
|
|
|
//
|
|
// Find the last MDL in the chain of head buffer MDLs.
|
|
//
|
|
|
|
for ( lastMdl = TransmitInfo->HeadMdl;
|
|
lastMdl->Next != NULL;
|
|
lastMdl = lastMdl->Next );
|
|
|
|
//
|
|
// Chain the file MDLs after the last head MDL.
|
|
//
|
|
|
|
lastMdl->Next = FileMdlChain;
|
|
|
|
//
|
|
// Put the head buffer MDL into the send IRP.
|
|
//
|
|
|
|
IrpInfo->Irp->MdlAddress = TransmitInfo->HeadMdl;
|
|
IrpInfo->AfdBuffer = TransmitInfo->Read.AfdBuffer;
|
|
IrpInfo->Length = TransmitInfo->Read.Length;
|
|
TransmitInfo->Read.AfdBuffer = NULL;
|
|
|
|
//
|
|
// Remember a pointer to the first actual file MDL so that we
|
|
// can quickly jump to it when the send completes and we need
|
|
// to return the file MDLs to the file system.
|
|
//
|
|
|
|
TransmitInfo->FirstFileMdlAfterHead = FileMdlChain;
|
|
|
|
//
|
|
// Calculate the number of bytes in the MDLs so far.
|
|
//
|
|
|
|
sendLength = FileDataLength + TransmitInfo->HeadLength;
|
|
|
|
} else {
|
|
|
|
IrpInfo->Irp->MdlAddress = FileMdlChain;
|
|
IrpInfo->AfdBuffer = TransmitInfo->Read.AfdBuffer;
|
|
IrpInfo->Length = TransmitInfo->Read.Length;
|
|
TransmitInfo->Read.AfdBuffer = NULL;
|
|
|
|
sendLength = FileDataLength;
|
|
}
|
|
|
|
//
|
|
// If we have read all the file data, put any tail buffer MDLs at
|
|
// the end of the MDL chain.
|
|
//
|
|
|
|
if ( TransmitInfo->BytesRead == TransmitInfo->FileWriteLength ) {
|
|
|
|
//
|
|
// If the caller needs to do a disconnect after the send, get a
|
|
// disconnect IRP.
|
|
//
|
|
|
|
if ( (TransmitInfo->Flags & AFD_TF_DISCONNECT) != 0 ) {
|
|
(VOID)AfdBeginDisconnect(
|
|
TransmitInfo->Endpoint,
|
|
NULL,
|
|
DisconnectIrp
|
|
);
|
|
} else {
|
|
*DisconnectIrp = NULL;
|
|
}
|
|
|
|
if ( TransmitInfo->TailMdl != NULL ) {
|
|
|
|
//
|
|
// Find the last MDL in the chain. If the FileMdlChain is
|
|
// NULL, then we're not sending any file data, so use the
|
|
// head MDL chain (if present) or just tag it on to the IRP
|
|
// if all we're sending is the tail.
|
|
//
|
|
|
|
if ( FileMdlChain != NULL ) {
|
|
|
|
for ( lastMdl = FileMdlChain;
|
|
lastMdl->Next != NULL;
|
|
lastMdl = lastMdl->Next );
|
|
|
|
//
|
|
// Chain the tail MDLs after the last file MDL.
|
|
//
|
|
|
|
lastMdl->Next = TransmitInfo->TailMdl;
|
|
|
|
//
|
|
// Remember a pointer to the last file MDL before the
|
|
// tail MDLs so that we can quickly jump to it when the
|
|
// send completes and we need to return the file MDLs to
|
|
// the system. Note that we must reset the Next pointer
|
|
// on this MDL to NULL before returning it to the file
|
|
// system.
|
|
//
|
|
|
|
TransmitInfo->LastFileMdlBeforeTail = lastMdl;
|
|
|
|
} else if ( IrpInfo->Irp->MdlAddress !=NULL ) {
|
|
|
|
for ( lastMdl = IrpInfo->Irp->MdlAddress;
|
|
lastMdl->Next != NULL;
|
|
lastMdl = lastMdl->Next );
|
|
|
|
//
|
|
// Chain the tail MDLs after the last head buffer MDL.
|
|
//
|
|
|
|
lastMdl->Next = TransmitInfo->TailMdl;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We're only sending a tail buffer here.
|
|
//
|
|
|
|
IrpInfo->Irp->MdlAddress = TransmitInfo->TailMdl;
|
|
}
|
|
|
|
//
|
|
// Remember the IRP we used to send the tail so that the
|
|
// send completion routine knows whether it needs to read
|
|
// just the file MDLs.
|
|
//
|
|
|
|
TransmitInfo->IrpUsedToSendTail = IrpInfo->Irp;
|
|
|
|
//
|
|
// Calculate the number of bytes in the MDLs so far.
|
|
//
|
|
|
|
sendLength += TransmitInfo->TailLength;
|
|
}
|
|
|
|
} else {
|
|
|
|
*DisconnectIrp = NULL;
|
|
}
|
|
|
|
//
|
|
// Return the total number of bytes in the MDLs we chained off the
|
|
// send IRP.
|
|
//
|
|
|
|
return sendLength;
|
|
|
|
} // AfdBuildTransmitSendMdls
|
|
|
|
|
|
VOID
|
|
AfdCancelTransmit (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The cancel routine for transmit requests. This routine simply
|
|
calls AfdCompleteTransmit which performs the actual work of
|
|
killing the transmit request.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - ignored.
|
|
|
|
Irp - a pointer to the transmit file IRP to cancel.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldIrql;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PAFD_ENDPOINT endpoint;
|
|
|
|
//
|
|
// Initialize some locals and grab the endpoint spin lock.
|
|
//
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
endpoint = irpSp->FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
|
|
|
//
|
|
// Test whether the transmit request is normally cancellable or if
|
|
// it is in the process of closing the connection, in which case we
|
|
// will complete it directly.
|
|
//
|
|
|
|
if ( irpSp->Parameters.DeviceIoControl.IoControlCode != 0 ) {
|
|
|
|
ASSERT( irpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_AFD_TRANSMIT_FILE );
|
|
|
|
ASSERT( endpoint->TransmitInfo ==
|
|
(PAFD_TRANSMIT_FILE_INFO_INTERNAL)Irp->AssociatedIrp.SystemBuffer );
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
|
|
//
|
|
// The transmit request is in the normal cancellation state.
|
|
// Just call the completion routine to handle the cancellation.
|
|
//
|
|
|
|
AfdCompleteTransmit(
|
|
(PAFD_TRANSMIT_FILE_INFO_INTERNAL)(Irp->AssociatedIrp.SystemBuffer),
|
|
STATUS_CANCELLED,
|
|
Irp->CancelIrql
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// The transmit request is in the process of closing the
|
|
// connection. Just complete the transmit IRP and let the
|
|
// connection closure proceed as normal.
|
|
//
|
|
|
|
endpoint->TransmitIrp = NULL;
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoSetCancelRoutine( Irp, NULL );
|
|
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|
|
|
IoCompleteRequest( Irp, AfdPriorityBoost );
|
|
}
|
|
|
|
return;
|
|
|
|
} // AfdCancelTransmit
|
|
|
|
|
|
BOOLEAN
|
|
AfdCancelIrp (
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to cancel an individual I/O Request Packet.
|
|
It is similiar to IoCancelIrp() except that it *must* be called with
|
|
the cancel spin lock held. This routine exists because of the
|
|
synchronization requirements of the cancellation/completion of
|
|
transmit IRPs.
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies a pointer to the IRP to be cancelled. The CancelIrql
|
|
field of the IRP must have been correctly initialized with the
|
|
IRQL from the cancel spin lock acquisition.
|
|
|
|
|
|
Return Value:
|
|
|
|
The function value is TRUE if the IRP was in a cancellable state (it
|
|
had a cancel routine), else FALSE is returned.
|
|
|
|
Notes:
|
|
|
|
It is assumed that the caller has taken the necessary action to ensure
|
|
that the packet cannot be fully completed before invoking this routine.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDRIVER_CANCEL cancelRoutine;
|
|
|
|
//
|
|
// Make sure that the cancel spin lock is held.
|
|
//
|
|
|
|
ASSERT( KeGetCurrentIrql( ) == DISPATCH_LEVEL );
|
|
|
|
//
|
|
// Set the cancel flag in the IRP.
|
|
//
|
|
|
|
Irp->Cancel = TRUE;
|
|
|
|
//
|
|
// Obtain the address of the cancel routine, and if one was specified,
|
|
// invoke it.
|
|
//
|
|
|
|
cancelRoutine = Irp->CancelRoutine;
|
|
if (cancelRoutine) {
|
|
if (Irp->CurrentLocation > (CCHAR) (Irp->StackCount + 1)) {
|
|
KeBugCheckEx( CANCEL_STATE_IN_COMPLETED_IRP, (ULONG) Irp, 0, 0, 0 );
|
|
}
|
|
Irp->CancelRoutine = (PDRIVER_CANCEL) NULL;
|
|
cancelRoutine( Irp->Tail.Overlay.CurrentStackLocation->DeviceObject,
|
|
Irp );
|
|
//
|
|
// The cancel spinlock should have been released by the cancel routine.
|
|
//
|
|
|
|
return(TRUE);
|
|
|
|
} else {
|
|
|
|
//
|
|
// There was no cancel routine, so release the cancel spinlock and
|
|
// return indicating the Irp was not currently cancelable.
|
|
//
|
|
|
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
} // AfdCancelIrp
|
|
|
|
|
|
VOID
|
|
AfdQueueTransmitRead (
|
|
IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Because FsRtl fast reads must be performed in thread context,
|
|
this routine is used to queue reads to a separate thread.
|
|
|
|
Arguments:
|
|
|
|
TransmitInfo - a pointer to the TransmitInfo structure which
|
|
contains information about the request to process.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT( !TransmitInfo->Completed );
|
|
ASSERT( TransmitInfo->ReadPending );
|
|
ASSERT( TransmitInfo->Endpoint->TransmitInfo == TransmitInfo );
|
|
|
|
//
|
|
// Initialize an executive work quete item and hand it off to an
|
|
// executive worker thread.
|
|
//
|
|
|
|
ExInitializeWorkItem(
|
|
&TransmitInfo->WorkQueueItem,
|
|
AfdStartTransmitIo,
|
|
TransmitInfo
|
|
);
|
|
ExQueueWorkItem( &TransmitInfo->WorkQueueItem, DelayedWorkQueue );
|
|
|
|
//
|
|
// All done.
|
|
//
|
|
|
|
return;
|
|
|
|
} // AfdQueueTransmitRead
|
|
|
|
|
|
VOID
|
|
AfdCompleteClosePendedTransmit (
|
|
IN PAFD_ENDPOINT Endpoint
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completes a transmit IRP that was waiting for the connection to be
|
|
completely disconnected.
|
|
|
|
Arguments:
|
|
|
|
Endpoint - the endpoint on which the transmit request is pending.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
KIRQL cancelIrql;
|
|
KIRQL oldIrql;
|
|
PIRP transmitIrp;
|
|
|
|
IoAcquireCancelSpinLock( &cancelIrql );
|
|
AfdAcquireSpinLock( &Endpoint->SpinLock, &oldIrql );
|
|
|
|
//
|
|
// First make sure that thre is really a transmit request pended on
|
|
// this endpoint. We do this while holding the appropriate locks
|
|
// to close the timing window that would exist otherwise, since
|
|
// the caller may not have had the locks when making the test.
|
|
//
|
|
|
|
if ( Endpoint->TransmitIrp == NULL ) {
|
|
AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql );
|
|
IoReleaseCancelSpinLock( cancelIrql );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Grab the IRP from the endpoint and reset the endpoint's pointer
|
|
// to it while still holding the locks so that nobody else accesses
|
|
// the IRP.
|
|
//
|
|
|
|
transmitIrp = Endpoint->TransmitIrp;
|
|
Endpoint->TransmitIrp = NULL;
|
|
|
|
//
|
|
// Reset the cancel routine in the IRP before attempting to complete
|
|
// it.
|
|
//
|
|
|
|
IoSetCancelRoutine( transmitIrp, NULL );
|
|
|
|
//
|
|
// Release the lock before completing the transmit IRP--it is
|
|
// illegal to call IoCompleteRequest while holding a spin lock.
|
|
//
|
|
|
|
AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql );
|
|
IoReleaseCancelSpinLock( cancelIrql );
|
|
|
|
//
|
|
// Make sure to refresh the endpoint BEFORE completing the transmit
|
|
// IRP. This is because the user-mode caller may reuse the endpoint
|
|
// as soon as the IRP completes, and there would be a timing window
|
|
// between reuse of the endpoint and the refresh otherwise.
|
|
//
|
|
// Also, AfdRefreshEndpoint must be called at low IRQL since it must
|
|
// do a KeAttachProcess to free some resources.
|
|
//
|
|
|
|
AfdRefreshEndpoint( Endpoint );
|
|
|
|
//
|
|
// Finally, we can complete the transmit request.
|
|
//
|
|
|
|
IoCompleteRequest( transmitIrp, AfdPriorityBoost );
|
|
|
|
//
|
|
// If we're enforcing a maximum active TransmitFile count, then
|
|
// check the list of queued TransmitFile requests and start the
|
|
// next one.
|
|
//
|
|
|
|
if( AfdMaxActiveTransmitFileCount > 0 ) {
|
|
|
|
AfdStartNextQueuedTransmitFile();
|
|
|
|
}
|
|
|
|
} // AfdCompleteClosePendedTransmit
|
|
|
|
|
|
VOID
|
|
AfdStartNextQueuedTransmitFile(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
KIRQL oldIrql;
|
|
PLIST_ENTRY listEntry;
|
|
PIRP irp;
|
|
PAFD_TRANSMIT_FILE_INFO_INTERNAL transmitInfo;
|
|
|
|
//
|
|
// This should only be called if we're actually enforcing a maximum
|
|
// TransmitFile count.
|
|
//
|
|
|
|
ASSERT( AfdMaxActiveTransmitFileCount > 0 );
|
|
|
|
//
|
|
// The TransmitFile request queue is protected by the I/O cancel
|
|
// spinlock, so grab that lock before examining the queue.
|
|
//
|
|
|
|
IoAcquireCancelSpinLock( &oldIrql );
|
|
|
|
//
|
|
// This routine is only called after a pended TransmitFile IRP
|
|
// completes, so account for that completion here.
|
|
//
|
|
|
|
ASSERT( AfdActiveTransmitFileCount > 0 );
|
|
AfdActiveTransmitFileCount--;
|
|
|
|
if( !IsListEmpty( &AfdQueuedTransmitFileListHead ) ) {
|
|
|
|
//
|
|
// Dequeue exactly one IRP from the list, then start the
|
|
// TransmitFile.
|
|
//
|
|
|
|
listEntry = RemoveHeadList(
|
|
&AfdQueuedTransmitFileListHead
|
|
);
|
|
|
|
irp = CONTAINING_RECORD(
|
|
listEntry,
|
|
IRP,
|
|
Tail.Overlay.ListEntry
|
|
);
|
|
|
|
transmitInfo = irp->AssociatedIrp.SystemBuffer;
|
|
|
|
ASSERT( transmitInfo != NULL );
|
|
ASSERT( transmitInfo->Endpoint->TransmitInfo == transmitInfo );
|
|
|
|
//
|
|
// Mark this TransmitFile request as no longer queued.
|
|
//
|
|
|
|
ASSERT( transmitInfo->Queued );
|
|
transmitInfo->Queued = FALSE;
|
|
|
|
//
|
|
// Adjust the count, release the I/O cancel spinlock, then queue
|
|
// the TransmitFile.
|
|
//
|
|
|
|
AfdActiveTransmitFileCount++;
|
|
ASSERT( AfdActiveTransmitFileCount <= AfdMaxActiveTransmitFileCount );
|
|
|
|
IoReleaseCancelSpinLock( oldIrql );
|
|
|
|
AfdQueueTransmitRead(
|
|
transmitInfo
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Release the I/O cancel spinlock before returning.
|
|
//
|
|
|
|
IoReleaseCancelSpinLock( oldIrql );
|
|
|
|
}
|
|
|
|
} // AfdStartNextQueuedTransmitFile
|
|
|
|
|
|
BOOLEAN
|
|
AfdFastTransmitFile (
|
|
IN struct _FILE_OBJECT *FileObject,
|
|
IN PVOID InputBuffer OPTIONAL,
|
|
IN ULONG InputBufferLength,
|
|
OUT PIO_STATUS_BLOCK IoStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempts to perform a fast TransmitFile call. This will succeed
|
|
only if the caller requests write behind, the file data to be sent
|
|
is small, and the data is in the file system cache.
|
|
|
|
Arguments:
|
|
|
|
FileObject - the endpoint file object of interest.
|
|
|
|
InputBuffer - a buffer from the caller containing the
|
|
AFD_TRANSMIT_FILE_INFO structure.
|
|
|
|
InputBufferLength - the length of the above buffer.
|
|
|
|
IoStatus - points to the IO status block that will be set on successful
|
|
return from this function.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the fast path was successful; FALSE if we need to do through
|
|
the normal path.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAFD_ENDPOINT endpoint;
|
|
PAFD_CONNECTION connection;
|
|
PAFD_TRANSMIT_FILE_INFO userTransmitInfo;
|
|
PAFD_BUFFER afdBuffer;
|
|
ULONG sendLength;
|
|
PFILE_OBJECT fileObject;
|
|
BOOLEAN success;
|
|
BOOLEAN sendCountersUpdated;
|
|
KIRQL oldIrql;
|
|
ULONG fileWriteLength;
|
|
NTSTATUS status;
|
|
LARGE_INTEGER fileOffset;
|
|
PMDL fileMdl;
|
|
|
|
//
|
|
// Initialize locals so that cleanup is easier.
|
|
//
|
|
|
|
fileObject = NULL;
|
|
afdBuffer = NULL;
|
|
sendCountersUpdated = FALSE;
|
|
fileMdl = NULL;
|
|
|
|
//
|
|
// Any access to the user-specified input buffer must be done inside
|
|
// a try-except block.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Make sure that the flags are specified such that a fast-path
|
|
// TransmitFile is reasonable. The caller must have specified
|
|
// the write-behind flag, but not the disconnect or reuse
|
|
// socket flags.
|
|
//
|
|
|
|
userTransmitInfo = InputBuffer;
|
|
|
|
if ( userTransmitInfo->Flags != AFD_TF_WRITE_BEHIND ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Calculate the length the entire send.
|
|
//
|
|
|
|
fileWriteLength = userTransmitInfo->WriteLength.LowPart;
|
|
|
|
sendLength = userTransmitInfo->HeadLength +
|
|
fileWriteLength +
|
|
userTransmitInfo->TailLength;
|
|
|
|
//
|
|
// Require the following for the fast path:
|
|
//
|
|
// - The caller must specify the write length.
|
|
// - The write length must be less than the configured maximum.
|
|
// - If the entire send is larger than an AFD buffer page,
|
|
// we're going to use FsRtlMdlRead, so for purposes of
|
|
// simplicity there must be:
|
|
// - a head buffer, and
|
|
// - no tail buffer
|
|
// - The configured maximum will always be less than 4GB.
|
|
// - There be no limitation on the count of simultaneous
|
|
// TransmitFile calls. The fast path would work around
|
|
// this limit, if it exists.
|
|
// - The head buffer, if any, fits on a single page.
|
|
//
|
|
|
|
if ( userTransmitInfo->WriteLength.LowPart == 0
|
|
|
|
||
|
|
|
|
sendLength > AfdMaxFastTransmit
|
|
|
|
||
|
|
|
|
( sendLength > AfdMaxFastCopyTransmit &&
|
|
(userTransmitInfo->HeadLength == 0 ||
|
|
userTransmitInfo->TailLength != 0 ) )
|
|
|
|
||
|
|
|
|
userTransmitInfo->WriteLength.HighPart != 0
|
|
|
|
||
|
|
|
|
AfdMaxActiveTransmitFileCount != 0
|
|
|
|
||
|
|
|
|
userTransmitInfo->HeadLength > AfdBufferLengthForOnePage ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Initial request validity checks: is the endpoint connected, is
|
|
// the input buffer large enough, etc.
|
|
//
|
|
|
|
endpoint = FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
if ( endpoint->Type != AfdBlockTypeVcConnecting ||
|
|
endpoint->State != AfdEndpointStateConnected ) {
|
|
return FALSE;
|
|
}
|
|
|
|
connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint );
|
|
ASSERT( connection->Type == AfdBlockTypeConnection );
|
|
|
|
if ( InputBufferLength < sizeof(AFD_TRANSMIT_FILE_INFO) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Determine whether there is already too much send data
|
|
// pending on the connection. If there is too much send
|
|
// data, don't do the fast path.
|
|
//
|
|
|
|
if ( AfdShouldSendBlock( endpoint, connection, sendLength ) ) {
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// AfdShouldSendBlock() updates the send counters in the AFD
|
|
// connection object. Remember this fact so that we can clean
|
|
// them up if the fast path fails after this point.
|
|
//
|
|
|
|
sendCountersUpdated = TRUE;
|
|
|
|
//
|
|
// Grab an AFD buffer large enough to hold the entire send.
|
|
//
|
|
|
|
afdBuffer = AfdGetBuffer( sendLength, 0 );
|
|
if ( afdBuffer == NULL ) {
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Get a referenced pointer to the file object for the file that
|
|
// we're going to transmit. This call will fail if the file
|
|
// handle specified by the caller is invalid.
|
|
//
|
|
|
|
status = ObReferenceObjectByHandle(
|
|
userTransmitInfo->FileHandle,
|
|
FILE_READ_DATA,
|
|
*IoFileObjectType,
|
|
UserMode,
|
|
(PVOID *)&fileObject,
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// If the file system doesn't support the fast cache manager
|
|
// interface, bail and go through the IRP path.
|
|
//
|
|
|
|
if( ( fileObject->Flags & FO_CACHE_SUPPORTED ) == 0 ) {
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Grab the file offset into a local so that we know that the
|
|
// offset pointer we pass to FsRtlCopyRead is valid.
|
|
//
|
|
|
|
fileOffset = userTransmitInfo->Offset;
|
|
|
|
//
|
|
// Get the file data. If the amount of file data is small, copy
|
|
// it directly into the AFD buffer. If it is large, get an MDL
|
|
// chain for the data and chain it on to the AFD buffer chain.
|
|
//
|
|
|
|
if ( sendLength < AfdMaxFastCopyTransmit ) {
|
|
|
|
success = FsRtlCopyRead(
|
|
fileObject,
|
|
&fileOffset,
|
|
fileWriteLength,
|
|
FALSE,
|
|
0,
|
|
(PCHAR)afdBuffer->Buffer + userTransmitInfo->HeadLength,
|
|
IoStatus,
|
|
IoGetRelatedDeviceObject( fileObject )
|
|
);
|
|
|
|
//
|
|
// We're done with the file object, so deference it now.
|
|
//
|
|
|
|
ObDereferenceObject( fileObject );
|
|
fileObject = NULL;
|
|
|
|
} else {
|
|
|
|
success = FsRtlMdlRead(
|
|
fileObject,
|
|
&fileOffset,
|
|
fileWriteLength,
|
|
0,
|
|
&fileMdl,
|
|
IoStatus
|
|
);
|
|
|
|
//
|
|
// Save the file object in the AFD buffer. The send restart
|
|
// routine will handle dereferencing the file object and
|
|
// returning the file MDLs to the system.
|
|
//
|
|
|
|
afdBuffer->FileObject = fileObject;
|
|
afdBuffer->FileOffset = fileOffset.QuadPart;
|
|
afdBuffer->ReadLength = fileWriteLength;
|
|
}
|
|
|
|
if ( !success ) {
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// If we read less information than was requested, we must have
|
|
// hit the end of the file. Fail the transmit request, since
|
|
// this can only happen if the caller requested that we send
|
|
// more data than the file currently contains.
|
|
//
|
|
|
|
if ( IoStatus->Information < fileWriteLength ) {
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// We got all the file data, so things are looking good. Copy
|
|
// in the head and tail buffers, if necessary. Note that if we
|
|
// used MDL read, then there cannot be a tail buffer because of
|
|
// the check at the beginning of this function.
|
|
//
|
|
|
|
if ( userTransmitInfo->HeadLength > 0 ) {
|
|
RtlCopyMemory(
|
|
afdBuffer->Buffer,
|
|
userTransmitInfo->Head,
|
|
userTransmitInfo->HeadLength
|
|
);
|
|
}
|
|
|
|
if ( userTransmitInfo->TailLength > 0 ) {
|
|
RtlCopyMemory(
|
|
(PCHAR)afdBuffer->Buffer + userTransmitInfo->HeadLength +
|
|
fileWriteLength,
|
|
userTransmitInfo->Tail,
|
|
userTransmitInfo->TailLength
|
|
);
|
|
}
|
|
|
|
//
|
|
// We have to rebuild the MDL in the AFD buffer structure to
|
|
// represent exactly the number of bytes we're going to be
|
|
// sending. If the AFD buffer has all the send data, indicate
|
|
// that. If we did MDL file I/O, then chain the file data on
|
|
// to the head MDL.
|
|
//
|
|
|
|
if ( fileMdl == NULL ) {
|
|
afdBuffer->Mdl->ByteCount = sendLength;
|
|
} else {
|
|
afdBuffer->Mdl->ByteCount = userTransmitInfo->HeadLength;
|
|
afdBuffer->Mdl->Next = fileMdl;
|
|
}
|
|
|
|
SET_CHAIN_LENGTH( afdBuffer, sendLength );
|
|
|
|
//
|
|
// Remember the endpoint in the AFD buffer structure. We need
|
|
// this in order to access the endpoint in the restart routine.
|
|
//
|
|
|
|
afdBuffer->Context = endpoint;
|
|
|
|
//
|
|
// Use the IRP in the AFD buffer structure to give to the TDI
|
|
// provider. Build the TDI send request.
|
|
//
|
|
|
|
TdiBuildSend(
|
|
afdBuffer->Irp,
|
|
connection->DeviceObject,
|
|
connection->FileObject,
|
|
AfdRestartBufferSend,
|
|
afdBuffer,
|
|
afdBuffer->Mdl,
|
|
0,
|
|
sendLength
|
|
);
|
|
|
|
//
|
|
// Add a reference to the connection object since the send
|
|
// request will complete asynchronously.
|
|
//
|
|
|
|
REFERENCE_CONNECTION( connection );
|
|
|
|
//
|
|
// Call the transport to actually perform the send.
|
|
//
|
|
|
|
status = IoCallDriver( connection->DeviceObject, afdBuffer->Irp );
|
|
|
|
//
|
|
// Reset all the local variables that control cleanup. This is
|
|
// necessary because the send restart routine will handle all
|
|
// cleanup at this point, and we cannot duplicate cleanup in the
|
|
// case of a failure or exception below.
|
|
//
|
|
|
|
fileObject = NULL;
|
|
afdBuffer = NULL;
|
|
sendCountersUpdated = FALSE;
|
|
fileMdl = NULL;
|
|
|
|
//
|
|
// The fast path succeeded--complete the call. Note that we
|
|
// change the status code from what was returned by the TDI
|
|
// provider into STATUS_SUCCESS. This is because we don't want
|
|
// to complete the IRP with STATUS_PENDING etc.
|
|
//
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
IoStatus->Information = sendLength;
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// The call failed for some reason. Fail fast IO.
|
|
//
|
|
|
|
goto complete;
|
|
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
goto complete;
|
|
}
|
|
|
|
complete:
|
|
|
|
if ( afdBuffer != NULL ) {
|
|
afdBuffer->FileObject = NULL;
|
|
afdBuffer->Mdl->Next = NULL;
|
|
afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength;
|
|
AfdReturnBuffer( afdBuffer );
|
|
}
|
|
|
|
if ( fileMdl != NULL ) {
|
|
FsRtlMdlReadComplete( fileObject, fileMdl );
|
|
}
|
|
if ( fileObject != NULL ) {
|
|
ObDereferenceObject( fileObject );
|
|
}
|
|
|
|
if ( sendCountersUpdated ) {
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
|
connection->VcBufferredSendBytes -= sendLength;
|
|
connection->VcBufferredSendCount -= 1;
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
} // AfdFastTransmitFile
|
|
|
|
|
|
NTSTATUS
|
|
AfdMdlReadComplete(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PMDL MdlChain,
|
|
IN LONGLONG FileOffset,
|
|
IN ULONG Length
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completes a MDL read operation by calling FsRtlMdlReadComplete().
|
|
If this returns TRUE, cool. Otherwise (it returns FALSE) and this
|
|
routine will allocate a new IRP and submit it to the filesystem.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being read.
|
|
|
|
MdlChain - Supplies a pointer to an MDL chain returned from CcMdlRead.
|
|
|
|
FileOffset - Byte offset in file for desired data.
|
|
|
|
Length - Length of desired data in bytes.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PAFD_MDL_COMPLETION_CONTEXT context;
|
|
|
|
//
|
|
// If we're being called at low IRQL, we can handle the
|
|
// request immediately "in-line".
|
|
//
|
|
|
|
if( KeGetCurrentIrql() == LOW_LEVEL ) {
|
|
|
|
//
|
|
// First, try the fast path. If that succeeds, we're done.
|
|
//
|
|
|
|
if( FsRtlMdlReadComplete( FileObject, MdlChain ) ) {
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// Fast past failed, so create a new IRP.
|
|
//
|
|
|
|
irp = IoAllocateIrp(
|
|
FileObject->DeviceObject->StackSize,
|
|
FALSE
|
|
);
|
|
|
|
if( irp == NULL ) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
//
|
|
// Setup the IRP.
|
|
//
|
|
|
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|
|
|
irp->MdlAddress = MdlChain;
|
|
|
|
irpSp->MajorFunction = IRP_MJ_READ;
|
|
irpSp->MinorFunction = IRP_MN_MDL | IRP_MN_COMPLETE;
|
|
|
|
irpSp->Parameters.Read.Length = Length;
|
|
irpSp->Parameters.Read.ByteOffset = *(PLARGE_INTEGER)&FileOffset;
|
|
irpSp->Parameters.Read.Key = 0;
|
|
|
|
irpSp->FileObject = FileObject;
|
|
irpSp->DeviceObject = IoGetRelatedDeviceObject( FileObject );
|
|
|
|
//
|
|
// Submit the IRP.
|
|
//
|
|
|
|
IoSetCompletionRoutine(
|
|
irp,
|
|
AfdRestartMdlReadComplete,
|
|
NULL,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
return IoCallDriver( irpSp->DeviceObject, irp );
|
|
|
|
}
|
|
|
|
//
|
|
// We're being called at raised IRQL (probably in a TDI completion
|
|
// routine) so we cannot touch the filesystem. We'll fire off a
|
|
// worker thread to do the actual completion.
|
|
//
|
|
// Allocate a deferred completion context.
|
|
//
|
|
|
|
context = AfdAllocateMdlCompletionContext();
|
|
|
|
if( context == NULL ) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
//
|
|
// Initialize the context and queue it to the worker thread.
|
|
//
|
|
|
|
context->FileObject = FileObject;
|
|
context->MdlChain = MdlChain;
|
|
context->FileOffset = FileOffset;
|
|
context->Length = Length;
|
|
|
|
//
|
|
// Add a reference to the FileObject so it doesn't go away
|
|
// before our completion routine gets called
|
|
//
|
|
|
|
ObReferenceObject( FileObject );
|
|
|
|
AfdQueueWorkItem(
|
|
AfdDeferredMdlReadComplete,
|
|
&context->WorkItem
|
|
);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // AfdMdlReadComplete
|
|
|
|
|
|
NTSTATUS
|
|
AfdRestartMdlReadComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completion routine for IRPs issued by AfdMdlReadComplete. The only
|
|
purpose of this completion routine is to free the IRPs created by
|
|
AfdMdlReadComplete().
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Unused.
|
|
|
|
Irp - The completed IRP.
|
|
|
|
Context - Unused.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Free the IRP since it's no longer needed.
|
|
//
|
|
|
|
IoFreeIrp( Irp );
|
|
|
|
//
|
|
// Return STATUS_MORE_PROCESSING_REQUIRED so that IoCompleteRequest
|
|
// will stop working on the IRP.
|
|
//
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // AfdRestartMdlReadComplete
|
|
|
|
|
|
VOID
|
|
AfdDeferredMdlReadComplete(
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delayed worker thread routine for completing MDL reads. This routine
|
|
is queued if we call AfdMdlReadComplete() at raised IRQL. Since it
|
|
is invalid to call file systems at raised IRQL, this routine is
|
|
scheduled to do the dirty work.
|
|
|
|
Arguments:
|
|
|
|
Context - Points to the AFD_WORK_ITEM structure embedded within the
|
|
AFD_MDL_COMPLETION_CONTEXT structure defining the MDL to complete.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PAFD_MDL_COMPLETION_CONTEXT mdlComplete;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( KeGetCurrentIrql() == LOW_LEVEL );
|
|
ASSERT( Context != NULL );
|
|
|
|
//
|
|
// Get the completion context pointer.
|
|
//
|
|
|
|
mdlComplete = CONTAINING_RECORD(
|
|
Context,
|
|
AFD_MDL_COMPLETION_CONTEXT,
|
|
WorkItem
|
|
);
|
|
|
|
//
|
|
// Let AfdMdlReadComplete do the dirty work.
|
|
//
|
|
|
|
AfdMdlReadComplete(
|
|
mdlComplete->FileObject,
|
|
mdlComplete->MdlChain,
|
|
mdlComplete->FileOffset,
|
|
mdlComplete->Length
|
|
);
|
|
|
|
//
|
|
// Remove the reference we added in AfdMdlReadComplete
|
|
//
|
|
|
|
ObDereferenceObject( mdlComplete->FileObject );
|
|
|
|
//
|
|
// Free the context.
|
|
//
|
|
|
|
AfdFreeMdlCompletionContext( mdlComplete );
|
|
|
|
} // AfdDeferredMdlReadComplete
|