- Use _SEH2_YIELD when returning from an exception instead of returning outside the...
[reactos.git] / reactos / ntoskrnl / lpc / reply.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/lpc/reply.c
5 * PURPOSE: Local Procedure Call: Receive (Replies)
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 LpcpFreeDataInfoMessage(IN PLPCP_PORT_OBJECT Port,
20 IN ULONG MessageId,
21 IN ULONG CallbackId,
22 IN CLIENT_ID ClientId)
23 {
24 PLPCP_MESSAGE Message;
25 PLIST_ENTRY ListHead, NextEntry;
26
27 /* Check if the port we want is the connection port */
28 if ((Port->Flags & LPCP_PORT_TYPE_MASK) > LPCP_UNCONNECTED_PORT)
29 {
30 /* Use it */
31 Port = Port->ConnectionPort;
32 if (!Port) return;
33 }
34
35 /* Loop the list */
36 ListHead = &Port->LpcDataInfoChainHead;
37 NextEntry = ListHead->Flink;
38 while (ListHead != NextEntry)
39 {
40 /* Get the message */
41 Message = CONTAINING_RECORD(NextEntry, LPCP_MESSAGE, Entry);
42
43 /* Make sure it matches */
44 if ((Message->Request.MessageId == MessageId) &&
45 (Message->Request.ClientId.UniqueThread == ClientId.UniqueThread) &&
46 (Message->Request.ClientId.UniqueProcess == ClientId.UniqueProcess))
47 {
48 /* Unlink and free it */
49 RemoveEntryList(&Message->Entry);
50 InitializeListHead(&Message->Entry);
51 LpcpFreeToPortZone(Message, 1);
52 break;
53 }
54
55 /* Go to the next entry */
56 NextEntry = NextEntry->Flink;
57 }
58 }
59
60 VOID
61 NTAPI
62 LpcpSaveDataInfoMessage(IN PLPCP_PORT_OBJECT Port,
63 IN PLPCP_MESSAGE Message,
64 IN ULONG LockHeld)
65 {
66 PAGED_CODE();
67
68 /* Acquire the lock */
69 if (!LockHeld) KeAcquireGuardedMutex(&LpcpLock);
70
71 /* Check if the port we want is the connection port */
72 if ((Port->Flags & LPCP_PORT_TYPE_MASK) > LPCP_UNCONNECTED_PORT)
73 {
74 /* Use it */
75 Port = Port->ConnectionPort;
76 if (!Port)
77 {
78 /* Release the lock and return */
79 if (!LockHeld) KeReleaseGuardedMutex(&LpcpLock);
80 return;
81 }
82 }
83
84 /* Link the message */
85 InsertTailList(&Port->LpcDataInfoChainHead, &Message->Entry);
86
87 /* Release the lock */
88 if (!LockHeld) KeReleaseGuardedMutex(&LpcpLock);
89 }
90
91 VOID
92 NTAPI
93 LpcpMoveMessage(IN PPORT_MESSAGE Destination,
94 IN PPORT_MESSAGE Origin,
95 IN PVOID Data,
96 IN ULONG MessageType,
97 IN PCLIENT_ID ClientId)
98 {
99 /* Set the Message size */
100 LPCTRACE((LPC_REPLY_DEBUG | LPC_SEND_DEBUG),
101 "Destination/Origin: %p/%p. Data: %p. Length: %lx\n",
102 Destination,
103 Origin,
104 Data,
105 Origin->u1.Length);
106 Destination->u1.Length = Origin->u1.Length;
107
108 /* Set the Message Type */
109 Destination->u2.s2.Type = !MessageType ?
110 Origin->u2.s2.Type : MessageType & 0xFFFF;
111
112 /* Check if we have a Client ID */
113 if (ClientId)
114 {
115 /* Set the Client ID */
116 Destination->ClientId.UniqueProcess = ClientId->UniqueProcess;
117 Destination->ClientId.UniqueThread = ClientId->UniqueThread;
118 }
119 else
120 {
121 /* Otherwise, copy it */
122 Destination->ClientId.UniqueProcess = Origin->ClientId.UniqueProcess;
123 Destination->ClientId.UniqueThread = Origin->ClientId.UniqueThread;
124 }
125
126 /* Copy the MessageId and ClientViewSize */
127 Destination->MessageId = Origin->MessageId;
128 Destination->ClientViewSize = Origin->ClientViewSize;
129
130 /* Copy the Message Data */
131 RtlCopyMemory(Destination + 1,
132 Data,
133 ((Destination->u1.Length & 0xFFFF) + 3) &~3);
134 }
135
136 /* PUBLIC FUNCTIONS **********************************************************/
137
138 /*
139 * @unimplemented
140 */
141 NTSTATUS
142 NTAPI
143 NtReplyPort(IN HANDLE PortHandle,
144 IN PPORT_MESSAGE LpcReply)
145 {
146 UNIMPLEMENTED;
147 return STATUS_NOT_IMPLEMENTED;
148 }
149
150 /*
151 * @implemented
152 */
153 NTSTATUS
154 NTAPI
155 NtReplyWaitReceivePortEx(IN HANDLE PortHandle,
156 OUT PVOID *PortContext OPTIONAL,
157 IN PPORT_MESSAGE ReplyMessage OPTIONAL,
158 OUT PPORT_MESSAGE ReceiveMessage,
159 IN PLARGE_INTEGER Timeout OPTIONAL)
160 {
161 PLPCP_PORT_OBJECT Port, ReceivePort, ConnectionPort = NULL;
162 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(), WaitMode = PreviousMode;
163 NTSTATUS Status;
164 PLPCP_MESSAGE Message;
165 PETHREAD Thread = PsGetCurrentThread(), WakeupThread;
166 PLPCP_CONNECTION_MESSAGE ConnectMessage;
167 ULONG ConnectionInfoLength;
168 //PORT_MESSAGE CapturedReplyMessage;
169 LARGE_INTEGER CapturedTimeout;
170
171 PAGED_CODE();
172 LPCTRACE(LPC_REPLY_DEBUG,
173 "Handle: %lx. Messages: %p/%p. Context: %p\n",
174 PortHandle,
175 ReplyMessage,
176 ReceiveMessage,
177 PortContext);
178
179 if (KeGetPreviousMode() == UserMode)
180 {
181 _SEH2_TRY
182 {
183 if (ReplyMessage != NULL)
184 {
185 ProbeForRead(ReplyMessage, sizeof(PORT_MESSAGE), sizeof(ULONG));
186 /*RtlCopyMemory(&CapturedReplyMessage, ReplyMessage, sizeof(PORT_MESSAGE));
187 ReplyMessage = &CapturedReplyMessage;*/
188 }
189
190 if (Timeout != NULL)
191 {
192 ProbeForReadLargeInteger(Timeout);
193 RtlCopyMemory(&CapturedTimeout, Timeout, sizeof(LARGE_INTEGER));
194 Timeout = &CapturedTimeout;
195 }
196
197 if (PortContext != NULL)
198 ProbeForWritePointer(PortContext);
199 }
200 _SEH2_EXCEPT(ExSystemExceptionFilter())
201 {
202 DPRINT1("SEH crash [1]\n");
203 DbgBreakPoint();
204 _SEH2_YIELD(return _SEH2_GetExceptionCode());
205 }
206 _SEH2_END;
207 }
208 else
209 {
210 /* If this is a system thread, then let it page out its stack */
211 if (Thread->SystemThread) WaitMode = UserMode;
212 }
213
214 /* Check if caller has a reply message */
215 if (ReplyMessage)
216 {
217 /* Validate its length */
218 if (((ULONG)ReplyMessage->u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
219 (ULONG)ReplyMessage->u1.s1.TotalLength)
220 {
221 /* Fail */
222 return STATUS_INVALID_PARAMETER;
223 }
224
225 /* Make sure it has a valid ID */
226 if (!ReplyMessage->MessageId) return STATUS_INVALID_PARAMETER;
227 }
228
229 /* Get the Port object */
230 Status = ObReferenceObjectByHandle(PortHandle,
231 0,
232 LpcPortObjectType,
233 PreviousMode,
234 (PVOID*)&Port,
235 NULL);
236 if (!NT_SUCCESS(Status)) return Status;
237
238 /* Check if the caller has a reply message */
239 if (ReplyMessage)
240 {
241 /* Validate its length in respect to the port object */
242 if (((ULONG)ReplyMessage->u1.s1.TotalLength > Port->MaxMessageLength) ||
243 ((ULONG)ReplyMessage->u1.s1.TotalLength <=
244 (ULONG)ReplyMessage->u1.s1.DataLength))
245 {
246 /* Too large, fail */
247 ObDereferenceObject(Port);
248 return STATUS_PORT_MESSAGE_TOO_LONG;
249 }
250 }
251
252 /* Check if this is anything but a client port */
253 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CLIENT_PORT)
254 {
255 /* Check if this is the connection port */
256 if (Port->ConnectionPort == Port)
257 {
258 /* Use this port */
259 ConnectionPort = ReceivePort = Port;
260 ObReferenceObject(ConnectionPort);
261 }
262 else
263 {
264 /* Acquire the lock */
265 KeAcquireGuardedMutex(&LpcpLock);
266
267 /* Get the port */
268 ConnectionPort = ReceivePort = Port->ConnectionPort;
269 if (!ConnectionPort)
270 {
271 /* Fail */
272 KeReleaseGuardedMutex(&LpcpLock);
273 ObDereferenceObject(Port);
274 return STATUS_PORT_DISCONNECTED;
275 }
276
277 /* Release lock and reference */
278 ObReferenceObject(ConnectionPort);
279 KeReleaseGuardedMutex(&LpcpLock);
280 }
281 }
282 else
283 {
284 /* Otherwise, use the port itself */
285 ReceivePort = Port;
286 }
287
288 /* Check if the caller gave a reply message */
289 if (ReplyMessage)
290 {
291 /* Get the ETHREAD corresponding to it */
292 Status = PsLookupProcessThreadByCid(&ReplyMessage->ClientId,
293 NULL,
294 &WakeupThread);
295 if (!NT_SUCCESS(Status))
296 {
297 /* No thread found, fail */
298 ObDereferenceObject(Port);
299 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
300 return Status;
301 }
302
303 /* Allocate a message from the port zone */
304 Message = LpcpAllocateFromPortZone();
305 if (!Message)
306 {
307 /* Fail if we couldn't allocate a message */
308 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
309 ObDereferenceObject(WakeupThread);
310 ObDereferenceObject(Port);
311 return STATUS_NO_MEMORY;
312 }
313
314 /* Keep the lock acquired */
315 KeAcquireGuardedMutex(&LpcpLock);
316
317 /* Make sure this is the reply the thread is waiting for */
318 if ((WakeupThread->LpcReplyMessageId != ReplyMessage->MessageId) ||
319 ((LpcpGetMessageFromThread(WakeupThread)) &&
320 (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread)->
321 Request) != LPC_REQUEST)))
322 {
323 /* It isn't, fail */
324 LpcpFreeToPortZone(Message, 3);
325 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
326 ObDereferenceObject(WakeupThread);
327 ObDereferenceObject(Port);
328 return STATUS_REPLY_MESSAGE_MISMATCH;
329 }
330
331 /* Copy the message */
332 LpcpMoveMessage(&Message->Request,
333 ReplyMessage,
334 ReplyMessage + 1,
335 LPC_REPLY,
336 NULL);
337
338 /* Reference the thread while we use it */
339 ObReferenceObject(WakeupThread);
340 Message->RepliedToThread = WakeupThread;
341
342 /* Set this as the reply message */
343 WakeupThread->LpcReplyMessageId = 0;
344 WakeupThread->LpcReplyMessage = (PVOID)Message;
345
346 /* Check if we have messages on the reply chain */
347 if (!(WakeupThread->LpcExitThreadCalled) &&
348 !(IsListEmpty(&WakeupThread->LpcReplyChain)))
349 {
350 /* Remove us from it and reinitialize it */
351 RemoveEntryList(&WakeupThread->LpcReplyChain);
352 InitializeListHead(&WakeupThread->LpcReplyChain);
353 }
354
355 /* Check if this is the message the thread had received */
356 if ((Thread->LpcReceivedMsgIdValid) &&
357 (Thread->LpcReceivedMessageId == ReplyMessage->MessageId))
358 {
359 /* Clear this data */
360 Thread->LpcReceivedMessageId = 0;
361 Thread->LpcReceivedMsgIdValid = FALSE;
362 }
363
364 /* Free any data information */
365 LpcpFreeDataInfoMessage(Port,
366 ReplyMessage->MessageId,
367 ReplyMessage->CallbackId,
368 ReplyMessage->ClientId);
369
370 /* Release the lock and release the LPC semaphore to wake up waiters */
371 KeReleaseGuardedMutex(&LpcpLock);
372 LpcpCompleteWait(&WakeupThread->LpcReplySemaphore);
373
374 /* Now we can let go of the thread */
375 ObDereferenceObject(WakeupThread);
376 }
377
378 /* Now wait for someone to reply to us */
379 LpcpReceiveWait(ReceivePort->MsgQueue.Semaphore, WaitMode);
380 if (Status != STATUS_SUCCESS) goto Cleanup;
381
382 /* Wait done, get the LPC lock */
383 KeAcquireGuardedMutex(&LpcpLock);
384
385 /* Check if we've received nothing */
386 if (IsListEmpty(&ReceivePort->MsgQueue.ReceiveHead))
387 {
388 /* Check if this was a waitable port and wake it */
389 if (ReceivePort->Flags & LPCP_WAITABLE_PORT)
390 {
391 /* Reset its event */
392 KeResetEvent(&ReceivePort->WaitEvent);
393 }
394
395 /* Release the lock and fail */
396 KeReleaseGuardedMutex(&LpcpLock);
397 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
398 ObDereferenceObject(Port);
399 return STATUS_UNSUCCESSFUL;
400 }
401
402 /* Get the message on the queue */
403 Message = CONTAINING_RECORD(RemoveHeadList(&ReceivePort->
404 MsgQueue.ReceiveHead),
405 LPCP_MESSAGE,
406 Entry);
407
408 /* Check if the queue is empty now */
409 if (IsListEmpty(&ReceivePort->MsgQueue.ReceiveHead))
410 {
411 /* Check if this was a waitable port */
412 if (ReceivePort->Flags & LPCP_WAITABLE_PORT)
413 {
414 /* Reset its event */
415 KeResetEvent(&ReceivePort->WaitEvent);
416 }
417 }
418
419 /* Re-initialize the message's list entry */
420 InitializeListHead(&Message->Entry);
421
422 /* Set this as the received message */
423 Thread->LpcReceivedMessageId = Message->Request.MessageId;
424 Thread->LpcReceivedMsgIdValid = TRUE;
425
426 _SEH2_TRY
427 {
428 /* Check if this was a connection request */
429 if (LpcpGetMessageType(&Message->Request) == LPC_CONNECTION_REQUEST)
430 {
431 /* Get the connection message */
432 ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1);
433 LPCTRACE(LPC_REPLY_DEBUG,
434 "Request Messages: %p/%p\n",
435 Message,
436 ConnectMessage);
437
438 /* Get its length */
439 ConnectionInfoLength = Message->Request.u1.s1.DataLength -
440 sizeof(LPCP_CONNECTION_MESSAGE);
441
442 /* Return it as the receive message */
443 *ReceiveMessage = Message->Request;
444
445 /* Clear our stack variable so the message doesn't get freed */
446 Message = NULL;
447
448 /* Setup the receive message */
449 ReceiveMessage->u1.s1.TotalLength = (CSHORT)(sizeof(LPCP_MESSAGE) +
450 ConnectionInfoLength);
451 ReceiveMessage->u1.s1.DataLength = (CSHORT)ConnectionInfoLength;
452 RtlCopyMemory(ReceiveMessage + 1,
453 ConnectMessage + 1,
454 ConnectionInfoLength);
455
456 /* Clear the port context if the caller requested one */
457 if (PortContext) *PortContext = NULL;
458 }
459 else if (LpcpGetMessageType(&Message->Request) != LPC_REPLY)
460 {
461 /* Otherwise, this is a new message or event */
462 LPCTRACE(LPC_REPLY_DEBUG,
463 "Non-Reply Messages: %p/%p\n",
464 &Message->Request,
465 (&Message->Request) + 1);
466
467 /* Copy it */
468 LpcpMoveMessage(ReceiveMessage,
469 &Message->Request,
470 (&Message->Request) + 1,
471 0,
472 NULL);
473
474 /* Return its context */
475 if (PortContext) *PortContext = Message->PortContext;
476
477 /* And check if it has data information */
478 if (Message->Request.u2.s2.DataInfoOffset)
479 {
480 /* It does, save it, and don't free the message below */
481 LpcpSaveDataInfoMessage(Port, Message, 1);
482 Message = NULL;
483 }
484 }
485 else
486 {
487 /* This is a reply message, should never happen! */
488 ASSERT(FALSE);
489 }
490 }
491 _SEH2_EXCEPT(ExSystemExceptionFilter())
492 {
493 DPRINT1("SEH crash [2]\n");
494 DbgBreakPoint();
495 Status = _SEH2_GetExceptionCode();
496 }
497 _SEH2_END;
498
499 /* Check if we have a message pointer here */
500 if (Message)
501 {
502 /* Free it and release the lock */
503 LpcpFreeToPortZone(Message, 3);
504 }
505 else
506 {
507 /* Just release the lock */
508 KeReleaseGuardedMutex(&LpcpLock);
509 }
510
511 Cleanup:
512 /* All done, dereference the port and return the status */
513 LPCTRACE(LPC_REPLY_DEBUG,
514 "Port: %p. Status: %p\n",
515 Port,
516 Status);
517 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
518 ObDereferenceObject(Port);
519 return Status;
520 }
521
522 /*
523 * @implemented
524 */
525 NTSTATUS
526 NTAPI
527 NtReplyWaitReceivePort(IN HANDLE PortHandle,
528 OUT PVOID *PortContext OPTIONAL,
529 IN PPORT_MESSAGE ReplyMessage OPTIONAL,
530 OUT PPORT_MESSAGE ReceiveMessage)
531 {
532 /* Call the newer API */
533 return NtReplyWaitReceivePortEx(PortHandle,
534 PortContext,
535 ReplyMessage,
536 ReceiveMessage,
537 NULL);
538 }
539
540 /*
541 * @unimplemented
542 */
543 NTSTATUS
544 NTAPI
545 NtReplyWaitReplyPort(IN HANDLE PortHandle,
546 IN PPORT_MESSAGE ReplyMessage)
547 {
548 UNIMPLEMENTED;
549 return STATUS_NOT_IMPLEMENTED;
550 }
551
552 /*
553 * @unimplemented
554 */
555 NTSTATUS
556 NTAPI
557 NtReadRequestData(IN HANDLE PortHandle,
558 IN PPORT_MESSAGE Message,
559 IN ULONG Index,
560 IN PVOID Buffer,
561 IN ULONG BufferLength,
562 OUT PULONG Returnlength)
563 {
564 UNIMPLEMENTED;
565 return STATUS_NOT_IMPLEMENTED;
566 }
567
568 /*
569 * @unimplemented
570 */
571 NTSTATUS
572 NTAPI
573 NtWriteRequestData(IN HANDLE PortHandle,
574 IN PPORT_MESSAGE Message,
575 IN ULONG Index,
576 IN PVOID Buffer,
577 IN ULONG BufferLength,
578 OUT PULONG ReturnLength)
579 {
580 UNIMPLEMENTED;
581 return STATUS_NOT_IMPLEMENTED;
582 }
583
584 /* EOF */