diff options
Diffstat (limited to 'private/ntos/tdi/st/connect.c')
-rw-r--r-- | private/ntos/tdi/st/connect.c | 1137 |
1 files changed, 1137 insertions, 0 deletions
diff --git a/private/ntos/tdi/st/connect.c b/private/ntos/tdi/st/connect.c new file mode 100644 index 000000000..8cb05b03b --- /dev/null +++ b/private/ntos/tdi/st/connect.c @@ -0,0 +1,1137 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + connect.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiAccept + o TdiListen + o TdiConnect + o TdiDisconnect + o TdiAssociateAddress + o TdiDisassociateAddress + o OpenConnection + o CloseConnection + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + +NTSTATUS +StTdiAccept( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiAccept request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_CONNECTION connection; + PIO_STACK_LOCATION irpSp; + KIRQL oldirql; + NTSTATUS status; + + // + // Get the connection this is associated with; if there is none, get out. + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // This adds a connection reference if successful. + // + + status = StVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + // + // just set the connection flags to allow reads and writes to proceed. + // + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + + // + // Turn off the stopping flag for this connection. + // + + connection->Flags &= ~CONNECTION_FLAGS_STOPPING; + connection->Status = STATUS_PENDING; + + connection->Flags2 |= CONNECTION_FLAGS2_ACCEPTED; + + + if (connection->AddressFile->ConnectIndicationInProgress) { + connection->Flags2 |= CONNECTION_FLAGS2_INDICATING; + } + + if ((connection->Flags2 & CONNECTION_FLAGS2_WAIT_ACCEPT) != 0) { + + // + // We previously completed a listen, now the user is + // coming back with an accept, Set this flag to allow + // the connection to proceed. + // + + connection->Flags |= CONNECTION_FLAGS_READY; + + INCREMENT_COUNTER (connection->Provider, OpenConnections); + + // + // Set this flag to enable disconnect indications; once + // the client has accepted he expects those. + // + + connection->Flags2 &= ~CONNECTION_FLAGS2_WAIT_ACCEPT; + connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + StReferenceConnection("Pended listen completed", connection); + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + + } else { + + // + // This accept is being called at some point before + // the link is up; directly from the connection handler + // or at some point slightly later. + // + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + + } + + StDereferenceConnection ("Temp TdiAccept", connection); + + return STATUS_SUCCESS; + +} /* StTdiAccept */ + + +NTSTATUS +StTdiAssociateAddress( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the association of the connection and the address for + the user. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PFILE_OBJECT fileObject; + PTP_ADDRESS_FILE addressFile; + PTP_ADDRESS oldAddress; + PTP_CONNECTION connection; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL_ASSOCIATE parameters; + PDEVICE_CONTEXT deviceContext; + + KIRQL oldirql, oldirql2; + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + // + // verify that the operation is taking place on a connection. At the same + // time we do this, we reference the connection. This ensures it does not + // get removed out from under us. Note also that we do the connection + // lookup within a try/except clause, thus protecting ourselves against + // really bogus handles + // + + connection = irpSp->FileObject->FsContext; + status = StVerifyConnectionObject (connection); + if (!NT_SUCCESS (status)) { + return status; + } + + + // + // Make sure this connection is ready to be associated. + // + + oldAddress = (PTP_ADDRESS)NULL; + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql2); + + if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) && + ((connection->Flags2 & CONNECTION_FLAGS2_DISASSOCIATED) == 0)) { + + // + // The connection is already associated with + // an active connection...bad! + // + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql2); + StDereferenceConnection ("Temp Ref Associate", connection); + + return STATUS_INVALID_CONNECTION; + + } else { + + // + // See if there is an old association hanging around... + // this happens if the connection has been disassociated, + // but not closed. + // + + if (connection->Flags2 & CONNECTION_FLAGS2_DISASSOCIATED) { + + // + // Save this; since it is non-null this address + // will be dereferenced after the connection + // spinlock is released. + // + + oldAddress = connection->AddressFile->Address; + + // + // Remove the old association. + // + + connection->Flags2 &= ~CONNECTION_FLAGS2_ASSOCIATED; + RemoveEntryList (&connection->AddressList); + RemoveEntryList (&connection->AddressFileList); + InitializeListHead (&connection->AddressList); + InitializeListHead (&connection->AddressFileList); + connection->AddressFile = NULL; + + } + + } + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql2); + + // + // If we removed an old association, dereference the + // address. + // + + if (oldAddress != (PTP_ADDRESS)NULL) { + + StDereferenceAddress("Removed old association", oldAddress); + + } + + + deviceContext = connection->Provider; + + parameters = (PTDI_REQUEST_KERNEL_ASSOCIATE)&irpSp->Parameters; + + // + // get a pointer to the address File Object, which points us to the + // transport's address object, which is where we want to put the + // connection. + // + + status = ObReferenceObjectByHandle ( + parameters->AddressHandle, + 0L, + 0, + KernelMode, + (PVOID *) &fileObject, + NULL); + + if (NT_SUCCESS(status)) { + + // + // we might have one of our address objects; verify that. + // + + addressFile = fileObject->FsContext; + + if (NT_SUCCESS (StVerifyAddressObject (addressFile))) { + + // + // have an address and connection object. Add the connection to the + // address object database. Also add the connection to the address + // file object db (used primarily for cleaning up). Reference the + // address to account for one more reason for it staying open. + // + + ACQUIRE_SPIN_LOCK (&addressFile->Address->SpinLock, &oldirql); + if ((addressFile->Address->Flags & ADDRESS_FLAGS_STOPPING) == 0) { + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql2); + + if ((connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0) { + + StReferenceAddress ( + "Connection associated", + addressFile->Address); + + InsertTailList ( + &addressFile->Address->ConnectionDatabase, + &connection->AddressList); + + InsertTailList ( + &addressFile->ConnectionDatabase, + &connection->AddressFileList); + + connection->AddressFile = addressFile; + connection->Flags2 |= CONNECTION_FLAGS2_ASSOCIATED; + connection->Flags2 &= ~CONNECTION_FLAGS2_DISASSOCIATED; + + if (addressFile->ConnectIndicationInProgress) { + connection->Flags2 |= CONNECTION_FLAGS2_INDICATING; + } + + status = STATUS_SUCCESS; + + } else { + + // + // The connection is closing, stop the + // association. + // + + status = STATUS_INVALID_CONNECTION; + + } + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql2); + + } else { + + status = STATUS_INVALID_HANDLE; + } + + RELEASE_SPIN_LOCK (&addressFile->Address->SpinLock, oldirql); + + StDereferenceAddress ("Temp associate", addressFile->Address); + + } else { + + status = STATUS_INVALID_HANDLE; + } + + // + // Note that we don't keep a reference to this file object around. + // That's because the IO subsystem manages the object for us; we simply + // want to keep the association. We only use this association when the + // IO subsystem has asked us to close one of the file object, and then + // we simply remove the association. + // + + ObDereferenceObject (fileObject); + + } else { + status = STATUS_INVALID_HANDLE; + } + + StDereferenceConnection ("Temp Ref Associate", connection); + + return status; + +} /* TdiAssociateAddress */ + + +NTSTATUS +StTdiDisassociateAddress( + IN PIRP Irp + ) +/*++ + +Routine Description: + + This routine performs the disassociation of the connection and the address + for the user. If the connection has not been stopped, it will be stopped + here. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + KIRQL oldirql; + PIO_STACK_LOCATION irpSp; + PTP_CONNECTION connection; + NTSTATUS status; + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference. + // + + status = StVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) == 0) { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + StStopConnection (connection, STATUS_LOCAL_DISCONNECT); + + } else { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + } + + // + // and now we disassociate the address. This only removes + // the appropriate reference for the connection, the + // actually disassociation will be done later. + // + // The DISASSOCIATED flag is used to make sure that + // only one person removes this reference. + // + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) && + ((connection->Flags2 & CONNECTION_FLAGS2_DISASSOCIATED) == 0)) { + connection->Flags2 |= CONNECTION_FLAGS2_DISASSOCIATED; + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + } else { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + } + + StDereferenceConnection ("Temp use in Associate", connection); + + return STATUS_SUCCESS; + +} /* TdiDisassociateAddress */ + + +NTSTATUS +StTdiConnect( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiConnect request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTP_CONNECTION connection; + PSTRING GeneralBroadcastSourceRoute = NULL; // BUGBUG: define this later. + LARGE_INTEGER timeout = {0,0}; + KIRQL oldirql, cancelirql; + PTP_REQUEST tpRequest; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL parameters; + PTA_NETBIOS_ADDRESS RemoteAddress; + ULONG RemoteAddressLength; + + // + // is the file object a connection? + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference. + // + + status = StVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + parameters = (PTDI_REQUEST_KERNEL)(&irpSp->Parameters); + + // + // fix up the timeout if required; no connect request should take more + // than 15 seconds if there is someone out there. We'll assume that's + // what the user wanted if they specify -1 as the timer length. + // + + if (parameters->RequestSpecific != NULL) { + if ((((PLARGE_INTEGER)(parameters->RequestSpecific))->LowPart == -1) && + (((PLARGE_INTEGER)(parameters->RequestSpecific))->HighPart == -1)) { + + timeout.LowPart = (ULONG)(-TDI_TIMEOUT_CONNECT * 10000000L); // n * 10 ** 7 => 100ns units + if (timeout.LowPart != 0) { + timeout.HighPart = -1L; + } else { + timeout.HighPart = 0; + } + + } else { + + timeout.LowPart = ((PLARGE_INTEGER)(parameters->RequestSpecific))->LowPart; + timeout.HighPart = ((PLARGE_INTEGER)(parameters->RequestSpecific))->HighPart; + } + } + + // + // Check that the remote is a Netbios address. + // + + RemoteAddress = (PTA_NETBIOS_ADDRESS) + (parameters->RequestConnectionInformation->RemoteAddress); + + if (RemoteAddress->Address[0].AddressType != TDI_ADDRESS_TYPE_NETBIOS) { + + StDereferenceConnection ("Not Netbios", connection); + return STATUS_BAD_NETWORK_PATH; // don't even try to find it. + + } + + // + // copy the called address someplace we can use it. + // + + RemoteAddressLength = parameters->RequestConnectionInformation->RemoteAddressLength; + + if (RemoteAddressLength > sizeof(TA_NETBIOS_ADDRESS)) { + RemoteAddressLength = sizeof(TA_NETBIOS_ADDRESS); + } + + RtlCopyMemory( + connection->CalledAddress.NetbiosName, + RemoteAddress->Address[0].Address[0].NetbiosName, + 16); + + // + // We need a request object to keep track of this TDI request. + // Attach this request to the new connection object. + // + + status = StCreateRequest ( + Irp, // IRP for this request. + connection, // context. + REQUEST_FLAGS_CONNECTION, // partial flags. + NULL, + 0, + timeout, + &tpRequest); + + if (!NT_SUCCESS (status)) { // couldn't make the request. + StDereferenceConnection ("Throw away", connection); + return status; // return with failure. + } else { + + // Reference the connection since StDestroyRequest derefs it. + + StReferenceConnection("For connect request", connection); + + tpRequest->Owner = ConnectionType; + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_SPIN_LOCK (&connection->SpinLock,&oldirql); + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + IoReleaseCancelSpinLock (cancelirql); + StCompleteRequest ( + tpRequest, + connection->Status, + 0); + StDereferenceConnection("Temporary Use 1", connection); + return STATUS_PENDING; + } else { + InsertTailList (&connection->InProgressRequest,&tpRequest->Linkage); + + connection->Flags |= CONNECTION_FLAGS_CONNECTOR; // we're the initiator. + + connection->Flags &= ~CONNECTION_FLAGS_STOPPING; + connection->Status = STATUS_PENDING; + + connection->Flags2 &= ~CONNECTION_FLAGS2_INDICATING; + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + + // + // Check if the IRP has been cancelled. + // + + if (Irp->Cancel) { + Irp->CancelIrql = cancelirql; + StCancelConnection((PDEVICE_OBJECT)(connection->Provider), Irp); + StDereferenceConnection ("IRP cancelled", connection); // release lookup hold. + return STATUS_PENDING; + } + + Irp->CancelRoutine = StCancelConnection; + IoReleaseCancelSpinLock(cancelirql); + + } + } + + status = StSendConnect ( + connection); + + if (!NT_SUCCESS(status)) { // can't send the name request + StStopConnection (connection, status); + StDereferenceConnection("Temporary Use 2", connection); + + // + // Note that this return status isn't really a lie. We are waiting + // for the connection to run down. + // + + return STATUS_PENDING; + } + + + StDereferenceConnection("Temporary Use 3", connection); + + return STATUS_PENDING; // things are started. + +} /* TdiConnect */ + + +NTSTATUS +StTdiDisconnect( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiDisconnect request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_CONNECTION connection; + LARGE_INTEGER timeout; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL parameters; + KIRQL oldirql; + NTSTATUS status; + + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference. + // + + status = StVerifyConnectionObject (connection); + if (!NT_SUCCESS (status)) { + return status; + } + + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + + // + // if the connection is currently stopping, there's no reason to blow + // it away... + // + + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + StDereferenceConnection ("Ignoring disconnect", connection); // release our lookup reference. + return connection->Status; + + } else { + connection->Flags2 &= ~ (CONNECTION_FLAGS2_ACCEPTED | + CONNECTION_FLAGS2_PRE_ACCEPT | + CONNECTION_FLAGS2_WAIT_ACCEPT); + connection->Flags2 |= CONNECTION_FLAGS2_DISCONNECT; + + // + // Set this flag so the disconnect IRP is completed. + // + // BUGBUG: If the connection goes down before we can + // call StStopConnection with STATUS_LOCAL_DISCONNECT, + // the disconnect IRP won't get completed. + // + + connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + connection->DisconnectIrp = Irp; + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + } + + // + // fix up the timeout if required; no disconnect request should take very + // long. However, the user can cause the timeout to not happen if they + // desire that. + // + + parameters = (PTDI_REQUEST_KERNEL)(&irpSp->Parameters); + + // + // fix up the timeout if required; no disconnect request should take more + // than 15 seconds. We'll assume that's what the user wanted if they + // specify -1 as the timer. + // + + if (parameters->RequestSpecific != NULL) { + if ((((PLARGE_INTEGER)(parameters->RequestSpecific))->LowPart == -1) && + (((PLARGE_INTEGER)(parameters->RequestSpecific))->HighPart == -1)) { + + timeout.LowPart = (ULONG)(-TDI_TIMEOUT_DISCONNECT * 10000000L); // n * 10 ** 7 => 100ns units + if (timeout.LowPart != 0) { + timeout.HighPart = -1L; + } else { + timeout.HighPart = 0; + } + + } else { + timeout.LowPart = ((PLARGE_INTEGER)(parameters->RequestSpecific))->LowPart; + timeout.HighPart = ((PLARGE_INTEGER)(parameters->RequestSpecific))->HighPart; + } + } + + // + // Now the reason for the disconnect + // + + if ((ULONG)(parameters->RequestFlags) & (ULONG)TDI_DISCONNECT_RELEASE) { + connection->Flags |= CONNECTION_FLAGS_DESTROY; + } else if ((ULONG)(parameters->RequestFlags) & (ULONG)TDI_DISCONNECT_ABORT) { + connection->Flags |= CONNECTION_FLAGS_ABORT; + } else if ((ULONG)(parameters->RequestFlags) & (ULONG)TDI_DISCONNECT_WAIT) { + connection->Flags |= CONNECTION_FLAGS_ORDREL; + } + + // + // This will get passed to IoCompleteRequest during TdiDestroyConnection + // + + StStopConnection (connection, STATUS_LOCAL_DISCONNECT); // starts the abort sequence. + StDereferenceConnection ("Disconnecting", connection); // release our lookup reference. + + // + // This request will be completed by TdiDestroyConnection once + // the connection reference count drops to 0. + // + + return STATUS_PENDING; +} /* TdiDisconnect */ + + +NTSTATUS +StTdiListen( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiListen request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTP_CONNECTION connection; + LARGE_INTEGER timeout = {0,0}; + KIRQL oldirql, cancelirql; + PTP_REQUEST tpRequest; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL_LISTEN parameters; + + // + // validate this connection + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference. + // + + status = StVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + parameters = (PTDI_REQUEST_KERNEL_LISTEN)&irpSp->Parameters; + + // + // We need a request object to keep track of this TDI request. + // Attach this request to the new connection object. + // + + status = StCreateRequest ( + Irp, // IRP for this request. + connection, // context. + REQUEST_FLAGS_CONNECTION, // partial flags. + NULL, + 0, + timeout, // timeout value (can be 0). + &tpRequest); + + + if (!NT_SUCCESS (status)) { // couldn't make the request. + + StDereferenceConnection ("For create", connection); + return status; // return with failure. + } + + // Reference the connection since StDestroyRequest derefs it. + + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + tpRequest->Owner = ConnectionType; + + StReferenceConnection("For listen request", connection); + + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { + + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + IoReleaseCancelSpinLock(cancelirql); + + StCompleteRequest ( + tpRequest, + connection->Status, + 0); + StDereferenceConnection("Temp create", connection); + return STATUS_PENDING; + + } else { + + InsertTailList (&connection->InProgressRequest,&tpRequest->Linkage); + connection->Flags |= CONNECTION_FLAGS_LISTENER | // we're the passive one. + CONNECTION_FLAGS_WAIT_LISTEN; // waiting for a connect + connection->Flags2 &= ~CONNECTION_FLAGS2_INDICATING; + connection->Flags &= ~CONNECTION_FLAGS_STOPPING; + connection->Status = STATUS_PENDING; + + // + // If TDI_QUERY_ACCEPT is not set, then we set PRE_ACCEPT to + // indicate that when the listen completes we do not have to + // wait for a TDI_ACCEPT to continue. + // + + if ((parameters->RequestFlags & TDI_QUERY_ACCEPT) == 0) { + connection->Flags2 |= CONNECTION_FLAGS2_PRE_ACCEPT; + } + + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + + // + // Check if the IRP has been cancelled. + // + + if (Irp->Cancel) { + Irp->CancelIrql = cancelirql; + StCancelConnection((PDEVICE_OBJECT)(connection->Provider), Irp); + StDereferenceConnection ("IRP cancelled", connection); // release lookup hold. + return STATUS_PENDING; + } + + Irp->CancelRoutine = StCancelConnection; + IoReleaseCancelSpinLock(cancelirql); + + } + + // + // Wait for an incoming NAME_QUERY frame. The remainder of the + // connectionless protocol to set up a connection is processed + // in the NAME_QUERY frame handler. + // + + StDereferenceConnection("Temp create", connection); + + return STATUS_PENDING; // things are started. +} /* TdiListen */ + + +NTSTATUS +StOpenConnection ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine is called to open a connection. Note that the connection that + is open is of little use until associated with an address; until then, + the only thing that can be done with it is close it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + + IrpSp - Pointer to current IRP stack frame. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + NTSTATUS status; + PTP_CONNECTION connection; + PFILE_FULL_EA_INFORMATION ea; + + UNREFERENCED_PARAMETER (Irp); + + DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + + // + // First, try to make a connection object to represent this pending + // connection. Then fill in the relevant fields. + // In addition to the creation, if successful StCreateConnection + // will create a second reference which is removed once the request + // references the connection, or if the function exits before that. + + status = StCreateConnection (DeviceContext, &connection); + if (!NT_SUCCESS (status)) { + return status; // sorry, we couldn't make one. + } + + // + // set the connection context so we can connect the user to this data + // structure + // + + ea = (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + RtlCopyMemory ( + &connection->Context, + &ea->EaName[ea->EaNameLength+1], + sizeof (PVOID)); + + // + // let file object point at connection and connection at file object + // + + IrpSp->FileObject->FsContext = (PVOID)connection; + IrpSp->FileObject->FsContext2 = (PVOID)TDI_CONNECTION_FILE; + connection->FileObject = IrpSp->FileObject; + + return status; + +} /* StOpenConnection */ + + +NTSTATUS +StCloseConnection ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine is called to close a connection. There may be actions in + progress on this connection, so we note the closing IRP, mark the + connection as closing, and complete it somewhere down the road (when all + references have been removed). + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + + IrpSp - Pointer to current IRP stack frame. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS status; + KIRQL oldirql; + PTP_CONNECTION connection; + + UNREFERENCED_PARAMETER (DeviceObject); + UNREFERENCED_PARAMETER (Irp); + + // + // is the file object a connection? + // + + connection = IrpSp->FileObject->FsContext; + + + // + // We duplicate the code from VerifyConnectionObject, + // although we don't actually call that since it does + // a reference, which we don't want (to avoid bouncing + // the reference count up from 0 if this is a dead + // link). + // + + try { + + if ((connection->Size == sizeof (TP_CONNECTION)) && + (connection->Type == ST_CONNECTION_SIGNATURE)) { + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + + if ((connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0) { + + status = STATUS_SUCCESS; + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + return GetExceptionCode(); + } + + if (!NT_SUCCESS (status)) { + return status; + } + + // + // We recognize it; is it closing already? + // + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + + if ((connection->Flags2 & CONNECTION_FLAGS2_CLOSING) != 0) { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + StDereferenceConnection("Temp Close", connection); + return STATUS_INVALID_CONNECTION; + } + + connection->Flags2 |= CONNECTION_FLAGS2_CLOSING; + + // + // if there is activity on the connection, tear it down. + // + + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) == 0) { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + StStopConnection (connection, STATUS_LOCAL_DISCONNECT); + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + } + + // + // If the connection is still associated, disassociate it. + // + + if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) && + ((connection->Flags2 & CONNECTION_FLAGS2_DISASSOCIATED) == 0)) { + connection->Flags2 |= CONNECTION_FLAGS2_DISASSOCIATED; + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + } else { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + } + + + // + // Save this to complete the IRP later. + // + + connection->CloseIrp = Irp; + + // + // make it impossible to use this connection from the file object + // + + IrpSp->FileObject->FsContext = NULL; + IrpSp->FileObject->FsContext2 = NULL; + connection->FileObject = NULL; + + // + // dereference for the creation. Note that this dereference + // here won't have any effect until the regular reference count + // hits zero. + // + + StDereferenceConnectionSpecial (" Closing Connection", connection); + + return STATUS_PENDING; + +} /* StCloseConnection */ + |