2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/lpc/complete.c
5 * PURPOSE: Local Procedure Call: Connection Completion
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
15 /* PRIVATE FUNCTIONS *********************************************************/
19 LpcpPrepareToWakeClient(IN PETHREAD Thread
)
23 /* Make sure the thread isn't dying and it has a valid chain */
24 if (!(Thread
->LpcExitThreadCalled
) &&
25 !(IsListEmpty(&Thread
->LpcReplyChain
)))
27 /* Remove it from the list and reinitialize it */
28 RemoveEntryList(&Thread
->LpcReplyChain
);
29 InitializeListHead(&Thread
->LpcReplyChain
);
33 /* PUBLIC FUNCTIONS **********************************************************/
40 NtAcceptConnectPort(OUT PHANDLE PortHandle
,
41 IN PVOID PortContext OPTIONAL
,
42 IN PPORT_MESSAGE ReplyMessage
,
43 IN BOOLEAN AcceptConnection
,
44 IN PPORT_VIEW ServerView
,
45 IN PREMOTE_PORT_VIEW ClientView
)
47 PLPCP_PORT_OBJECT ConnectionPort
, ServerPort
, ClientPort
;
48 PVOID ClientSectionToMap
= NULL
;
50 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
52 ULONG ConnectionInfoLength
;
53 PLPCP_MESSAGE Message
;
54 PLPCP_CONNECTION_MESSAGE ConnectMessage
;
55 PEPROCESS ClientProcess
;
56 PETHREAD ClientThread
;
57 LARGE_INTEGER SectionOffset
;
59 LPCTRACE(LPC_COMPLETE_DEBUG
,
60 "Context: %p. Message: %p. Accept: %lx. Views: %p/%p\n",
67 /* Validate the size of the server view */
68 if ((ServerView
) && (ServerView
->Length
!= sizeof(PORT_VIEW
)))
71 return STATUS_INVALID_PARAMETER
;
74 /* Validate the size of the client view */
75 if ((ClientView
) && (ClientView
->Length
!= sizeof(REMOTE_PORT_VIEW
)))
78 return STATUS_INVALID_PARAMETER
;
81 /* Get the client process and thread */
82 Status
= PsLookupProcessThreadByCid(&ReplyMessage
->ClientId
,
85 if (!NT_SUCCESS(Status
)) return Status
;
87 /* Acquire the LPC Lock */
88 KeAcquireGuardedMutex(&LpcpLock
);
90 /* Make sure that the client wants a reply, and this is the right one */
91 if (!(LpcpGetMessageFromThread(ClientThread
)) ||
92 !(ReplyMessage
->MessageId
) ||
93 (ClientThread
->LpcReplyMessageId
!= ReplyMessage
->MessageId
))
95 /* Not the reply asked for, or no reply wanted, fail */
96 KeReleaseGuardedMutex(&LpcpLock
);
97 ObDereferenceObject(ClientProcess
);
98 ObDereferenceObject(ClientThread
);
99 return STATUS_REPLY_MESSAGE_MISMATCH
;
102 /* Now get the message and connection message */
103 Message
= LpcpGetMessageFromThread(ClientThread
);
104 ConnectMessage
= (PLPCP_CONNECTION_MESSAGE
)(Message
+ 1);
106 /* Get the client and connection port as well */
107 ClientPort
= ConnectMessage
->ClientPort
;
108 ConnectionPort
= ClientPort
->ConnectionPort
;
110 /* Make sure that the reply is being sent to the proper server process */
111 if (ConnectionPort
->ServerProcess
!= PsGetCurrentProcess())
113 /* It's not, so fail */
114 KeReleaseGuardedMutex(&LpcpLock
);
115 ObDereferenceObject(ClientProcess
);
116 ObDereferenceObject(ClientThread
);
117 return STATUS_REPLY_MESSAGE_MISMATCH
;
120 /* At this point, don't let other accept attempts happen */
121 ClientThread
->LpcReplyMessage
= NULL
;
122 ClientThread
->LpcReplyMessageId
= 0;
124 /* Clear the client port for now as well, then release the lock */
125 ConnectMessage
->ClientPort
= NULL
;
126 KeReleaseGuardedMutex(&LpcpLock
);
128 /* Get the connection information length */
129 ConnectionInfoLength
= ReplyMessage
->u1
.s1
.DataLength
;
130 if (ConnectionInfoLength
> ConnectionPort
->MaxConnectionInfoLength
)
132 /* Normalize it since it's too large */
133 ConnectionInfoLength
= ConnectionPort
->MaxConnectionInfoLength
;
136 /* Set the sizes of our reply message */
137 Message
->Request
.u1
.s1
.DataLength
= (CSHORT
)ConnectionInfoLength
+
138 sizeof(LPCP_CONNECTION_MESSAGE
);
139 Message
->Request
.u1
.s1
.TotalLength
= sizeof(LPCP_MESSAGE
) +
140 Message
->Request
.u1
.s1
.DataLength
;
142 /* Setup the reply message */
143 Message
->Request
.u2
.s2
.Type
= LPC_REPLY
;
144 Message
->Request
.u2
.s2
.DataInfoOffset
= 0;
145 Message
->Request
.ClientId
= ReplyMessage
->ClientId
;
146 Message
->Request
.MessageId
= ReplyMessage
->MessageId
;
147 Message
->Request
.ClientViewSize
= 0;
148 RtlCopyMemory(ConnectMessage
+ 1, ReplyMessage
+ 1, ConnectionInfoLength
);
150 /* At this point, if the caller refused the connection, go to cleanup */
151 if (!AcceptConnection
) goto Cleanup
;
153 /* Otherwise, create the actual port */
154 Status
= ObCreateObject(PreviousMode
,
159 sizeof(LPCP_PORT_OBJECT
),
162 (PVOID
*)&ServerPort
);
163 if (!NT_SUCCESS(Status
)) goto Cleanup
;
166 RtlZeroMemory(ServerPort
, sizeof(LPCP_PORT_OBJECT
));
167 ServerPort
->PortContext
= PortContext
;
168 ServerPort
->Flags
= LPCP_COMMUNICATION_PORT
;
169 ServerPort
->MaxMessageLength
= ConnectionPort
->MaxMessageLength
;
170 InitializeListHead(&ServerPort
->LpcReplyChainHead
);
171 InitializeListHead(&ServerPort
->LpcDataInfoChainHead
);
173 /* Reference the connection port until we're fully setup */
174 ObReferenceObject(ConnectionPort
);
176 /* Link the ports together */
177 ServerPort
->ConnectionPort
= ConnectionPort
;
178 ServerPort
->ConnectedPort
= ClientPort
;
179 ClientPort
->ConnectedPort
= ServerPort
;
181 /* Also set the creator CID */
182 ServerPort
->Creator
= PsGetCurrentThread()->Cid
;
183 ClientPort
->Creator
= Message
->Request
.ClientId
;
185 /* Get the section associated and then clear it, while inside the lock */
186 KeAcquireGuardedMutex(&LpcpLock
);
187 ClientSectionToMap
= ConnectMessage
->SectionToMap
;
188 ConnectMessage
->SectionToMap
= NULL
;
189 KeReleaseGuardedMutex(&LpcpLock
);
191 /* Now check if there's a client section */
192 if (ClientSectionToMap
)
194 /* Setup the offset */
195 SectionOffset
.QuadPart
= ConnectMessage
->ClientView
.SectionOffset
;
197 /* Map the section */
198 Status
= MmMapViewOfSection(ClientSectionToMap
,
199 PsGetCurrentProcess(),
200 &ServerPort
->ClientSectionBase
,
204 &ConnectMessage
->ClientView
.ViewSize
,
209 /* Update the offset and check for mapping status */
210 ConnectMessage
->ClientView
.SectionOffset
= SectionOffset
.LowPart
;
211 if (NT_SUCCESS(Status
))
213 /* Set the view base */
214 ConnectMessage
->ClientView
.ViewRemoteBase
= ServerPort
->
217 /* Save and reference the mapping process */
218 ServerPort
->MappingProcess
= PsGetCurrentProcess();
219 ObReferenceObject(ServerPort
->MappingProcess
);
223 /* Otherwise, quit */
224 ObDereferenceObject(ServerPort
);
229 /* Check if there's a server section */
236 /* Reference the server port until it's fully inserted */
237 ObReferenceObject(ServerPort
);
239 /* Insert the server port in the namespace */
240 Status
= ObInsertObject(ServerPort
,
246 if (!NT_SUCCESS(Status
))
248 /* We failed, remove the extra reference and cleanup */
249 ObDereferenceObject(ServerPort
);
253 /* Check if the caller gave a client view */
257 ClientView
->ViewBase
= ConnectMessage
->ClientView
.ViewRemoteBase
;
258 ClientView
->ViewSize
= ConnectMessage
->ClientView
.ViewSize
;
261 /* Return the handle to user mode */
262 *PortHandle
= Handle
;
263 LPCTRACE(LPC_COMPLETE_DEBUG
,
264 "Handle: %lx. Messages: %p/%p. Ports: %p/%p/%p\n",
272 /* If there was no port context, use the handle by default */
273 if (!PortContext
) ServerPort
->PortContext
= Handle
;
274 ServerPort
->ClientThread
= ClientThread
;
276 /* Set this message as the LPC Reply message while holding the lock */
277 KeAcquireGuardedMutex(&LpcpLock
);
278 ClientThread
->LpcReplyMessage
= Message
;
279 KeReleaseGuardedMutex(&LpcpLock
);
281 /* Clear the thread pointer so it doesn't get cleaned later */
284 /* Remove the extra reference we had added */
285 ObDereferenceObject(ServerPort
);
288 /* If there was a section, dereference it */
289 if (ClientSectionToMap
) ObDereferenceObject(ClientSectionToMap
);
291 /* Check if we got here while still having a client thread */
294 /* FIXME: Complex cleanup code */
298 /* Dereference the client port if we have one, and the process */
299 LPCTRACE(LPC_COMPLETE_DEBUG
,
300 "Status: %lx. Thread: %p. Process: [%.16s]\n",
303 ClientProcess
->ImageFileName
);
304 if (ClientPort
) ObDereferenceObject(ClientPort
);
305 ObDereferenceObject(ClientProcess
);
314 NtCompleteConnectPort(IN HANDLE PortHandle
)
317 PLPCP_PORT_OBJECT Port
;
318 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
321 LPCTRACE(LPC_COMPLETE_DEBUG
, "Handle: %lx\n", PortHandle
);
323 /* Get the Port Object */
324 Status
= ObReferenceObjectByHandle(PortHandle
,
330 if (!NT_SUCCESS(Status
)) return Status
;
332 /* Make sure this is a connection port */
333 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_COMMUNICATION_PORT
)
336 ObDereferenceObject(Port
);
337 return STATUS_INVALID_PORT_HANDLE
;
340 /* Acquire the lock */
341 KeAcquireGuardedMutex(&LpcpLock
);
343 /* Make sure we have a client thread */
344 if (!Port
->ClientThread
)
347 KeReleaseGuardedMutex(&LpcpLock
);
348 ObDereferenceObject(Port
);
349 return STATUS_INVALID_PARAMETER
;
353 Thread
= Port
->ClientThread
;
355 /* Make sure it has a reply message */
356 if (!LpcpGetMessageFromThread(Thread
))
358 /* It doesn't, quit */
359 KeReleaseGuardedMutex(&LpcpLock
);
360 ObDereferenceObject(Port
);
361 return STATUS_SUCCESS
;
364 /* Clear the client thread and wake it up */
365 Port
->ClientThread
= NULL
;
366 LpcpPrepareToWakeClient(Thread
);
368 /* Release the lock and wait for an answer */
369 KeReleaseGuardedMutex(&LpcpLock
);
370 LpcpCompleteWait(&Thread
->LpcReplySemaphore
);
372 /* Dereference the Thread and Port and return */
373 ObDereferenceObject(Port
);
374 ObDereferenceObject(Thread
);
375 LPCTRACE(LPC_COMPLETE_DEBUG
, "Port: %p. Thread: %p\n", Port
, Thread
);