[CLT2012]
[reactos.git] / ntoskrnl / lpc / complete.c
1 /*
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)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* PRIVATE FUNCTIONS *********************************************************/
16
17 VOID
18 NTAPI
19 LpcpPrepareToWakeClient(IN PETHREAD Thread)
20 {
21 PAGED_CODE();
22
23 /* Make sure the thread isn't dying and it has a valid chain */
24 if (!(Thread->LpcExitThreadCalled) &&
25 !(IsListEmpty(&Thread->LpcReplyChain)))
26 {
27 /* Remove it from the list and reinitialize it */
28 RemoveEntryList(&Thread->LpcReplyChain);
29 InitializeListHead(&Thread->LpcReplyChain);
30 }
31 }
32
33 /* PUBLIC FUNCTIONS **********************************************************/
34
35 /*
36 * @implemented
37 */
38 NTSTATUS
39 NTAPI
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)
46 {
47 PLPCP_PORT_OBJECT ConnectionPort, ServerPort, ClientPort;
48 PVOID ClientSectionToMap = NULL;
49 HANDLE Handle;
50 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
51 NTSTATUS Status;
52 ULONG ConnectionInfoLength;
53 PLPCP_MESSAGE Message;
54 PLPCP_CONNECTION_MESSAGE ConnectMessage;
55 PEPROCESS ClientProcess;
56 PETHREAD ClientThread;
57 LARGE_INTEGER SectionOffset;
58 PAGED_CODE();
59 LPCTRACE(LPC_COMPLETE_DEBUG,
60 "Context: %p. Message: %p. Accept: %lx. Views: %p/%p\n",
61 PortContext,
62 ReplyMessage,
63 AcceptConnection,
64 ClientView,
65 ServerView);
66
67 /* Validate the size of the server view */
68 if ((ServerView) && (ServerView->Length != sizeof(PORT_VIEW)))
69 {
70 /* Invalid size */
71 return STATUS_INVALID_PARAMETER;
72 }
73
74 /* Validate the size of the client view */
75 if ((ClientView) && (ClientView->Length != sizeof(REMOTE_PORT_VIEW)))
76 {
77 /* Invalid size */
78 return STATUS_INVALID_PARAMETER;
79 }
80
81 /* Get the client process and thread */
82 Status = PsLookupProcessThreadByCid(&ReplyMessage->ClientId,
83 &ClientProcess,
84 &ClientThread);
85 if (!NT_SUCCESS(Status)) return Status;
86
87 /* Acquire the LPC Lock */
88 KeAcquireGuardedMutex(&LpcpLock);
89
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))
94 {
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;
100 }
101
102 /* Now get the message and connection message */
103 Message = LpcpGetMessageFromThread(ClientThread);
104 ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1);
105
106 /* Get the client and connection port as well */
107 ClientPort = ConnectMessage->ClientPort;
108 ConnectionPort = ClientPort->ConnectionPort;
109
110 /* Make sure that the reply is being sent to the proper server process */
111 if (ConnectionPort->ServerProcess != PsGetCurrentProcess())
112 {
113 /* It's not, so fail */
114 KeReleaseGuardedMutex(&LpcpLock);
115 ObDereferenceObject(ClientProcess);
116 ObDereferenceObject(ClientThread);
117 return STATUS_REPLY_MESSAGE_MISMATCH;
118 }
119
120 /* At this point, don't let other accept attempts happen */
121 ClientThread->LpcReplyMessage = NULL;
122 ClientThread->LpcReplyMessageId = 0;
123
124 /* Clear the client port for now as well, then release the lock */
125 ConnectMessage->ClientPort = NULL;
126 KeReleaseGuardedMutex(&LpcpLock);
127
128 /* Get the connection information length */
129 ConnectionInfoLength = ReplyMessage->u1.s1.DataLength;
130 if (ConnectionInfoLength > ConnectionPort->MaxConnectionInfoLength)
131 {
132 /* Normalize it since it's too large */
133 ConnectionInfoLength = ConnectionPort->MaxConnectionInfoLength;
134 }
135
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;
141
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);
149
150 /* At this point, if the caller refused the connection, go to cleanup */
151 if (!AcceptConnection)
152 {
153 DPRINT1("LPC connection was refused\n");
154 goto Cleanup;
155 }
156
157 /* Otherwise, create the actual port */
158 Status = ObCreateObject(PreviousMode,
159 LpcPortObjectType,
160 NULL,
161 PreviousMode,
162 NULL,
163 sizeof(LPCP_PORT_OBJECT),
164 0,
165 0,
166 (PVOID*)&ServerPort);
167 if (!NT_SUCCESS(Status)) goto Cleanup;
168
169 /* Set it up */
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);
176
177 /* Reference the connection port until we're fully setup */
178 ObReferenceObject(ConnectionPort);
179
180 /* Link the ports together */
181 ServerPort->ConnectionPort = ConnectionPort;
182 ServerPort->ConnectedPort = ClientPort;
183 ClientPort->ConnectedPort = ServerPort;
184
185 /* Also set the creator CID */
186 ServerPort->Creator = PsGetCurrentThread()->Cid;
187 ClientPort->Creator = Message->Request.ClientId;
188
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);
194
195 /* Now check if there's a client section */
196 if (ClientSectionToMap)
197 {
198 /* Setup the offset */
199 SectionOffset.QuadPart = ConnectMessage->ClientView.SectionOffset;
200
201 /* Map the section */
202 Status = MmMapViewOfSection(ClientSectionToMap,
203 PsGetCurrentProcess(),
204 &ServerPort->ClientSectionBase,
205 0,
206 0,
207 &SectionOffset,
208 &ConnectMessage->ClientView.ViewSize,
209 ViewUnmap,
210 0,
211 PAGE_READWRITE);
212
213 /* Update the offset and check for mapping status */
214 ConnectMessage->ClientView.SectionOffset = SectionOffset.LowPart;
215 if (NT_SUCCESS(Status))
216 {
217 /* Set the view base */
218 ConnectMessage->ClientView.ViewRemoteBase = ServerPort->
219 ClientSectionBase;
220
221 /* Save and reference the mapping process */
222 ServerPort->MappingProcess = PsGetCurrentProcess();
223 ObReferenceObject(ServerPort->MappingProcess);
224 }
225 else
226 {
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,
233 SectionOffset);
234 goto Cleanup;
235 }
236 }
237
238 /* Check if there's a server section */
239 if (ServerView)
240 {
241 /* FIXME: TODO */
242 ASSERT(FALSE);
243 }
244
245 /* Reference the server port until it's fully inserted */
246 ObReferenceObject(ServerPort);
247
248 /* Insert the server port in the namespace */
249 Status = ObInsertObject(ServerPort,
250 NULL,
251 PORT_ALL_ACCESS,
252 0,
253 NULL,
254 &Handle);
255 if (!NT_SUCCESS(Status))
256 {
257 /* We failed, remove the extra reference and cleanup */
258 ObDereferenceObject(ServerPort);
259 goto Cleanup;
260 }
261
262 /* Check if the caller gave a client view */
263 if (ClientView)
264 {
265 /* Fill it out */
266 ClientView->ViewBase = ConnectMessage->ClientView.ViewRemoteBase;
267 ClientView->ViewSize = ConnectMessage->ClientView.ViewSize;
268 }
269
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",
274 Handle,
275 Message,
276 ConnectMessage,
277 ServerPort,
278 ClientPort,
279 ConnectionPort);
280
281 /* If there was no port context, use the handle by default */
282 if (!PortContext) ServerPort->PortContext = Handle;
283 ServerPort->ClientThread = ClientThread;
284
285 /* Set this message as the LPC Reply message while holding the lock */
286 KeAcquireGuardedMutex(&LpcpLock);
287 ClientThread->LpcReplyMessage = Message;
288 KeReleaseGuardedMutex(&LpcpLock);
289
290 /* Clear the thread pointer so it doesn't get cleaned later */
291 ClientThread = NULL;
292
293 /* Remove the extra reference we had added */
294 ObDereferenceObject(ServerPort);
295
296 Cleanup:
297 /* If there was a section, dereference it */
298 if (ClientSectionToMap) ObDereferenceObject(ClientSectionToMap);
299
300 /* Check if we got here while still having a client thread */
301 if (ClientThread)
302 {
303 /* FIXME: Complex cleanup code */
304 ASSERT(FALSE);
305 }
306
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",
310 Status,
311 ClientThread,
312 ClientProcess->ImageFileName);
313 if (ClientPort) ObDereferenceObject(ClientPort);
314 ObDereferenceObject(ClientProcess);
315 return Status;
316 }
317
318 /*
319 * @implemented
320 */
321 NTSTATUS
322 NTAPI
323 NtCompleteConnectPort(IN HANDLE PortHandle)
324 {
325 NTSTATUS Status;
326 PLPCP_PORT_OBJECT Port;
327 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
328 PETHREAD Thread;
329 PAGED_CODE();
330 LPCTRACE(LPC_COMPLETE_DEBUG, "Handle: %lx\n", PortHandle);
331
332 /* Get the Port Object */
333 Status = ObReferenceObjectByHandle(PortHandle,
334 PORT_ALL_ACCESS,
335 LpcPortObjectType,
336 PreviousMode,
337 (PVOID*)&Port,
338 NULL);
339 if (!NT_SUCCESS(Status)) return Status;
340
341 /* Make sure this is a connection port */
342 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT)
343 {
344 /* It isn't, fail */
345 ObDereferenceObject(Port);
346 return STATUS_INVALID_PORT_HANDLE;
347 }
348
349 /* Acquire the lock */
350 KeAcquireGuardedMutex(&LpcpLock);
351
352 /* Make sure we have a client thread */
353 if (!Port->ClientThread)
354 {
355 /* We don't, fail */
356 KeReleaseGuardedMutex(&LpcpLock);
357 ObDereferenceObject(Port);
358 return STATUS_INVALID_PARAMETER;
359 }
360
361 /* Get the thread */
362 Thread = Port->ClientThread;
363
364 /* Make sure it has a reply message */
365 if (!LpcpGetMessageFromThread(Thread))
366 {
367 /* It doesn't, quit */
368 KeReleaseGuardedMutex(&LpcpLock);
369 ObDereferenceObject(Port);
370 return STATUS_SUCCESS;
371 }
372
373 /* Clear the client thread and wake it up */
374 Port->ClientThread = NULL;
375 LpcpPrepareToWakeClient(Thread);
376
377 /* Release the lock and wait for an answer */
378 KeReleaseGuardedMutex(&LpcpLock);
379 LpcpCompleteWait(&Thread->LpcReplySemaphore);
380
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);
385 return Status;
386 }
387
388 /* EOF */