/* * PROJECT: ReactOS Kernel * LICENSE: GPL - See COPYING in the top level directory * FILE: ntoskrnl/lpc/close.c * PURPOSE: Local Procedure Call: Rundown, Cleanup, Deletion * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) */ /* INCLUDES ******************************************************************/ #include #define NDEBUG #include /* PRIVATE FUNCTIONS *********************************************************/ VOID NTAPI LpcExitThread(IN PETHREAD Thread) { PLPCP_MESSAGE Message; ASSERT(Thread == PsGetCurrentThread()); /* Acquire the lock */ KeAcquireGuardedMutex(&LpcpLock); /* Make sure that the Reply Chain is empty */ if (!IsListEmpty(&Thread->LpcReplyChain)) { /* It's not, remove the entry */ RemoveEntryList(&Thread->LpcReplyChain); } /* Set the thread in exit mode */ Thread->LpcExitThreadCalled = TRUE; Thread->LpcReplyMessageId = 0; /* Check if there's a reply message */ Message = LpcpGetMessageFromThread(Thread); if (Message) { /* FIXME: TODO */ ASSERT(FALSE); } /* Release the lock */ KeReleaseGuardedMutex(&LpcpLock); } VOID NTAPI LpcpFreeToPortZone(IN PLPCP_MESSAGE Message, IN ULONG LockFlags) { PLPCP_CONNECTION_MESSAGE ConnectMessage; PLPCP_PORT_OBJECT ClientPort = NULL; PETHREAD Thread = NULL; BOOLEAN LockHeld = (LockFlags & LPCP_LOCK_HELD); BOOLEAN ReleaseLock = (LockFlags & LPCP_LOCK_RELEASE); PAGED_CODE(); LPCTRACE(LPC_CLOSE_DEBUG, "Message: %p. LockFlags: %lx\n", Message, LockFlags); /* Acquire the lock if not already */ if (!LockHeld) KeAcquireGuardedMutex(&LpcpLock); /* Check if the queue list is empty */ if (!IsListEmpty(&Message->Entry)) { /* Remove and re-initialize */ RemoveEntryList(&Message->Entry); InitializeListHead(&Message->Entry); } /* Check if we've already replied */ if (Message->RepliedToThread) { /* Set thread to dereference and clean up */ Thread = Message->RepliedToThread; Message->RepliedToThread = NULL; } /* Check if this is a connection request */ if (Message->Request.u2.s2.Type == LPC_CONNECTION_REQUEST) { /* Get the connection message */ ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1); /* Clear the client port */ ClientPort = ConnectMessage->ClientPort; if (ClientPort) ConnectMessage->ClientPort = NULL; } /* Release the lock */ KeReleaseGuardedMutex(&LpcpLock); /* Check if we had anything to dereference */ if (Thread) ObDereferenceObject(Thread); if (ClientPort) ObDereferenceObject(ClientPort); /* Free the entry */ ExFreeToPagedLookasideList(&LpcpMessagesLookaside, Message); /* Reacquire the lock if needed */ if ((LockHeld) && !(ReleaseLock)) KeAcquireGuardedMutex(&LpcpLock); } VOID NTAPI LpcpDestroyPortQueue(IN PLPCP_PORT_OBJECT Port, IN BOOLEAN Destroy) { PLIST_ENTRY ListHead, NextEntry; PETHREAD Thread; PLPCP_MESSAGE Message; PLPCP_PORT_OBJECT ConnectionPort = NULL; PLPCP_CONNECTION_MESSAGE ConnectMessage; PLPCP_NONPAGED_PORT_QUEUE MessageQueue; PAGED_CODE(); LPCTRACE(LPC_CLOSE_DEBUG, "Port: %p. Flags: %lx\n", Port, Port->Flags); /* Hold the lock */ KeAcquireGuardedMutex(&LpcpLock); /* Check if we have a connected port */ if (((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_UNCONNECTED_PORT) && (Port->ConnectedPort)) { /* Disconnect it */ Port->ConnectedPort->ConnectedPort = NULL; ConnectionPort = Port->ConnectedPort->ConnectionPort; if (ConnectionPort) { /* Clear connection port */ Port->ConnectedPort->ConnectionPort = NULL; } } /* Check if this is a connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CONNECTION_PORT) { /* Delete the name */ Port->Flags |= LPCP_NAME_DELETED; } /* Walk all the threads waiting and signal them */ ListHead = &Port->LpcReplyChainHead; NextEntry = ListHead->Flink; while ((NextEntry) && (NextEntry != ListHead)) { /* Get the Thread */ Thread = CONTAINING_RECORD(NextEntry, ETHREAD, LpcReplyChain); /* Make sure we're not in exit */ if (Thread->LpcExitThreadCalled) break; /* Move to the next entry */ NextEntry = NextEntry->Flink; /* Remove and reinitialize the List */ RemoveEntryList(&Thread->LpcReplyChain); InitializeListHead(&Thread->LpcReplyChain); /* Check if someone is waiting */ if (!KeReadStateSemaphore(&Thread->LpcReplySemaphore)) { /* Get the message */ Message = LpcpGetMessageFromThread(Thread); if (Message) { /* Check if it's a connection request */ if (Message->Request.u2.s2.Type == LPC_CONNECTION_REQUEST) { /* Get the connection message */ ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1); /* Check if it had a section */ if (ConnectMessage->SectionToMap) { /* Dereference it */ ObDereferenceObject(ConnectMessage->SectionToMap); } } /* Clear the reply message */ Thread->LpcReplyMessage = NULL; /* And remove the message from the port zone */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD); NextEntry = Port->LpcReplyChainHead.Flink; } /* Release the semaphore and reset message id count */ Thread->LpcReplyMessageId = 0; KeReleaseSemaphore(&Thread->LpcReplySemaphore, 0, 1, FALSE); } } /* Reinitialize the list head */ InitializeListHead(&Port->LpcReplyChainHead); /* Loop queued messages */ while ((Port->MsgQueue.ReceiveHead.Flink) && !(IsListEmpty(&Port->MsgQueue.ReceiveHead))) { /* Get the message */ Message = CONTAINING_RECORD(Port->MsgQueue.ReceiveHead.Flink, LPCP_MESSAGE, Entry); /* Free and reinitialize it's list head */ RemoveEntryList(&Message->Entry); InitializeListHead(&Message->Entry); /* Remove it from the port zone */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD); } /* Release the lock */ KeReleaseGuardedMutex(&LpcpLock); /* Dereference the connection port */ if (ConnectionPort) ObDereferenceObject(ConnectionPort); /* Check if we have to free the port entirely */ if (Destroy) { /* Check if the semaphore exists */ if (Port->MsgQueue.Semaphore) { /* Use the semaphore to find the port queue and free it */ MessageQueue = CONTAINING_RECORD(Port->MsgQueue.Semaphore, LPCP_NONPAGED_PORT_QUEUE, Semaphore); ExFreePoolWithTag(MessageQueue, 'troP'); } } } VOID NTAPI LpcpClosePort(IN PEPROCESS Process OPTIONAL, IN PVOID Object, IN ACCESS_MASK GrantedAccess, IN ULONG ProcessHandleCount, IN ULONG SystemHandleCount) { PLPCP_PORT_OBJECT Port = (PLPCP_PORT_OBJECT)Object; LPCTRACE(LPC_CLOSE_DEBUG, "Port: %p. Flags: %lx\n", Port, Port->Flags); /* Only Server-side Connection Ports need clean up*/ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CONNECTION_PORT) { /* Check the handle count */ switch (SystemHandleCount) { /* No handles left */ case 0: /* Destroy the port queue */ LpcpDestroyPortQueue(Port, TRUE); break; /* Last handle remaining */ case 1: /* Reset the queue only */ LpcpDestroyPortQueue(Port, FALSE); /* More handles remain, do nothing */ default: break; } } } VOID NTAPI LpcpFreePortClientSecurity(IN PLPCP_PORT_OBJECT Port) { /* Check if this is a client port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) { /* Check if security is static */ if (!(Port->Flags & LPCP_SECURITY_DYNAMIC)) { /* Check if we have a token */ if (Port->StaticSecurity.ClientToken) { /* Free security */ SeDeleteClientSecurity(&Port->StaticSecurity); } } } } VOID NTAPI LpcpDeletePort(IN PVOID ObjectBody) { LARGE_INTEGER Timeout; PETHREAD Thread; PLPCP_PORT_OBJECT Port = (PLPCP_PORT_OBJECT)ObjectBody; PLPCP_PORT_OBJECT ConnectionPort; PLPCP_MESSAGE Message; PLIST_ENTRY ListHead, NextEntry; HANDLE Pid; CLIENT_DIED_MSG ClientDiedMsg; PAGED_CODE(); LPCTRACE(LPC_CLOSE_DEBUG, "Port: %p. Flags: %lx\n", Port, Port->Flags); Timeout.QuadPart = -1000000; /* Check if this is a communication port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_COMMUNICATION_PORT) { /* Acquire the lock */ KeAcquireGuardedMutex(&LpcpLock); /* Get the thread */ Thread = Port->ClientThread; if (Thread) { /* Clear it */ Port->ClientThread = NULL; /* Release the lock and dereference */ KeReleaseGuardedMutex(&LpcpLock); ObDereferenceObject(Thread); } else { /* Release the lock */ KeReleaseGuardedMutex(&LpcpLock); } } /* Check if this is a client-side port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) { /* Setup the client died message */ ClientDiedMsg.h.u1.s1.TotalLength = sizeof(ClientDiedMsg); ClientDiedMsg.h.u1.s1.DataLength = sizeof(ClientDiedMsg.CreateTime); ClientDiedMsg.h.u2.ZeroInit = 0; ClientDiedMsg.h.u2.s2.Type = LPC_PORT_CLOSED; ClientDiedMsg.CreateTime = PsGetCurrentProcess()->CreateTime; /* Send it */ for (;;) { /* Send the message */ if (LpcRequestPort(Port, &ClientDiedMsg.h) != STATUS_NO_MEMORY) break; /* Wait until trying again */ KeDelayExecutionThread(KernelMode, FALSE, &Timeout); } } /* Destroy the port queue */ LpcpDestroyPortQueue(Port, TRUE); /* Check if we had views */ if ((Port->ClientSectionBase) || (Port->ServerSectionBase)) { /* Check if we had a client view */ if (Port->ClientSectionBase) { /* Unmap it */ MmUnmapViewOfSection(Port->MappingProcess, Port->ClientSectionBase); } /* Check for a server view */ if (Port->ServerSectionBase) { /* Unmap it */ MmUnmapViewOfSection(Port->MappingProcess, Port->ServerSectionBase); } /* Dereference the mapping process */ ObDereferenceObject(Port->MappingProcess); Port->MappingProcess = NULL; } /* Acquire the lock */ KeAcquireGuardedMutex(&LpcpLock); /* Get the connection port */ ConnectionPort = Port->ConnectionPort; if (ConnectionPort) { /* Get the PID */ Pid = PsGetCurrentProcessId(); /* Loop the data lists */ ListHead = &ConnectionPort->LpcDataInfoChainHead; NextEntry = ListHead->Flink; while (NextEntry != ListHead) { /* Get the message */ Message = CONTAINING_RECORD(NextEntry, LPCP_MESSAGE, Entry); NextEntry = NextEntry->Flink; /* Check if this is the connection port */ if (Port == ConnectionPort) { /* Free queued messages */ RemoveEntryList(&Message->Entry); InitializeListHead(&Message->Entry); LpcpFreeToPortZone(Message, LPCP_LOCK_HELD); /* Restart at the head */ NextEntry = ListHead->Flink; } else if ((Message->Request.ClientId.UniqueProcess == Pid) && ((Message->SenderPort == Port) || (Message->SenderPort == Port->ConnectedPort) || (Message->SenderPort == ConnectionPort))) { /* Remove it */ RemoveEntryList(&Message->Entry); InitializeListHead(&Message->Entry); LpcpFreeToPortZone(Message, LPCP_LOCK_HELD); /* Restart at the head */ NextEntry = ListHead->Flink; } } /* Release the lock */ KeReleaseGuardedMutex(&LpcpLock); /* Dereference the object unless it's the same port */ if (ConnectionPort != Port) ObDereferenceObject(ConnectionPort); /* Check if this is a connection port with a server process */ if (((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CONNECTION_PORT) && (ConnectionPort->ServerProcess)) { /* Dereference the server process */ ObDereferenceObject(ConnectionPort->ServerProcess); ConnectionPort->ServerProcess = NULL; } } else { /* Release the lock */ KeReleaseGuardedMutex(&LpcpLock); } /* Free client security */ LpcpFreePortClientSecurity(Port); LPCTRACE(LPC_CLOSE_DEBUG, "Port: %p deleted\n", Port); } /* EOF */