846 lines
29 KiB
C
846 lines
29 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989-1999 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
close.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains code for cleanup and close IRPs.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
David Treadwell (davidtr) 18-Mar-1992
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "afdp.h"
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text( PAGE, AfdClose )
|
|||
|
#pragma alloc_text( PAGEAFD, AfdCleanup )
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FASTCALL
|
|||
|
AfdCleanup (
|
|||
|
IN PIRP Irp,
|
|||
|
IN PIO_STACK_LOCATION IrpSp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the routine that handles Cleanup IRPs in AFD.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - Pointer to I/O request packet.
|
|||
|
|
|||
|
IrpSp - pointer to the IO stack location to use for this request.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS -- Indicates whether the request was successfully queued.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PAFD_ENDPOINT endpoint;
|
|||
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|||
|
PLIST_ENTRY listEntry;
|
|||
|
LARGE_INTEGER processExitTime;
|
|||
|
|
|||
|
endpoint = IrpSp->FileObject->FsContext;
|
|||
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|||
|
|
|||
|
IF_DEBUG(OPEN_CLOSE) {
|
|||
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|||
|
"AfdCleanup: cleanup on file object %p, endpoint %p, connection %p\n",
|
|||
|
IrpSp->FileObject,
|
|||
|
endpoint,
|
|||
|
AFD_CONNECTION_FROM_ENDPOINT( endpoint )
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the process exit time while still at low IRQL.
|
|||
|
//
|
|||
|
|
|||
|
processExitTime = PsGetProcessExitTime( );
|
|||
|
|
|||
|
//
|
|||
|
// Indicate that there was a local close on this endpoint. If there
|
|||
|
// are any outstanding polls on this endpoint, they will be
|
|||
|
// completed now.
|
|||
|
//
|
|||
|
AfdIndicatePollEvent(
|
|||
|
endpoint,
|
|||
|
AFD_POLL_LOCAL_CLOSE,
|
|||
|
STATUS_SUCCESS
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Remember that the endpoint has been cleaned up. This is important
|
|||
|
// because it allows AfdRestartAccept to know that the endpoint has
|
|||
|
// been cleaned up and that it should toss the connection.
|
|||
|
//
|
|||
|
|
|||
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|||
|
|
|||
|
AfdIndicateEventSelectEvent (endpoint, AFD_POLL_LOCAL_CLOSE, STATUS_SUCCESS);
|
|||
|
|
|||
|
ASSERT( endpoint->EndpointCleanedUp == FALSE );
|
|||
|
endpoint->EndpointCleanedUp = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// If this a datagram endpoint, cancel all IRPs and free any buffers
|
|||
|
// of data. Note that if the state of the endpoint is just "open"
|
|||
|
// (not bound, etc.) then we can't have any pended IRPs or datagrams
|
|||
|
// on the endpoint. Also, the lists of IRPs and datagrams may not
|
|||
|
// yet been initialized if the state is just open.
|
|||
|
//
|
|||
|
|
|||
|
if ( IS_DGRAM_ENDPOINT(endpoint) ) {
|
|||
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|||
|
|
|||
|
if ( endpoint->State == AfdEndpointStateBound ||
|
|||
|
endpoint->State == AfdEndpointStateConnected) {
|
|||
|
|
|||
|
//
|
|||
|
// Reset the counts of datagrams bufferred on the endpoint.
|
|||
|
// This prevents anyone from thinking that there is bufferred
|
|||
|
// data on the endpoint.
|
|||
|
//
|
|||
|
|
|||
|
endpoint->DgBufferredReceiveCount = 0;
|
|||
|
endpoint->DgBufferredReceiveBytes = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Cancel all receive datagram and peek datagram IRPs on the
|
|||
|
// endpoint.
|
|||
|
//
|
|||
|
|
|||
|
AfdCompleteIrpList(
|
|||
|
&endpoint->ReceiveDatagramIrpListHead,
|
|||
|
endpoint,
|
|||
|
STATUS_CANCELLED,
|
|||
|
AfdCleanupReceiveDatagramIrp
|
|||
|
);
|
|||
|
|
|||
|
AfdCompleteIrpList(
|
|||
|
&endpoint->PeekDatagramIrpListHead,
|
|||
|
endpoint,
|
|||
|
STATUS_CANCELLED,
|
|||
|
AfdCleanupReceiveDatagramIrp
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
else if (IS_SAN_ENDPOINT (endpoint)) {
|
|||
|
if (!IsListEmpty (&endpoint->Common.SanEndp.IrpList)) {
|
|||
|
PIRP irp;
|
|||
|
PDRIVER_CANCEL cancelRoutine;
|
|||
|
KIRQL cancelIrql;
|
|||
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|||
|
|
|||
|
//
|
|||
|
// Acquire cancel spinlock and endpoint spinlock in
|
|||
|
// this order and recheck the IRP list
|
|||
|
//
|
|||
|
IoAcquireCancelSpinLock (&cancelIrql);
|
|||
|
AfdAcquireSpinLockAtDpcLevel (&endpoint->SpinLock, &lockHandle);
|
|||
|
|
|||
|
//
|
|||
|
// While list is not empty attempt to cancel the IRPs
|
|||
|
//
|
|||
|
while (!IsListEmpty (&endpoint->Common.SanEndp.IrpList)) {
|
|||
|
irp = CONTAINING_RECORD (
|
|||
|
endpoint->Common.SanEndp.IrpList.Flink,
|
|||
|
IRP,
|
|||
|
Tail.Overlay.ListEntry);
|
|||
|
//
|
|||
|
// Reset the cancel routine.
|
|||
|
//
|
|||
|
cancelRoutine = IoSetCancelRoutine (irp, NULL);
|
|||
|
if (cancelRoutine!=NULL) {
|
|||
|
//
|
|||
|
// Cancel routine was not NULL, cancel it here.
|
|||
|
// If someone else attempts to complete this IRP
|
|||
|
// it will have to wait at least until cancel
|
|||
|
// spinlock is released, so we can release
|
|||
|
// the endpoint spinlock
|
|||
|
//
|
|||
|
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &lockHandle);
|
|||
|
irp->CancelIrql = cancelIrql;
|
|||
|
irp->Cancel = TRUE;
|
|||
|
(*cancelRoutine) (AfdDeviceObject, irp);
|
|||
|
|
|||
|
//
|
|||
|
// Reacquire the locks
|
|||
|
//
|
|||
|
IoAcquireCancelSpinLock (&cancelIrql);
|
|||
|
AfdAcquireSpinLockAtDpcLevel (&endpoint->SpinLock, &lockHandle);
|
|||
|
}
|
|||
|
}
|
|||
|
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &lockHandle);
|
|||
|
IoReleaseCancelSpinLock (cancelIrql);
|
|||
|
}
|
|||
|
else {
|
|||
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|||
|
}
|
|||
|
}
|
|||
|
else if (IS_SAN_HELPER (endpoint)) {
|
|||
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|||
|
if (endpoint==AfdSanServiceHelper) {
|
|||
|
//
|
|||
|
// Last handle to the service helper is being closed.
|
|||
|
// We can no longer count on it, clear our global.
|
|||
|
//
|
|||
|
KeEnterCriticalRegion ();
|
|||
|
ExAcquireResourceExclusiveLite( AfdResource, TRUE );
|
|||
|
AfdSanServiceHelper = NULL;
|
|||
|
ExReleaseResourceLite( AfdResource );
|
|||
|
KeLeaveCriticalRegion ();
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
PAFD_CONNECTION connection;
|
|||
|
|
|||
|
connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint );
|
|||
|
ASSERT( connection == NULL || connection->Type == AfdBlockTypeConnection );
|
|||
|
|
|||
|
//
|
|||
|
// Reference the connection object so that it does not go away while
|
|||
|
// we are freeing the resources
|
|||
|
//
|
|||
|
|
|||
|
if (connection!=NULL) {
|
|||
|
REFERENCE_CONNECTION (connection);
|
|||
|
|
|||
|
//
|
|||
|
// If this is a connected non-datagram socket and the send side has
|
|||
|
// not been disconnected and there is no outstanding data to be
|
|||
|
// received, begin a graceful disconnect on the connection. If there
|
|||
|
// is unreceived data out outstanding IO, abort the connection.
|
|||
|
//
|
|||
|
|
|||
|
if ( (endpoint->State == AfdEndpointStateConnected
|
|||
|
|| endpoint->State==AfdEndpointStateBound
|
|||
|
|| endpoint->State==AfdEndpointStateTransmitClosing)
|
|||
|
// Endpoint is in bound state when connection
|
|||
|
// request is in progress, we still need
|
|||
|
// to abort those.
|
|||
|
|
|||
|
&&
|
|||
|
connection->ConnectedReferenceAdded
|
|||
|
|
|||
|
&&
|
|||
|
|
|||
|
!endpoint->afdC_Root // these are connected on bind
|
|||
|
|
|||
|
&&
|
|||
|
|
|||
|
( (endpoint->DisconnectMode & AFD_ABORTIVE_DISCONNECT) == 0)
|
|||
|
|
|||
|
&&
|
|||
|
|
|||
|
( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) == 0 ||
|
|||
|
IS_DATA_ON_CONNECTION(connection) ||
|
|||
|
IS_EXPEDITED_DATA_ON_CONNECTION(connection) ||
|
|||
|
( !IS_TDI_BUFFERRING(endpoint) &&
|
|||
|
connection->Common.NonBufferring.ReceiveBytesInTransport > 0 ) ||
|
|||
|
endpoint->StateChangeInProgress)
|
|||
|
|
|||
|
&&
|
|||
|
|
|||
|
!connection->AbortIndicated ) {
|
|||
|
|
|||
|
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting );
|
|||
|
|
|||
|
if ( IS_DATA_ON_CONNECTION( connection )
|
|||
|
|
|||
|
||
|
|||
|
|
|||
|
IS_EXPEDITED_DATA_ON_CONNECTION( connection )
|
|||
|
|
|||
|
||
|
|||
|
|
|||
|
( !IS_TDI_BUFFERRING(endpoint) &&
|
|||
|
connection->Common.NonBufferring.ReceiveBytesInTransport > 0 )
|
|||
|
|
|||
|
||
|
|||
|
|
|||
|
processExitTime.QuadPart != 0
|
|||
|
|
|||
|
||
|
|||
|
|
|||
|
endpoint->OutstandingIrpCount != 0
|
|||
|
|
|||
|
||
|
|||
|
|
|||
|
endpoint->StateChangeInProgress
|
|||
|
|
|||
|
||
|
|||
|
|
|||
|
( !IS_TDI_BUFFERRING(endpoint) &&
|
|||
|
(!IsListEmpty( &connection->VcReceiveIrpListHead ) ||
|
|||
|
!IsListEmpty( &connection->VcSendIrpListHead )) )
|
|||
|
|
|||
|
) {
|
|||
|
|
|||
|
#if DBG
|
|||
|
if ( processExitTime.QuadPart != 0 ) {
|
|||
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|||
|
"AfdCleanup: process exiting w/o closesocket, aborting endp %p\n",
|
|||
|
endpoint ));
|
|||
|
}
|
|||
|
else {
|
|||
|
if ( IS_DATA_ON_CONNECTION( connection ) ) {
|
|||
|
if (IS_TDI_BUFFERRING(endpoint)) {
|
|||
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|||
|
"AfdCleanup: unrecv'd data on endp %p, aborting. "
|
|||
|
"%ld ind, %ld taken, %ld out\n",
|
|||
|
endpoint,
|
|||
|
connection->Common.Bufferring.ReceiveBytesIndicated,
|
|||
|
connection->Common.Bufferring.ReceiveBytesTaken,
|
|||
|
connection->Common.Bufferring.ReceiveBytesOutstanding ));
|
|||
|
}
|
|||
|
else {
|
|||
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|||
|
"AfdCleanup: unrecv'd data (%ld) on endp %p, aborting.\n",
|
|||
|
connection->Common.NonBufferring.BufferredReceiveBytes,
|
|||
|
endpoint ));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( IS_EXPEDITED_DATA_ON_CONNECTION( connection ) ) {
|
|||
|
if (IS_TDI_BUFFERRING(endpoint)) {
|
|||
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|||
|
"AfdCleanup: unrecv'd exp data on endp %p, aborting. "
|
|||
|
"%ld ind, %ld taken, %ld out\n",
|
|||
|
endpoint,
|
|||
|
connection->Common.Bufferring.ReceiveExpeditedBytesIndicated,
|
|||
|
connection->Common.Bufferring.ReceiveExpeditedBytesTaken,
|
|||
|
connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding ));
|
|||
|
}
|
|||
|
else {
|
|||
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|||
|
"AfdCleanup: unrecv'd exp data (%ld) on endp %p, aborting.\n",
|
|||
|
connection->Common.NonBufferring.BufferredExpeditedBytes,
|
|||
|
endpoint ));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( !IS_TDI_BUFFERRING(endpoint) &&
|
|||
|
connection->Common.NonBufferring.ReceiveBytesInTransport > 0 ) {
|
|||
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|||
|
"AfdCleanup: unrecv'd data (%ld) in transport on endp %p, aborting.\n",
|
|||
|
connection->Common.NonBufferring.ReceiveBytesInTransport,
|
|||
|
endpoint));
|
|||
|
}
|
|||
|
|
|||
|
if ( endpoint->OutstandingIrpCount != 0 ) {
|
|||
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|||
|
"AfdCleanup: %ld IRPs %s outstanding on endpoint %p, "
|
|||
|
"aborting.\n",
|
|||
|
endpoint->OutstandingIrpCount,
|
|||
|
(endpoint->StateChangeInProgress
|
|||
|
? "(accept, connect, or transmit file)"
|
|||
|
: ""),
|
|||
|
endpoint ));
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|||
|
|
|||
|
(VOID)AfdBeginAbort( connection );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_RECEIVE;
|
|||
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|||
|
|
|||
|
status = AfdBeginDisconnect( endpoint, NULL, NULL );
|
|||
|
if (!NT_SUCCESS (status)) {
|
|||
|
//
|
|||
|
// If disconnect failed, we have no choice but to abort the
|
|||
|
// connection because we cannot return error from close and
|
|||
|
// have application try it again. If we don't abort, connection
|
|||
|
// ends up hanging there forever.
|
|||
|
//
|
|||
|
(VOID)AfdBeginAbort (connection);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this is a connected VC endpoint on a nonbufferring TDI provider,
|
|||
|
// cancel all outstanding send and receive IRPs.
|
|||
|
//
|
|||
|
|
|||
|
if ( !IS_TDI_BUFFERRING(endpoint) ) {
|
|||
|
|
|||
|
AfdCompleteIrpList(
|
|||
|
&connection->VcReceiveIrpListHead,
|
|||
|
endpoint,
|
|||
|
STATUS_CANCELLED,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
AfdCompleteIrpList(
|
|||
|
&connection->VcSendIrpListHead,
|
|||
|
endpoint,
|
|||
|
STATUS_CANCELLED,
|
|||
|
AfdCleanupSendIrp
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Remember that we have started cleanup on this connection.
|
|||
|
// We know that we'll never get a request on the connection
|
|||
|
// after we start cleanup on the connection.
|
|||
|
//
|
|||
|
|
|||
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|||
|
connection->CleanupBegun = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Attempt to remove the connected reference.
|
|||
|
//
|
|||
|
|
|||
|
AfdDeleteConnectedReference( connection, TRUE );
|
|||
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|||
|
|
|||
|
//
|
|||
|
// Remove connection reference added in the beginning of this
|
|||
|
// function. We can't access connection object after this point
|
|||
|
// because in can be freed inside the AfdDereferenceConnection.
|
|||
|
//
|
|||
|
|
|||
|
DEREFERENCE_CONNECTION (connection);
|
|||
|
connection = NULL;
|
|||
|
}
|
|||
|
else {
|
|||
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Complete any outstanding wait for listen IRPs on the endpoint.
|
|||
|
//
|
|||
|
|
|||
|
if ( (endpoint->Type & AfdBlockTypeVcListening) == AfdBlockTypeVcListening ) {
|
|||
|
|
|||
|
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
|||
|
while ( !IsListEmpty( &endpoint->Common.VcListening.ListeningIrpListHead ) ) {
|
|||
|
|
|||
|
PIRP waitForListenIrp;
|
|||
|
PIO_STACK_LOCATION irpSp;
|
|||
|
|
|||
|
listEntry = RemoveHeadList( &endpoint->Common.VcListening.ListeningIrpListHead );
|
|||
|
waitForListenIrp = CONTAINING_RECORD(
|
|||
|
listEntry,
|
|||
|
IRP,
|
|||
|
Tail.Overlay.ListEntry
|
|||
|
);
|
|||
|
//
|
|||
|
// Set FLink to NULL so that cancel routine won't touch the IRP.
|
|||
|
//
|
|||
|
|
|||
|
listEntry->Flink = NULL;
|
|||
|
|
|||
|
irpSp = IoGetCurrentIrpStackLocation (waitForListenIrp);
|
|||
|
if (irpSp->MajorFunction==IRP_MJ_INTERNAL_DEVICE_CONTROL) {
|
|||
|
AfdCleanupSuperAccept (waitForListenIrp, STATUS_CANCELLED);
|
|||
|
}
|
|||
|
else {
|
|||
|
waitForListenIrp->IoStatus.Status = STATUS_CANCELLED;
|
|||
|
waitForListenIrp->IoStatus.Information = 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Release the AFD spin lock so that we can complete the
|
|||
|
// wait for listen IRP.
|
|||
|
//
|
|||
|
|
|||
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|||
|
|
|||
|
//
|
|||
|
// Cancel the IRP.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Reset the cancel routine in the IRP.
|
|||
|
//
|
|||
|
|
|||
|
if ( IoSetCancelRoutine( waitForListenIrp, NULL ) == NULL ) {
|
|||
|
KIRQL cancelIrql;
|
|||
|
|
|||
|
//
|
|||
|
// If the cancel routine was NULL then cancel routine
|
|||
|
// may be running. Wait on the cancel spinlock until
|
|||
|
// the cancel routine is done.
|
|||
|
//
|
|||
|
// Note: The cancel routine will not find the IRP
|
|||
|
// since it is not in the list.
|
|||
|
//
|
|||
|
|
|||
|
IoAcquireCancelSpinLock( &cancelIrql );
|
|||
|
ASSERT( waitForListenIrp->Cancel );
|
|||
|
IoReleaseCancelSpinLock( cancelIrql );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
IoCompleteRequest( waitForListenIrp, AfdPriorityBoost );
|
|||
|
|
|||
|
//
|
|||
|
// Reacquire the AFD spin lock for the next pass through the
|
|||
|
// loop.
|
|||
|
//
|
|||
|
|
|||
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free all queued (free, unaccepted, and returned) connections
|
|||
|
// on the endpoint.
|
|||
|
//
|
|||
|
|
|||
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|||
|
AfdFreeQueuedConnections( endpoint );
|
|||
|
endpoint->Common.VcListening.FailedConnectionAdds = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If there are pending routing notifications on endpoint, cancel them.
|
|||
|
// We must hold both cancel and endpoint spinlocks to make
|
|||
|
// sure that IRP is not completed as we are cancelling it
|
|||
|
//
|
|||
|
|
|||
|
if (endpoint->RoutingQueryReferenced) {
|
|||
|
KIRQL cancelIrql;
|
|||
|
SHORT queryAddrType;
|
|||
|
IoAcquireCancelSpinLock( &cancelIrql );
|
|||
|
AfdAcquireSpinLockAtDpcLevel( &endpoint->SpinLock, &lockHandle );
|
|||
|
|
|||
|
//
|
|||
|
// Can only have one cleanup IRP per endpoint
|
|||
|
// So this should not change
|
|||
|
//
|
|||
|
|
|||
|
ASSERT (endpoint->RoutingQueryReferenced);
|
|||
|
endpoint->RoutingQueryReferenced = FALSE;
|
|||
|
if (endpoint->RoutingQueryIPv6) {
|
|||
|
queryAddrType = TDI_ADDRESS_TYPE_IP6;
|
|||
|
endpoint->RoutingQueryIPv6 = FALSE;
|
|||
|
}
|
|||
|
else {
|
|||
|
queryAddrType = TDI_ADDRESS_TYPE_IP;
|
|||
|
}
|
|||
|
|
|||
|
listEntry = endpoint->RoutingNotifications.Flink;
|
|||
|
while (listEntry!=&endpoint->RoutingNotifications) {
|
|||
|
PIRP notifyIrp;
|
|||
|
PROUTING_NOTIFY notifyCtx = CONTAINING_RECORD (listEntry,
|
|||
|
ROUTING_NOTIFY,
|
|||
|
NotifyListLink);
|
|||
|
listEntry = listEntry->Flink;
|
|||
|
|
|||
|
//
|
|||
|
// Check if IRP has not been completed yet
|
|||
|
//
|
|||
|
|
|||
|
notifyIrp = (PIRP)InterlockedExchangePointer ((PVOID *)¬ifyCtx->NotifyIrp, NULL);
|
|||
|
if (notifyIrp!=NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Remove it from the list and call cancel routing while still
|
|||
|
// holding cancel spinlock
|
|||
|
//
|
|||
|
|
|||
|
RemoveEntryList (¬ifyCtx->NotifyListLink);
|
|||
|
AfdReleaseSpinLockFromDpcLevel ( &endpoint->SpinLock, &lockHandle);
|
|||
|
notifyIrp->CancelIrql = cancelIrql;
|
|||
|
AfdCancelIrp (notifyIrp);
|
|||
|
|
|||
|
//
|
|||
|
// Reacquire cancel and endpoint spinlocks
|
|||
|
//
|
|||
|
|
|||
|
IoAcquireCancelSpinLock( &cancelIrql );
|
|||
|
AfdAcquireSpinLockAtDpcLevel( &endpoint->SpinLock, &lockHandle );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
AfdReleaseSpinLockFromDpcLevel ( &endpoint->SpinLock, &lockHandle);
|
|||
|
IoReleaseCancelSpinLock( cancelIrql );
|
|||
|
|
|||
|
AfdDereferenceRoutingQuery (queryAddrType);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|||
|
while (!IsListEmpty (&endpoint->RequestList)) {
|
|||
|
PAFD_REQUEST_CONTEXT requestCtx;
|
|||
|
listEntry = RemoveHeadList (&endpoint->RequestList);
|
|||
|
listEntry->Flink = NULL;
|
|||
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|||
|
requestCtx = CONTAINING_RECORD (listEntry,
|
|||
|
AFD_REQUEST_CONTEXT,
|
|||
|
EndpointListLink);
|
|||
|
(*requestCtx->CleanupRoutine) (endpoint, requestCtx);
|
|||
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|||
|
}
|
|||
|
|
|||
|
if ( endpoint->Irp != NULL) {
|
|||
|
|
|||
|
PIRP transmitIrp;
|
|||
|
KIRQL cancelIrql;
|
|||
|
|
|||
|
//
|
|||
|
// Release the endpoint and acquire the cancel spinlock
|
|||
|
// and then the enpoint spinlock.
|
|||
|
//
|
|||
|
|
|||
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|||
|
|
|||
|
IoAcquireCancelSpinLock( &cancelIrql );
|
|||
|
AfdAcquireSpinLockAtDpcLevel( &endpoint->SpinLock, &lockHandle );
|
|||
|
|
|||
|
//
|
|||
|
// Make sure there is still a transmit IRP.
|
|||
|
//
|
|||
|
|
|||
|
transmitIrp = endpoint->Irp;
|
|||
|
|
|||
|
if ( transmitIrp != NULL ) {
|
|||
|
PDRIVER_CANCEL cancelRoutine;
|
|||
|
|
|||
|
// indicate that it has to be cancelled
|
|||
|
transmitIrp->Cancel = TRUE;
|
|||
|
cancelRoutine = IoSetCancelRoutine( transmitIrp, NULL );
|
|||
|
if ( cancelRoutine != NULL ) {
|
|||
|
|
|||
|
//
|
|||
|
// The IRP needs to be canceled. Release the
|
|||
|
// endpoint spinlock. The value in endpoint->Irp can
|
|||
|
// now change, but the IRP cannot be completed while the
|
|||
|
// cancel spinlock is held since we set the cancel flag
|
|||
|
// in the IRP.
|
|||
|
//
|
|||
|
|
|||
|
AfdReleaseSpinLockFromDpcLevel( &endpoint->SpinLock, &lockHandle );
|
|||
|
|
|||
|
transmitIrp->CancelIrql = cancelIrql;
|
|||
|
cancelRoutine ( NULL, transmitIrp );
|
|||
|
}
|
|||
|
else {
|
|||
|
// The IRP has not been completely setup yet
|
|||
|
// and will be cancelled in the dispatch routine
|
|||
|
AfdReleaseSpinLockFromDpcLevel( &endpoint->SpinLock, &lockHandle );
|
|||
|
IoReleaseCancelSpinLock( cancelIrql );
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// The IRP has been completed or canceled. Release the locks
|
|||
|
// and continue.
|
|||
|
//
|
|||
|
|
|||
|
AfdReleaseSpinLockFromDpcLevel( &endpoint->SpinLock, &lockHandle );
|
|||
|
IoReleaseCancelSpinLock( cancelIrql );
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Remember the new state of the endpoint.
|
|||
|
//
|
|||
|
|
|||
|
//endpoint->State = AfdEndpointStateCleanup;
|
|||
|
|
|||
|
//
|
|||
|
// Reset relevant event handlers on the endpoint. This prevents
|
|||
|
// getting indications after we free the endpoint and connection
|
|||
|
// objects. We should not be able to get new connects after this
|
|||
|
// handle has been cleaned up.
|
|||
|
//
|
|||
|
// Note that these calls can fail if, for example, DHCP changes the
|
|||
|
// host's IP address while the endpoint is active.
|
|||
|
//
|
|||
|
//
|
|||
|
|
|||
|
if ( endpoint->AddressHandle != NULL ) {
|
|||
|
|
|||
|
if ( endpoint->Listening) {
|
|||
|
status = AfdSetEventHandler(
|
|||
|
endpoint->AddressFileObject,
|
|||
|
TDI_EVENT_CONNECT,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
//ASSERT( NT_SUCCESS(status) );
|
|||
|
}
|
|||
|
|
|||
|
if ( IS_DGRAM_ENDPOINT(endpoint) ) {
|
|||
|
status = AfdSetEventHandler(
|
|||
|
endpoint->AddressFileObject,
|
|||
|
TDI_EVENT_RECEIVE_DATAGRAM,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
//ASSERT( NT_SUCCESS(status) );
|
|||
|
|
|||
|
status = AfdSetEventHandler(
|
|||
|
endpoint->AddressFileObject,
|
|||
|
TDI_EVENT_ERROR_EX,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
//ASSERT( NT_SUCCESS(status) );
|
|||
|
status = AfdSetEventHandler(
|
|||
|
endpoint->AddressFileObject,
|
|||
|
TDI_EVENT_ERROR,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
//ASSERT( NT_SUCCESS(status) );
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
InterlockedIncrement(
|
|||
|
&AfdEndpointsCleanedUp
|
|||
|
);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
} // AfdCleanup
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FASTCALL
|
|||
|
AfdClose (
|
|||
|
IN PIRP Irp,
|
|||
|
IN PIO_STACK_LOCATION IrpSp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the routine that handles Close IRPs in AFD. It
|
|||
|
dereferences the endpoint specified in the IRP, which will result in
|
|||
|
the endpoint being freed when all other references go away.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - Pointer to I/O request packet.
|
|||
|
|
|||
|
IrpSp - pointer to the IO stack location to use for this request.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS -- Indicates whether the request was successfully queued.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PAFD_ENDPOINT endpoint;
|
|||
|
PAFD_CONNECTION connection;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
endpoint = IrpSp->FileObject->FsContext;
|
|||
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|||
|
|
|||
|
IF_DEBUG(OPEN_CLOSE) {
|
|||
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|||
|
"AfdClose: closing file object %p, endpoint %p\n",
|
|||
|
IrpSp->FileObject, endpoint ));
|
|||
|
}
|
|||
|
|
|||
|
IF_DEBUG(ENDPOINT) {
|
|||
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|||
|
"AfdClose: closing endpoint at %p\n",
|
|||
|
endpoint ));
|
|||
|
}
|
|||
|
|
|||
|
connection = AFD_CONNECTION_FROM_ENDPOINT (endpoint);
|
|||
|
|
|||
|
//
|
|||
|
// If there is a connection on this endpoint, dereference it here
|
|||
|
// rather than in AfdDereferenceEndpoint, because the connection
|
|||
|
// has a referenced pointer to the endpoint which must be removed
|
|||
|
// before the endpoint can dereference the connection.
|
|||
|
//
|
|||
|
|
|||
|
if (connection != NULL) {
|
|||
|
endpoint->Common.VcConnecting.Connection = NULL;
|
|||
|
DEREFERENCE_CONNECTION (connection);
|
|||
|
//
|
|||
|
// This is to simplify debugging.
|
|||
|
// If connection is not being closed by the transport
|
|||
|
// we want to be able to find it in the debugger faster
|
|||
|
// then thru !poolfind AfdC.
|
|||
|
//
|
|||
|
endpoint->WorkItem.Context = connection;
|
|||
|
}
|
|||
|
else if (IS_SAN_ENDPOINT (endpoint) &&
|
|||
|
endpoint->Common.SanEndp.SwitchContext!=NULL) {
|
|||
|
PVOID requestCtx;
|
|||
|
endpoint->Common.SanEndp.FileObject = NULL;
|
|||
|
requestCtx = AFD_SWITCH_MAKE_REQUEST_CONTEXT(
|
|||
|
((ULONG)0xFFFFFFFF),
|
|||
|
AFD_SWITCH_REQUEST_CLOSE);
|
|||
|
IoSetIoCompletion (
|
|||
|
endpoint->Common.SanEndp.SanHlpr->Common.SanHlpr.IoCompletionPort,// Port
|
|||
|
endpoint->Common.SanEndp.SwitchContext, // Key
|
|||
|
requestCtx, // ApcContext
|
|||
|
STATUS_SUCCESS, // Status
|
|||
|
0, // Information
|
|||
|
FALSE // ChargeQuota
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the state of the endpoint to closing and dereference to
|
|||
|
// get rid of the active reference.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT (endpoint->State!=AfdEndpointStateClosing);
|
|||
|
endpoint->State = AfdEndpointStateClosing;
|
|||
|
|
|||
|
//
|
|||
|
// Dereference the endpoint to get rid of the active reference.
|
|||
|
// This will result in the endpoint storage being freed as soon
|
|||
|
// as all other references go away.
|
|||
|
//
|
|||
|
|
|||
|
DEREFERENCE_ENDPOINT( endpoint );
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
} // AfdClose
|