mirror of
https://github.com/reactos/reactos.git
synced 2024-10-31 20:02:55 +00:00
463 lines
14 KiB
C
463 lines
14 KiB
C
/*
|
|
* 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 <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* 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 */
|