diff options
Diffstat (limited to 'private/ntos/afd/close.c')
-rw-r--r-- | private/ntos/afd/close.c | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/private/ntos/afd/close.c b/private/ntos/afd/close.c new file mode 100644 index 000000000..0845c4e6d --- /dev/null +++ b/private/ntos/afd/close.c @@ -0,0 +1,488 @@ +/*++ + +Copyright (c) 1989 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 +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; + PAFD_CONNECTION connection; + KIRQL oldIrql1, oldIrql2; + KIRQL cancelIrql; + PLIST_ENTRY listEntry; + LARGE_INTEGER processExitTime; + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + IF_DEBUG(OPEN_CLOSE) { + KdPrint(( + "AfdCleanup: cleanup on file object %lx, endpoint %lx, connection %lx\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_BIT, + 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( &AfdSpinLock, &oldIrql1 ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql2 ); + + ASSERT( endpoint->EndpointCleanedUp == FALSE ); + endpoint->EndpointCleanedUp = TRUE; + + connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint ); + ASSERT( connection == NULL || connection->Type == AfdBlockTypeConnection ); + + // + // Complete any outstanding wait for listen IRPs on the endpoint. + // + + if ( endpoint->Type == AfdBlockTypeVcListening ) { + + while ( !IsListEmpty( &endpoint->Common.VcListening.ListeningIrpListHead ) ) { + + PIRP waitForListenIrp; + + listEntry = RemoveHeadList( &endpoint->Common.VcListening.ListeningIrpListHead ); + waitForListenIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + + // + // Release the AFD spin lock so that we can complete the + // wait for listen IRP. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); + AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); + + // + // Cancel the IRP. + // + + waitForListenIrp->IoStatus.Status = STATUS_CANCELLED; + waitForListenIrp->IoStatus.Information = 0; + + // + // Reset the cancel routine in the IRP. + // + + IoAcquireCancelSpinLock( &cancelIrql ); + IoSetCancelRoutine( waitForListenIrp, NULL ); + IoReleaseCancelSpinLock( cancelIrql ); + + IoCompleteRequest( waitForListenIrp, AfdPriorityBoost ); + + // + // Reacquire the AFD spin lock for the next pass through the + // loop. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql1 ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql2 ); + } + + // + // Free all queued (free, unaccepted, and returned) connections + // on the endpoint. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); + AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); + AfdFreeQueuedConnections( endpoint ); + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql1 ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql2 ); + endpoint->Common.VcListening.FailedConnectionAdds = 0; + } + + UPDATE_CONN( connection, 0 ); + + // + // 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 && connection != NULL + + && + + !IS_DGRAM_ENDPOINT(endpoint) + + && + + ( (endpoint->DisconnectMode & AFD_ABORTIVE_DISCONNECT) == 0) + + && + + ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) == 0 || + ( !endpoint->TdiBufferring && + connection->Common.NonBufferring.ReceiveBytesInTransport > 0 ) ) + + && + + !connection->AbortIndicated ) { + + ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ); + + if ( IS_DATA_ON_CONNECTION( connection ) + + || + + IS_EXPEDITED_DATA_ON_CONNECTION( connection ) + + || + + processExitTime.QuadPart != 0 + + || + + endpoint->OutstandingIrpCount != 0 + + || + + ( !endpoint->TdiBufferring && + (!IsListEmpty( &connection->VcReceiveIrpListHead ) || + !IsListEmpty( &connection->VcSendIrpListHead )) ) + + ) { + +#if DBG + if ( IS_DATA_ON_CONNECTION( connection ) ) { + KdPrint(( "AfdCleanup: unrecv'd data on endp %lx, aborting. " + "%ld ind, %ld taken, %ld out\n", + endpoint, + connection->Common.Bufferring.ReceiveBytesIndicated, + connection->Common.Bufferring.ReceiveBytesTaken, + connection->Common.Bufferring.ReceiveBytesOutstanding )); + } + + if ( IS_EXPEDITED_DATA_ON_CONNECTION( connection ) ) { + KdPrint(( "AfdCleanup: unrecv'd exp data on endp %lx, aborting. " + "%ld ind, %ld taken, %ld out\n", + endpoint, + connection->Common.Bufferring.ReceiveExpeditedBytesIndicated, + connection->Common.Bufferring.ReceiveExpeditedBytesTaken, + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding )); + } + + if ( processExitTime.QuadPart != 0 ) { + KdPrint(( "AfdCleanup: process exiting w/o closesocket, " + "aborting endp %lx\n", endpoint )); + } + + if ( endpoint->OutstandingIrpCount != 0 ) { + KdPrint(( "AfdCleanup: 3 IRPs outstanding on endpoint %lx, " + "aborting.\n", endpoint )); + } +#endif + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); + AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); + + (VOID)AfdBeginAbort( connection ); + + } else { + + endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_RECEIVE; + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); + AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); + + (VOID)AfdBeginDisconnect( endpoint, NULL, NULL ); + } + + } else { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); + AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); + } + + // + // 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 ( endpoint->State != AfdEndpointStateOpen && + endpoint->Type == AfdBlockTypeDatagram ) { + + // + // Reset the counts of datagrams bufferred on the endpoint. + // This prevents anyone from thinking that there is bufferred + // data on the endpoint. + // + + endpoint->BufferredDatagramCount = 0; + endpoint->BufferredDatagramBytes = 0; + + // + // Cancel all receive datagram and peek datagram IRPs on the + // endpoint. + // + + AfdCompleteIrpList( + &endpoint->ReceiveDatagramIrpListHead, + &endpoint->SpinLock, + STATUS_CANCELLED, + AfdCleanupReceiveDatagramIrp + ); + + AfdCompleteIrpList( + &endpoint->PeekDatagramIrpListHead, + &endpoint->SpinLock, + STATUS_CANCELLED, + AfdCleanupReceiveDatagramIrp + ); + } + + // + // If this is a datagram endpoint, return the process quota which we + // charged when the endpoint was created. + // + + if ( endpoint->Type == AfdBlockTypeDatagram ) { + + PsReturnPoolQuota( + endpoint->OwningProcess, + NonPagedPool, + endpoint->Common.Datagram.MaxBufferredSendBytes + + endpoint->Common.Datagram.MaxBufferredReceiveBytes + ); + AfdRecordQuotaHistory( + endpoint->OwningProcess, + -(LONG)(endpoint->Common.Datagram.MaxBufferredSendBytes + + endpoint->Common.Datagram.MaxBufferredReceiveBytes), + "Cleanup dgrm", + endpoint + ); + AfdRecordPoolQuotaReturned( + endpoint->Common.Datagram.MaxBufferredSendBytes + + endpoint->Common.Datagram.MaxBufferredReceiveBytes + ); + } + + // + // If this is a connected VC endpoint on a nonbufferring TDI provider, + // cancel all outstanding send and receive IRPs. + // + + if ( connection != NULL ) { + + if ( !endpoint->TdiBufferring ) { + + AfdCompleteIrpList( + &connection->VcReceiveIrpListHead, + &endpoint->SpinLock, + STATUS_CANCELLED, + NULL + ); + + AfdCompleteIrpList( + &connection->VcSendIrpListHead, + &endpoint->SpinLock, + STATUS_CANCELLED, + NULL + ); + } + + // + // 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, &oldIrql2 ); + connection->CleanupBegun = TRUE; + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); + + // + // Attempt to remove the connected reference. + // + + AfdDeleteConnectedReference( connection, FALSE ); + } + + // + // If there is a transmit IRP on the endpoint, cancel it. + // + + IoAcquireCancelSpinLock( &cancelIrql ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql2 ); + + if ( endpoint->TransmitIrp != NULL ) { + endpoint->TransmitIrp->CancelIrql = cancelIrql; + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); + AfdCancelTransmit( NULL, endpoint->TransmitIrp ); + } else { + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); + IoReleaseCancelSpinLock( cancelIrql ); + } + + // + // Remember the new state of the endpoint. + // + + //endpoint->State = AfdEndpointStateCleanup; + + // + // Reset relevent 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->State == AfdEndpointStateListening ) { + 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) ); + } + + } + + InterlockedIncrement( + &AfdEndpointsCleanedUp + ); + + return STATUS_SUCCESS; + +} // AfdCleanup + + +NTSTATUS +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; + + PAGED_CODE( ); + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + IF_DEBUG(OPEN_CLOSE) { + KdPrint(( "AfdClose: closing file object %lx, endpoint %lx\n", + IrpSp->FileObject, endpoint )); + } + + AfdCloseEndpoint( endpoint ); + + return STATUS_SUCCESS; + +} // AfdClose |