- Update to trunk
[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) goto Cleanup;
152
153 /* Otherwise, create the actual port */
154 Status = ObCreateObject(PreviousMode,
155 LpcPortObjectType,
156 NULL,
157 PreviousMode,
158 NULL,
159 sizeof(LPCP_PORT_OBJECT),
160 0,
161 0,
162 (PVOID*)&ServerPort);
163 if (!NT_SUCCESS(Status)) goto Cleanup;
164
165 /* Set it up */
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);
172
173 /* Reference the connection port until we're fully setup */
174 ObReferenceObject(ConnectionPort);
175
176 /* Link the ports together */
177 ServerPort->ConnectionPort = ConnectionPort;
178 ServerPort->ConnectedPort = ClientPort;
179 ClientPort->ConnectedPort = ServerPort;
180
181 /* Also set the creator CID */
182 ServerPort->Creator = PsGetCurrentThread()->Cid;
183 ClientPort->Creator = Message->Request.ClientId;
184
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);
190
191 /* Now check if there's a client section */
192 if (ClientSectionToMap)
193 {
194 /* Setup the offset */
195 SectionOffset.QuadPart = ConnectMessage->ClientView.SectionOffset;
196
197 /* Map the section */
198 Status = MmMapViewOfSection(ClientSectionToMap,
199 PsGetCurrentProcess(),
200 &ServerPort->ClientSectionBase,
201 0,
202 0,
203 &SectionOffset,
204 &ConnectMessage->ClientView.ViewSize,
205 ViewUnmap,
206 0,
207 PAGE_READWRITE);
208
209 /* Update the offset and check for mapping status */
210 ConnectMessage->ClientView.SectionOffset = SectionOffset.LowPart;
211 if (NT_SUCCESS(Status))
212 {
213 /* Set the view base */
214 ConnectMessage->ClientView.ViewRemoteBase = ServerPort->
215 ClientSectionBase;
216
217 /* Save and reference the mapping process */
218 ServerPort->MappingProcess = PsGetCurrentProcess();
219 ObReferenceObject(ServerPort->MappingProcess);
220 }
221 else
222 {
223 /* Otherwise, quit */
224 ObDereferenceObject(ServerPort);
225 goto Cleanup;
226 }
227 }
228
229 /* Check if there's a server section */
230 if (ServerView)
231 {
232 /* FIXME: TODO */
233 ASSERT(FALSE);
234 }
235
236 /* Reference the server port until it's fully inserted */
237 ObReferenceObject(ServerPort);
238
239 /* Insert the server port in the namespace */
240 Status = ObInsertObject(ServerPort,
241 NULL,
242 PORT_ALL_ACCESS,
243 0,
244 NULL,
245 &Handle);
246 if (!NT_SUCCESS(Status))
247 {
248 /* We failed, remove the extra reference and cleanup */
249 ObDereferenceObject(ServerPort);
250 goto Cleanup;
251 }
252
253 /* Check if the caller gave a client view */
254 if (ClientView)
255 {
256 /* Fill it out */
257 ClientView->ViewBase = ConnectMessage->ClientView.ViewRemoteBase;
258 ClientView->ViewSize = ConnectMessage->ClientView.ViewSize;
259 }
260
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",
265 Handle,
266 Message,
267 ConnectMessage,
268 ServerPort,
269 ClientPort,
270 ConnectionPort);
271
272 /* If there was no port context, use the handle by default */
273 if (!PortContext) ServerPort->PortContext = Handle;
274 ServerPort->ClientThread = ClientThread;
275
276 /* Set this message as the LPC Reply message while holding the lock */
277 KeAcquireGuardedMutex(&LpcpLock);
278 ClientThread->LpcReplyMessage = Message;
279 KeReleaseGuardedMutex(&LpcpLock);
280
281 /* Clear the thread pointer so it doesn't get cleaned later */
282 ClientThread = NULL;
283
284 /* Remove the extra reference we had added */
285 ObDereferenceObject(ServerPort);
286
287 Cleanup:
288 /* If there was a section, dereference it */
289 if (ClientSectionToMap) ObDereferenceObject(ClientSectionToMap);
290
291 /* Check if we got here while still having a client thread */
292 if (ClientThread)
293 {
294 /* FIXME: Complex cleanup code */
295 ASSERT(FALSE);
296 }
297
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",
301 Status,
302 ClientThread,
303 ClientProcess->ImageFileName);
304 if (ClientPort) ObDereferenceObject(ClientPort);
305 ObDereferenceObject(ClientProcess);
306 return Status;
307 }
308
309 /*
310 * @implemented
311 */
312 NTSTATUS
313 NTAPI
314 NtCompleteConnectPort(IN HANDLE PortHandle)
315 {
316 NTSTATUS Status;
317 PLPCP_PORT_OBJECT Port;
318 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
319 PETHREAD Thread;
320 PAGED_CODE();
321 LPCTRACE(LPC_COMPLETE_DEBUG, "Handle: %lx\n", PortHandle);
322
323 /* Get the Port Object */
324 Status = ObReferenceObjectByHandle(PortHandle,
325 PORT_ALL_ACCESS,
326 LpcPortObjectType,
327 PreviousMode,
328 (PVOID*)&Port,
329 NULL);
330 if (!NT_SUCCESS(Status)) return Status;
331
332 /* Make sure this is a connection port */
333 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT)
334 {
335 /* It isn't, fail */
336 ObDereferenceObject(Port);
337 return STATUS_INVALID_PORT_HANDLE;
338 }
339
340 /* Acquire the lock */
341 KeAcquireGuardedMutex(&LpcpLock);
342
343 /* Make sure we have a client thread */
344 if (!Port->ClientThread)
345 {
346 /* We don't, fail */
347 KeReleaseGuardedMutex(&LpcpLock);
348 ObDereferenceObject(Port);
349 return STATUS_INVALID_PARAMETER;
350 }
351
352 /* Get the thread */
353 Thread = Port->ClientThread;
354
355 /* Make sure it has a reply message */
356 if (!LpcpGetMessageFromThread(Thread))
357 {
358 /* It doesn't, quit */
359 KeReleaseGuardedMutex(&LpcpLock);
360 ObDereferenceObject(Port);
361 return STATUS_SUCCESS;
362 }
363
364 /* Clear the client thread and wake it up */
365 Port->ClientThread = NULL;
366 LpcpPrepareToWakeClient(Thread);
367
368 /* Release the lock and wait for an answer */
369 KeReleaseGuardedMutex(&LpcpLock);
370 LpcpCompleteWait(&Thread->LpcReplySemaphore);
371
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);
376 return Status;
377 }
378
379 /* EOF */