|
|
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
lpcqueue.c
Abstract:
Local Inter-Process Communication (LPC) queue support routines.
Author:
Steve Wood (stevewo) 15-May-1989
Revision History:
--*/
#include "lpcp.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,LpcpInitializePortZone)
#pragma alloc_text(PAGE,LpcpInitializePortQueue)
#pragma alloc_text(PAGE,LpcpDestroyPortQueue)
#pragma alloc_text(PAGE,LpcpExtendPortZone)
#pragma alloc_text(PAGE,LpcpFreeToPortZone)
#pragma alloc_text(PAGE,LpcpSaveDataInfoMessage)
#pragma alloc_text(PAGE,LpcpFreeDataInfoMessage)
#pragma alloc_text(PAGE,LpcpFindDataInfoMessage)
#pragma alloc_text(PAGE,LpcDisconnectPort)
#endif
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg("PAGEDATA")
#endif // ALLOC_DATA_PRAGMA
ULONG LpcpTotalNumberOfMessages = 0; ULONG LpcpMaxMessageSize = 0; PAGED_LOOKASIDE_LIST LpcpMessagesLookaside;
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg()
#endif // ALLOC_DATA_PRAGMA
NTSTATUS LpcpInitializePortQueue ( IN PLPCP_PORT_OBJECT Port )
/*++
Routine Description:
This routine is used to initialize the message queue for a port object.
Arguments:
Port - Supplies the port object being initialized
Return Value:
NTSTATUS - An appropriate status value
--*/
{ PLPCP_NONPAGED_PORT_QUEUE NonPagedPortQueue;
PAGED_CODE();
//
// Allocate space for the port queue
//
NonPagedPortQueue = ExAllocatePoolWithTag( NonPagedPool, sizeof(LPCP_NONPAGED_PORT_QUEUE), 'troP' );
if (NonPagedPortQueue == NULL) {
return STATUS_INSUFFICIENT_RESOURCES; }
//
// Initialize the fields in the non paged port queue
//
KeInitializeSemaphore( &NonPagedPortQueue->Semaphore, 0, 0x7FFFFFFF );
NonPagedPortQueue->BackPointer = Port;
//
// Have the port msg queue point to the non nonpaged port queue
//
Port->MsgQueue.Semaphore = &NonPagedPortQueue->Semaphore;
//
// Initialize the port msg queue to be empty
//
InitializeListHead( &Port->MsgQueue.ReceiveHead );
//
// And return to our caller
//
return STATUS_SUCCESS; }
VOID LpcpDestroyPortQueue ( IN PLPCP_PORT_OBJECT Port, IN BOOLEAN CleanupAndDestroy )
/*++
Routine Description:
This routine is used to teardown the message queue of a port object. After running this message will either be empty (like it was just initialized) or completely gone (needs to be initialized)
Arguments:
Port - Supplies the port containing the message queue being modified
CleanupAndDestroy - Specifies if the message queue should be set back to the freshly initialized state (value of FALSE) or completely torn down (value of TRUE)
Return Value:
None.
--*/
{ PLIST_ENTRY Next, Head; PETHREAD ThreadWaitingForReply; PLPCP_MESSAGE Msg; PLPCP_PORT_OBJECT ConnectionPort = NULL;
PAGED_CODE();
//
// If this port is connected to another port, then disconnect it.
// Protect this with a lock in case the other side is going away
// at the same time.
//
LpcpAcquireLpcpLock();
if ( ((Port->Flags & PORT_TYPE) != UNCONNECTED_COMMUNICATION_PORT) && (Port->ConnectedPort != NULL) ) {
Port->ConnectedPort->ConnectedPort = NULL; //
// Disconnect the connection port
//
if (Port->ConnectedPort->ConnectionPort) {
ConnectionPort = Port->ConnectedPort->ConnectionPort;
Port->ConnectedPort->ConnectionPort = NULL; } }
//
// If connection port, then mark name as deleted
//
if ((Port->Flags & PORT_TYPE) == SERVER_CONNECTION_PORT) {
Port->Flags |= PORT_NAME_DELETED; }
//
// Walk list of threads waiting for a reply to a message sent to this
// port. Signal each thread's LpcReplySemaphore to wake them up. They
// will notice that there was no reply and return
// STATUS_PORT_DISCONNECTED
//
Head = &Port->LpcReplyChainHead; Next = Head->Flink;
while ((Next != NULL) && (Next != Head)) {
ThreadWaitingForReply = CONTAINING_RECORD( Next, ETHREAD, LpcReplyChain );
//
// If the thread is exiting, in the location of LpcReplyChain is stored the ExitTime
// We'll stop to search through the list.
if ( ThreadWaitingForReply->LpcExitThreadCalled ) { break; }
Next = Next->Flink;
RemoveEntryList( &ThreadWaitingForReply->LpcReplyChain );
InitializeListHead( &ThreadWaitingForReply->LpcReplyChain );
if (!KeReadStateSemaphore( &ThreadWaitingForReply->LpcReplySemaphore )) {
//
// Thread is waiting on a message. Signal the semaphore and free
// the message
//
Msg = LpcpGetThreadMessage(ThreadWaitingForReply);
if ( Msg ) {
//
// If the message is a connection request and has a section object
// attached, then dereference that section object
//
if ((Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) == LPC_CONNECTION_REQUEST) {
PLPCP_CONNECTION_MESSAGE ConnectMsg; ConnectMsg = (PLPCP_CONNECTION_MESSAGE)(Msg + 1);
if ( ConnectMsg->SectionToMap != NULL ) {
ObDereferenceObject( ConnectMsg->SectionToMap ); } }
ThreadWaitingForReply->LpcReplyMessage = NULL;
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED ); Next = Port->LpcReplyChainHead.Flink; // Lock has been dropped
}
ThreadWaitingForReply->LpcReplyMessageId = 0;
KeReleaseSemaphore( &ThreadWaitingForReply->LpcReplySemaphore, 0, 1L, FALSE ); } }
InitializeListHead( &Port->LpcReplyChainHead );
//
// Walk list of messages queued to this port. Remove each message from
// the list and free it.
//
while (Port->MsgQueue.ReceiveHead.Flink && !IsListEmpty (&Port->MsgQueue.ReceiveHead)) {
Msg = CONTAINING_RECORD( Port->MsgQueue.ReceiveHead.Flink, LPCP_MESSAGE, Entry );
RemoveEntryList (&Msg->Entry);
InitializeListHead( &Msg->Entry );
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED ); }
LpcpReleaseLpcpLock();
if ( ConnectionPort ) {
ObDereferenceObject( ConnectionPort ); }
//
// Check if the caller wants it all to go away
//
if ( CleanupAndDestroy ) {
//
// Free semaphore associated with the queue.
//
if (Port->MsgQueue.Semaphore != NULL) {
ExFreePool( CONTAINING_RECORD( Port->MsgQueue.Semaphore, LPCP_NONPAGED_PORT_QUEUE, Semaphore )); } }
//
// And return to our caller
//
return; }
NTSTATUS LpcDisconnectPort ( IN PVOID Port )
/*++
Routine Description:
This routine is used to disconnect an LPC port so no more messages can be sent and anybody waiting for a message is woken up with an error.
Arguments:
Port - Supplies the port to be disconnected
Return Value:
NTSTATUS - Status of operation
--*/ { LpcpDestroyPortQueue (Port, FALSE); return STATUS_SUCCESS; }
VOID LpcpInitializePortZone ( IN ULONG MaxEntrySize ) { LpcpMaxMessageSize = MaxEntrySize;
ExInitializePagedLookasideList( &LpcpMessagesLookaside, NULL, NULL, 0, MaxEntrySize, 'McpL', 32 ); }
VOID FASTCALL LpcpFreeToPortZone ( IN PLPCP_MESSAGE Msg, IN ULONG MutexFlags ) { PLPCP_CONNECTION_MESSAGE ConnectMsg; PETHREAD RepliedToThread = NULL; PLPCP_PORT_OBJECT ClientPort = NULL;
PAGED_CODE();
//
// Acquire the global lock if necessary
//
if ((MutexFlags & LPCP_MUTEX_OWNED) == 0) {
LpcpAcquireLpcpLock(); }
//
// A entry field connects the message to the message queue of the
// owning port object. If not already removed then remove this
// message
//
if (!IsListEmpty( &Msg->Entry )) { RemoveEntryList( &Msg->Entry ); InitializeListHead( &Msg->Entry ); }
//
// If the replied to thread is not null then we have a reference
// to the thread that we should now remove
//
if (Msg->RepliedToThread != NULL) { RepliedToThread = Msg->RepliedToThread; Msg->RepliedToThread = NULL; }
//
// If the msg was for a connection request then we know that
// right after the lpcp message is a connection message whose
// client port field might need to be dereferenced
//
if ((Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) == LPC_CONNECTION_REQUEST) {
ConnectMsg = (PLPCP_CONNECTION_MESSAGE)(Msg + 1);
if (ConnectMsg->ClientPort) {
//
// Capture a pointer to the client port then null it
// out so that no one else can use it, then release
// lpcp lock before we dereference the client port
//
ClientPort = ConnectMsg->ClientPort;
ConnectMsg->ClientPort = NULL; } }
LpcpReleaseLpcpLock();
if ( ClientPort ) { ObDereferenceObject( ClientPort ); }
if ( RepliedToThread ) {
ObDereferenceObject( RepliedToThread ); }
ExFreeToPagedLookasideList(&LpcpMessagesLookaside, Msg);
if ((MutexFlags & LPCP_MUTEX_OWNED) && ((MutexFlags & LPCP_MUTEX_RELEASE_ON_RETURN) == 0)) {
LpcpAcquireLpcpLock(); }
}
VOID LpcpSaveDataInfoMessage ( IN PLPCP_PORT_OBJECT Port, IN PLPCP_MESSAGE Msg, IN ULONG MutexFlags )
/*++
Routine Description:
This routine is used in place of freeing a message and instead saves the message off a separate queue from the port.
Arguments:
Port - Specifies the port object under which to save this message
Msg - Supplies the message being saved
MutexFlags - Supplies whether the mutex is owned.
Return Value:
None.
--*/
{ PAGED_CODE();
//
// Take out the global lock if our caller didn't already.
//
if ((MutexFlags & LPCP_MUTEX_OWNED) == 0) { LpcpAcquireLpcpLock(); }
//
// Make sure we get to the connection port object of this port
//
if ((Port->Flags & PORT_TYPE) > UNCONNECTED_COMMUNICATION_PORT) {
Port = Port->ConnectionPort;
if (Port == NULL) {
if ((MutexFlags & LPCP_MUTEX_OWNED) == 0) { LpcpReleaseLpcpLock(); }
return; } }
LpcpTrace(( "%s Saving DataInfo Message %lx (%u.%u) Port: %lx\n", PsGetCurrentProcess()->ImageFileName, Msg, Msg->Request.MessageId, Msg->Request.CallbackId, Port ));
//
// Enqueue this message onto the data info chain for the port
//
InsertTailList( &Port->LpcDataInfoChainHead, &Msg->Entry );
//
// Free the global lock
//
if ((MutexFlags & LPCP_MUTEX_OWNED) == 0) { LpcpReleaseLpcpLock(); }
//
// And return to our caller
//
return; }
VOID LpcpFreeDataInfoMessage ( IN PLPCP_PORT_OBJECT Port, IN ULONG MessageId, IN ULONG CallbackId, IN LPC_CLIENT_ID ClientId )
/*++
Routine Description:
This routine is used to free up a saved message in a port
Arguments:
Port - Supplies the port being manipulated
MessageId - Supplies the id of the message being freed
CallbackId - Supplies the callback id of the message being freed
Return Value:
None.
--*/
{ PLPCP_MESSAGE Msg; PLIST_ENTRY Head, Next;
PAGED_CODE();
//
// Make sure we get to the connection port object of this port
//
if ((Port->Flags & PORT_TYPE) > UNCONNECTED_COMMUNICATION_PORT) {
Port = Port->ConnectionPort;
if (Port == NULL) {
return; } }
//
// Zoom down the data info chain for the connection port object
//
Head = &Port->LpcDataInfoChainHead; Next = Head->Flink;
while (Next != Head) {
Msg = CONTAINING_RECORD( Next, LPCP_MESSAGE, Entry );
//
// If this message matches the callers specification then remove
// this message, free it back to the port zone, and return back
// to our caller
//
if ((Msg->Request.MessageId == MessageId) && (Msg->Request.ClientId.UniqueProcess == ClientId.UniqueProcess) && (Msg->Request.ClientId.UniqueThread == ClientId.UniqueThread)) {
LpcpTrace(( "%s Removing DataInfo Message %lx (%u.%u) Port: %lx\n", PsGetCurrentProcess()->ImageFileName, Msg, Msg->Request.MessageId, Msg->Request.CallbackId, Port ));
RemoveEntryList( &Msg->Entry );
InitializeListHead( &Msg->Entry );
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED );
return;
} else { //
// Keep on going down the data info chain
//
Next = Next->Flink; } }
//
// We didn't find a match so just return to our caller
//
LpcpTrace(( "%s Unable to find DataInfo Message (%u.%u) Port: %lx\n", PsGetCurrentProcess()->ImageFileName, MessageId, CallbackId, Port ));
return; }
PLPCP_MESSAGE LpcpFindDataInfoMessage ( IN PLPCP_PORT_OBJECT Port, IN ULONG MessageId, IN ULONG CallbackId, IN LPC_CLIENT_ID ClientId )
/*++
Routine Description:
This routine is used to locate a specific message stored off the data info chain of a port
Arguments:
Port - Supplies the port being examined
MessageId - Supplies the ID of the message being searched for
CallbackId - Supplies the callback ID being searched for
Return Value:
PLPCP_MESSAGE - returns a pointer to the message satisfying the search criteria or NULL of none was found
--*/
{ PLPCP_MESSAGE Msg; PLIST_ENTRY Head, Next;
PAGED_CODE();
//
// Make sure we get to the connection port object of this port
//
if ((Port->Flags & PORT_TYPE) > UNCONNECTED_COMMUNICATION_PORT) {
Port = Port->ConnectionPort;
if (Port == NULL) {
return NULL; } }
//
// Zoom down the data info chain for the connection port object looking
// for a match
//
Head = &Port->LpcDataInfoChainHead; Next = Head->Flink;
while (Next != Head) {
Msg = CONTAINING_RECORD( Next, LPCP_MESSAGE, Entry );
if ((Msg->Request.MessageId == MessageId) && (Msg->Request.ClientId.UniqueProcess == ClientId.UniqueProcess) && (Msg->Request.ClientId.UniqueThread == ClientId.UniqueThread)) {
LpcpTrace(( "%s Found DataInfo Message %lx (%u.%u) Port: %lx\n", PsGetCurrentProcess()->ImageFileName, Msg, Msg->Request.MessageId, Msg->Request.CallbackId, Port ));
return Msg;
} else {
Next = Next->Flink; } }
//
// We did not find a match so return null to our caller
//
LpcpTrace(( "%s Unable to find DataInfo Message (%u.%u) Port: %lx\n", PsGetCurrentProcess()->ImageFileName, MessageId, CallbackId, Port ));
return NULL; }
|