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
)
153 DPRINT1("LPC connection was refused\n");
157 /* Otherwise, create the actual port */
158 Status
= ObCreateObject(PreviousMode
,
163 sizeof(LPCP_PORT_OBJECT
),
166 (PVOID
*)&ServerPort
);
167 if (!NT_SUCCESS(Status
)) goto Cleanup
;
170 RtlZeroMemory(ServerPort
, sizeof(LPCP_PORT_OBJECT
));
171 ServerPort
->PortContext
= PortContext
;
172 ServerPort
->Flags
= LPCP_COMMUNICATION_PORT
;
173 ServerPort
->MaxMessageLength
= ConnectionPort
->MaxMessageLength
;
174 InitializeListHead(&ServerPort
->LpcReplyChainHead
);
175 InitializeListHead(&ServerPort
->LpcDataInfoChainHead
);
177 /* Reference the connection port until we're fully setup */
178 ObReferenceObject(ConnectionPort
);
180 /* Link the ports together */
181 ServerPort
->ConnectionPort
= ConnectionPort
;
182 ServerPort
->ConnectedPort
= ClientPort
;
183 ClientPort
->ConnectedPort
= ServerPort
;
185 /* Also set the creator CID */
186 ServerPort
->Creator
= PsGetCurrentThread()->Cid
;
187 ClientPort
->Creator
= Message
->Request
.ClientId
;
189 /* Get the section associated and then clear it, while inside the lock */
190 KeAcquireGuardedMutex(&LpcpLock
);
191 ClientSectionToMap
= ConnectMessage
->SectionToMap
;
192 ConnectMessage
->SectionToMap
= NULL
;
193 KeReleaseGuardedMutex(&LpcpLock
);
195 /* Now check if there's a client section */
196 if (ClientSectionToMap
)
198 /* Setup the offset */
199 SectionOffset
.QuadPart
= ConnectMessage
->ClientView
.SectionOffset
;
201 /* Map the section */
202 Status
= MmMapViewOfSection(ClientSectionToMap
,
203 PsGetCurrentProcess(),
204 &ServerPort
->ClientSectionBase
,
208 &ConnectMessage
->ClientView
.ViewSize
,
213 /* Update the offset and check for mapping status */
214 ConnectMessage
->ClientView
.SectionOffset
= SectionOffset
.LowPart
;
215 if (NT_SUCCESS(Status
))
217 /* Set the view base */
218 ConnectMessage
->ClientView
.ViewRemoteBase
= ServerPort
->
221 /* Save and reference the mapping process */
222 ServerPort
->MappingProcess
= PsGetCurrentProcess();
223 ObReferenceObject(ServerPort
->MappingProcess
);
227 /* Otherwise, quit */
228 ObDereferenceObject(ServerPort
);
229 DPRINT1("Client section mapping failed: %lx\n", Status
);
230 DPRINT1("View base, offset, size: %lx %lx %lx\n",
231 ServerPort
->ClientSectionBase
,
232 ConnectMessage
->ClientView
.ViewSize
,
238 /* Check if there's a server section */
245 /* Reference the server port until it's fully inserted */
246 ObReferenceObject(ServerPort
);
248 /* Insert the server port in the namespace */
249 Status
= ObInsertObject(ServerPort
,
255 if (!NT_SUCCESS(Status
))
257 /* We failed, remove the extra reference and cleanup */
258 ObDereferenceObject(ServerPort
);
262 /* Check if the caller gave a client view */
266 ClientView
->ViewBase
= ConnectMessage
->ClientView
.ViewRemoteBase
;
267 ClientView
->ViewSize
= ConnectMessage
->ClientView
.ViewSize
;
270 /* Return the handle to user mode */
271 *PortHandle
= Handle
;
272 LPCTRACE(LPC_COMPLETE_DEBUG
,
273 "Handle: %lx. Messages: %p/%p. Ports: %p/%p/%p\n",
281 /* If there was no port context, use the handle by default */
282 if (!PortContext
) ServerPort
->PortContext
= Handle
;
283 ServerPort
->ClientThread
= ClientThread
;
285 /* Set this message as the LPC Reply message while holding the lock */
286 KeAcquireGuardedMutex(&LpcpLock
);
287 ClientThread
->LpcReplyMessage
= Message
;
288 KeReleaseGuardedMutex(&LpcpLock
);
290 /* Clear the thread pointer so it doesn't get cleaned later */
293 /* Remove the extra reference we had added */
294 ObDereferenceObject(ServerPort
);
297 /* If there was a section, dereference it */
298 if (ClientSectionToMap
) ObDereferenceObject(ClientSectionToMap
);
300 /* Check if we got here while still having a client thread */
303 /* FIXME: Complex cleanup code */
307 /* Dereference the client port if we have one, and the process */
308 LPCTRACE(LPC_COMPLETE_DEBUG
,
309 "Status: %lx. Thread: %p. Process: [%.16s]\n",
312 ClientProcess
->ImageFileName
);
313 if (ClientPort
) ObDereferenceObject(ClientPort
);
314 ObDereferenceObject(ClientProcess
);
323 NtCompleteConnectPort(IN HANDLE PortHandle
)
326 PLPCP_PORT_OBJECT Port
;
327 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
330 LPCTRACE(LPC_COMPLETE_DEBUG
, "Handle: %lx\n", PortHandle
);
332 /* Get the Port Object */
333 Status
= ObReferenceObjectByHandle(PortHandle
,
339 if (!NT_SUCCESS(Status
)) return Status
;
341 /* Make sure this is a connection port */
342 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_COMMUNICATION_PORT
)
345 ObDereferenceObject(Port
);
346 return STATUS_INVALID_PORT_HANDLE
;
349 /* Acquire the lock */
350 KeAcquireGuardedMutex(&LpcpLock
);
352 /* Make sure we have a client thread */
353 if (!Port
->ClientThread
)
356 KeReleaseGuardedMutex(&LpcpLock
);
357 ObDereferenceObject(Port
);
358 return STATUS_INVALID_PARAMETER
;
362 Thread
= Port
->ClientThread
;
364 /* Make sure it has a reply message */
365 if (!LpcpGetMessageFromThread(Thread
))
367 /* It doesn't, quit */
368 KeReleaseGuardedMutex(&LpcpLock
);
369 ObDereferenceObject(Port
);
370 return STATUS_SUCCESS
;
373 /* Clear the client thread and wake it up */
374 Port
->ClientThread
= NULL
;
375 LpcpPrepareToWakeClient(Thread
);
377 /* Release the lock and wait for an answer */
378 KeReleaseGuardedMutex(&LpcpLock
);
379 LpcpCompleteWait(&Thread
->LpcReplySemaphore
);
381 /* Dereference the Thread and Port and return */
382 ObDereferenceObject(Port
);
383 ObDereferenceObject(Thread
);
384 LPCTRACE(LPC_COMPLETE_DEBUG
, "Port: %p. Thread: %p\n", Port
, Thread
);