Sync with trunk revision 63128.
[reactos.git] / 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, LPCP_LOCK_HELD);
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 LockFlags)
65 {
66 BOOLEAN LockHeld = (LockFlags & LPCP_LOCK_HELD);
67
68 PAGED_CODE();
69
70 /* Acquire the lock */
71 if (!LockHeld) KeAcquireGuardedMutex(&LpcpLock);
72
73 /* Check if the port we want is the connection port */
74 if ((Port->Flags & LPCP_PORT_TYPE_MASK) > LPCP_UNCONNECTED_PORT)
75 {
76 /* Use it */
77 Port = Port->ConnectionPort;
78 if (!Port)
79 {
80 /* Release the lock and return */
81 if (!LockHeld) KeReleaseGuardedMutex(&LpcpLock);
82 return;
83 }
84 }
85
86 /* Link the message */
87 InsertTailList(&Port->LpcDataInfoChainHead, &Message->Entry);
88
89 /* Release the lock */
90 if (!LockHeld) KeReleaseGuardedMutex(&LpcpLock);
91 }
92
93 PLPCP_MESSAGE
94 NTAPI
95 LpcpFindDataInfoMessage(
96 IN PLPCP_PORT_OBJECT Port,
97 IN ULONG MessageId,
98 IN LPC_CLIENT_ID ClientId)
99 {
100 PLPCP_MESSAGE Message;
101 PLIST_ENTRY ListEntry;
102 PAGED_CODE();
103
104 /* Check if the port we want is the connection port */
105 if ((Port->Flags & LPCP_PORT_TYPE_MASK) > LPCP_UNCONNECTED_PORT)
106 {
107 /* Use it */
108 Port = Port->ConnectionPort;
109 if (!Port)
110 {
111 /* Return NULL */
112 return NULL;
113 }
114 }
115
116 /* Loop all entries in the list */
117 for (ListEntry = Port->LpcDataInfoChainHead.Flink;
118 ListEntry != &Port->LpcDataInfoChainHead;
119 ListEntry = ListEntry->Flink)
120 {
121 Message = CONTAINING_RECORD(ListEntry, LPCP_MESSAGE, Entry);
122
123 /* Check if this is the desired message */
124 if ((Message->Request.MessageId == MessageId) &&
125 (Message->Request.ClientId.UniqueProcess == ClientId.UniqueProcess) &&
126 (Message->Request.ClientId.UniqueThread == ClientId.UniqueThread))
127 {
128 /* It is, return it */
129 return Message;
130 }
131 }
132
133 return NULL;
134 }
135
136 VOID
137 NTAPI
138 LpcpMoveMessage(IN PPORT_MESSAGE Destination,
139 IN PPORT_MESSAGE Origin,
140 IN PVOID Data,
141 IN ULONG MessageType,
142 IN PCLIENT_ID ClientId)
143 {
144 /* Set the Message size */
145 LPCTRACE((LPC_REPLY_DEBUG | LPC_SEND_DEBUG),
146 "Destination/Origin: %p/%p. Data: %p. Length: %lx\n",
147 Destination,
148 Origin,
149 Data,
150 Origin->u1.Length);
151 Destination->u1.Length = Origin->u1.Length;
152
153 /* Set the Message Type */
154 Destination->u2.s2.Type = !MessageType ?
155 Origin->u2.s2.Type : MessageType & 0xFFFF;
156
157 /* Check if we have a Client ID */
158 if (ClientId)
159 {
160 /* Set the Client ID */
161 Destination->ClientId.UniqueProcess = ClientId->UniqueProcess;
162 Destination->ClientId.UniqueThread = ClientId->UniqueThread;
163 }
164 else
165 {
166 /* Otherwise, copy it */
167 Destination->ClientId.UniqueProcess = Origin->ClientId.UniqueProcess;
168 Destination->ClientId.UniqueThread = Origin->ClientId.UniqueThread;
169 }
170
171 /* Copy the MessageId and ClientViewSize */
172 Destination->MessageId = Origin->MessageId;
173 Destination->ClientViewSize = Origin->ClientViewSize;
174
175 /* Copy the Message Data */
176 RtlCopyMemory(Destination + 1,
177 Data,
178 ALIGN_UP_BY(Destination->u1.s1.DataLength, sizeof(ULONG)));
179 }
180
181 /* PUBLIC FUNCTIONS **********************************************************/
182
183 /*
184 * @implemented
185 */
186 NTSTATUS
187 NTAPI
188 NtReplyPort(IN HANDLE PortHandle,
189 IN PPORT_MESSAGE ReplyMessage)
190 {
191 PLPCP_PORT_OBJECT Port, ConnectionPort = NULL;
192 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
193 NTSTATUS Status;
194 PLPCP_MESSAGE Message;
195 PETHREAD Thread = PsGetCurrentThread(), WakeupThread;
196 //PORT_MESSAGE CapturedReplyMessage;
197
198 PAGED_CODE();
199 LPCTRACE(LPC_REPLY_DEBUG,
200 "Handle: %p. Message: %p.\n",
201 PortHandle,
202 ReplyMessage);
203
204 if (KeGetPreviousMode() == UserMode)
205 {
206 _SEH2_TRY
207 {
208 ProbeForRead(ReplyMessage, sizeof(PORT_MESSAGE), sizeof(ULONG));
209 /*RtlCopyMemory(&CapturedReplyMessage, ReplyMessage, sizeof(PORT_MESSAGE));
210 ReplyMessage = &CapturedReplyMessage;*/
211 }
212 _SEH2_EXCEPT(ExSystemExceptionFilter())
213 {
214 DPRINT1("SEH crash [1]\n");
215 DbgBreakPoint();
216 _SEH2_YIELD(return _SEH2_GetExceptionCode());
217 }
218 _SEH2_END;
219 }
220
221 /* Validate its length */
222 if (((ULONG)ReplyMessage->u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
223 (ULONG)ReplyMessage->u1.s1.TotalLength)
224 {
225 /* Fail */
226 return STATUS_INVALID_PARAMETER;
227 }
228
229 /* Make sure it has a valid ID */
230 if (!ReplyMessage->MessageId) return STATUS_INVALID_PARAMETER;
231
232 /* Get the Port object */
233 Status = ObReferenceObjectByHandle(PortHandle,
234 0,
235 LpcPortObjectType,
236 PreviousMode,
237 (PVOID*)&Port,
238 NULL);
239 if (!NT_SUCCESS(Status)) return Status;
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 /* Get the ETHREAD corresponding to it */
252 Status = PsLookupProcessThreadByCid(&ReplyMessage->ClientId,
253 NULL,
254 &WakeupThread);
255 if (!NT_SUCCESS(Status))
256 {
257 /* No thread found, fail */
258 ObDereferenceObject(Port);
259 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
260 return Status;
261 }
262
263 /* Allocate a message from the port zone */
264 Message = LpcpAllocateFromPortZone();
265 if (!Message)
266 {
267 /* Fail if we couldn't allocate a message */
268 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
269 ObDereferenceObject(WakeupThread);
270 ObDereferenceObject(Port);
271 return STATUS_NO_MEMORY;
272 }
273
274 /* Keep the lock acquired */
275 KeAcquireGuardedMutex(&LpcpLock);
276
277 /* Make sure this is the reply the thread is waiting for */
278 if ((WakeupThread->LpcReplyMessageId != ReplyMessage->MessageId) ||
279 ((LpcpGetMessageFromThread(WakeupThread)) &&
280 (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread)->
281 Request) != LPC_REQUEST)))
282 {
283 /* It isn't, fail */
284 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
285 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
286 ObDereferenceObject(WakeupThread);
287 ObDereferenceObject(Port);
288 return STATUS_REPLY_MESSAGE_MISMATCH;
289 }
290
291 /* Copy the message */
292 _SEH2_TRY
293 {
294 LpcpMoveMessage(&Message->Request,
295 ReplyMessage,
296 ReplyMessage + 1,
297 LPC_REPLY,
298 NULL);
299 }
300 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
301 {
302 /* Fail */
303 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
304 ObDereferenceObject(WakeupThread);
305 ObDereferenceObject(Port);
306 _SEH2_YIELD(return _SEH2_GetExceptionCode());
307 }
308 _SEH2_END;
309
310 /* Reference the thread while we use it */
311 ObReferenceObject(WakeupThread);
312 Message->RepliedToThread = WakeupThread;
313
314 /* Set this as the reply message */
315 WakeupThread->LpcReplyMessageId = 0;
316 WakeupThread->LpcReplyMessage = (PVOID)Message;
317
318 /* Check if we have messages on the reply chain */
319 if (!(WakeupThread->LpcExitThreadCalled) &&
320 !(IsListEmpty(&WakeupThread->LpcReplyChain)))
321 {
322 /* Remove us from it and reinitialize it */
323 RemoveEntryList(&WakeupThread->LpcReplyChain);
324 InitializeListHead(&WakeupThread->LpcReplyChain);
325 }
326
327 /* Check if this is the message the thread had received */
328 if ((Thread->LpcReceivedMsgIdValid) &&
329 (Thread->LpcReceivedMessageId == ReplyMessage->MessageId))
330 {
331 /* Clear this data */
332 Thread->LpcReceivedMessageId = 0;
333 Thread->LpcReceivedMsgIdValid = FALSE;
334 }
335
336 /* Free any data information */
337 LpcpFreeDataInfoMessage(Port,
338 ReplyMessage->MessageId,
339 ReplyMessage->CallbackId,
340 ReplyMessage->ClientId);
341
342 /* Release the lock and release the LPC semaphore to wake up waiters */
343 KeReleaseGuardedMutex(&LpcpLock);
344 LpcpCompleteWait(&WakeupThread->LpcReplySemaphore);
345
346 /* Now we can let go of the thread */
347 ObDereferenceObject(WakeupThread);
348
349 /* Dereference port object */
350 ObDereferenceObject(Port);
351 return Status;
352 }
353
354 /*
355 * @implemented
356 */
357 NTSTATUS
358 NTAPI
359 NtReplyWaitReceivePortEx(IN HANDLE PortHandle,
360 OUT PVOID *PortContext OPTIONAL,
361 IN PPORT_MESSAGE ReplyMessage OPTIONAL,
362 OUT PPORT_MESSAGE ReceiveMessage,
363 IN PLARGE_INTEGER Timeout OPTIONAL)
364 {
365 PLPCP_PORT_OBJECT Port, ReceivePort, ConnectionPort = NULL;
366 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(), WaitMode = PreviousMode;
367 NTSTATUS Status;
368 PLPCP_MESSAGE Message;
369 PETHREAD Thread = PsGetCurrentThread(), WakeupThread;
370 PLPCP_CONNECTION_MESSAGE ConnectMessage;
371 ULONG ConnectionInfoLength;
372 //PORT_MESSAGE CapturedReplyMessage;
373 LARGE_INTEGER CapturedTimeout;
374
375 PAGED_CODE();
376 LPCTRACE(LPC_REPLY_DEBUG,
377 "Handle: %p. Messages: %p/%p. Context: %p\n",
378 PortHandle,
379 ReplyMessage,
380 ReceiveMessage,
381 PortContext);
382
383 if (KeGetPreviousMode() == UserMode)
384 {
385 _SEH2_TRY
386 {
387 if (ReplyMessage != NULL)
388 {
389 ProbeForRead(ReplyMessage, sizeof(PORT_MESSAGE), sizeof(ULONG));
390 /*RtlCopyMemory(&CapturedReplyMessage, ReplyMessage, sizeof(PORT_MESSAGE));
391 ReplyMessage = &CapturedReplyMessage;*/
392 }
393
394 if (Timeout != NULL)
395 {
396 ProbeForReadLargeInteger(Timeout);
397 RtlCopyMemory(&CapturedTimeout, Timeout, sizeof(LARGE_INTEGER));
398 Timeout = &CapturedTimeout;
399 }
400
401 if (PortContext != NULL)
402 ProbeForWritePointer(PortContext);
403 }
404 _SEH2_EXCEPT(ExSystemExceptionFilter())
405 {
406 DPRINT1("SEH crash [1]\n");
407 DbgBreakPoint();
408 _SEH2_YIELD(return _SEH2_GetExceptionCode());
409 }
410 _SEH2_END;
411 }
412 else
413 {
414 /* If this is a system thread, then let it page out its stack */
415 if (Thread->SystemThread) WaitMode = UserMode;
416 }
417
418 /* Check if caller has a reply message */
419 if (ReplyMessage)
420 {
421 /* Validate its length */
422 if (((ULONG)ReplyMessage->u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
423 (ULONG)ReplyMessage->u1.s1.TotalLength)
424 {
425 /* Fail */
426 return STATUS_INVALID_PARAMETER;
427 }
428
429 /* Make sure it has a valid ID */
430 if (!ReplyMessage->MessageId) return STATUS_INVALID_PARAMETER;
431 }
432
433 /* Get the Port object */
434 Status = ObReferenceObjectByHandle(PortHandle,
435 0,
436 LpcPortObjectType,
437 PreviousMode,
438 (PVOID*)&Port,
439 NULL);
440 if (!NT_SUCCESS(Status)) return Status;
441
442 /* Check if the caller has a reply message */
443 if (ReplyMessage)
444 {
445 /* Validate its length in respect to the port object */
446 if (((ULONG)ReplyMessage->u1.s1.TotalLength > Port->MaxMessageLength) ||
447 ((ULONG)ReplyMessage->u1.s1.TotalLength <=
448 (ULONG)ReplyMessage->u1.s1.DataLength))
449 {
450 /* Too large, fail */
451 ObDereferenceObject(Port);
452 return STATUS_PORT_MESSAGE_TOO_LONG;
453 }
454 }
455
456 /* Check if this is anything but a client port */
457 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CLIENT_PORT)
458 {
459 /* Check if this is the connection port */
460 if (Port->ConnectionPort == Port)
461 {
462 /* Use this port */
463 ConnectionPort = ReceivePort = Port;
464 ObReferenceObject(ConnectionPort);
465 }
466 else
467 {
468 /* Acquire the lock */
469 KeAcquireGuardedMutex(&LpcpLock);
470
471 /* Get the port */
472 ConnectionPort = ReceivePort = Port->ConnectionPort;
473 if (!ConnectionPort)
474 {
475 /* Fail */
476 KeReleaseGuardedMutex(&LpcpLock);
477 ObDereferenceObject(Port);
478 return STATUS_PORT_DISCONNECTED;
479 }
480
481 /* Release lock and reference */
482 ObReferenceObject(ConnectionPort);
483 KeReleaseGuardedMutex(&LpcpLock);
484 }
485 }
486 else
487 {
488 /* Otherwise, use the port itself */
489 ReceivePort = Port;
490 }
491
492 /* Check if the caller gave a reply message */
493 if (ReplyMessage)
494 {
495 /* Get the ETHREAD corresponding to it */
496 Status = PsLookupProcessThreadByCid(&ReplyMessage->ClientId,
497 NULL,
498 &WakeupThread);
499 if (!NT_SUCCESS(Status))
500 {
501 /* No thread found, fail */
502 ObDereferenceObject(Port);
503 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
504 return Status;
505 }
506
507 /* Allocate a message from the port zone */
508 Message = LpcpAllocateFromPortZone();
509 if (!Message)
510 {
511 /* Fail if we couldn't allocate a message */
512 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
513 ObDereferenceObject(WakeupThread);
514 ObDereferenceObject(Port);
515 return STATUS_NO_MEMORY;
516 }
517
518 /* Keep the lock acquired */
519 KeAcquireGuardedMutex(&LpcpLock);
520
521 /* Make sure this is the reply the thread is waiting for */
522 if ((WakeupThread->LpcReplyMessageId != ReplyMessage->MessageId) ||
523 ((LpcpGetMessageFromThread(WakeupThread)) &&
524 (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread)->
525 Request) != LPC_REQUEST)))
526 {
527 /* It isn't, fail */
528 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
529 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
530 ObDereferenceObject(WakeupThread);
531 ObDereferenceObject(Port);
532 return STATUS_REPLY_MESSAGE_MISMATCH;
533 }
534
535 /* Copy the message */
536 LpcpMoveMessage(&Message->Request,
537 ReplyMessage,
538 ReplyMessage + 1,
539 LPC_REPLY,
540 NULL);
541
542 /* Reference the thread while we use it */
543 ObReferenceObject(WakeupThread);
544 Message->RepliedToThread = WakeupThread;
545
546 /* Set this as the reply message */
547 WakeupThread->LpcReplyMessageId = 0;
548 WakeupThread->LpcReplyMessage = (PVOID)Message;
549
550 /* Check if we have messages on the reply chain */
551 if (!(WakeupThread->LpcExitThreadCalled) &&
552 !(IsListEmpty(&WakeupThread->LpcReplyChain)))
553 {
554 /* Remove us from it and reinitialize it */
555 RemoveEntryList(&WakeupThread->LpcReplyChain);
556 InitializeListHead(&WakeupThread->LpcReplyChain);
557 }
558
559 /* Check if this is the message the thread had received */
560 if ((Thread->LpcReceivedMsgIdValid) &&
561 (Thread->LpcReceivedMessageId == ReplyMessage->MessageId))
562 {
563 /* Clear this data */
564 Thread->LpcReceivedMessageId = 0;
565 Thread->LpcReceivedMsgIdValid = FALSE;
566 }
567
568 /* Free any data information */
569 LpcpFreeDataInfoMessage(Port,
570 ReplyMessage->MessageId,
571 ReplyMessage->CallbackId,
572 ReplyMessage->ClientId);
573
574 /* Release the lock and release the LPC semaphore to wake up waiters */
575 KeReleaseGuardedMutex(&LpcpLock);
576 LpcpCompleteWait(&WakeupThread->LpcReplySemaphore);
577
578 /* Now we can let go of the thread */
579 ObDereferenceObject(WakeupThread);
580 }
581
582 /* Now wait for someone to reply to us */
583 LpcpReceiveWait(ReceivePort->MsgQueue.Semaphore, WaitMode);
584 if (Status != STATUS_SUCCESS) goto Cleanup;
585
586 /* Wait done, get the LPC lock */
587 KeAcquireGuardedMutex(&LpcpLock);
588
589 /* Check if we've received nothing */
590 if (IsListEmpty(&ReceivePort->MsgQueue.ReceiveHead))
591 {
592 /* Check if this was a waitable port and wake it */
593 if (ReceivePort->Flags & LPCP_WAITABLE_PORT)
594 {
595 /* Reset its event */
596 KeResetEvent(&ReceivePort->WaitEvent);
597 }
598
599 /* Release the lock and fail */
600 KeReleaseGuardedMutex(&LpcpLock);
601 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
602 ObDereferenceObject(Port);
603 return STATUS_UNSUCCESSFUL;
604 }
605
606 /* Get the message on the queue */
607 Message = CONTAINING_RECORD(RemoveHeadList(&ReceivePort->
608 MsgQueue.ReceiveHead),
609 LPCP_MESSAGE,
610 Entry);
611
612 /* Check if the queue is empty now */
613 if (IsListEmpty(&ReceivePort->MsgQueue.ReceiveHead))
614 {
615 /* Check if this was a waitable port */
616 if (ReceivePort->Flags & LPCP_WAITABLE_PORT)
617 {
618 /* Reset its event */
619 KeResetEvent(&ReceivePort->WaitEvent);
620 }
621 }
622
623 /* Re-initialize the message's list entry */
624 InitializeListHead(&Message->Entry);
625
626 /* Set this as the received message */
627 Thread->LpcReceivedMessageId = Message->Request.MessageId;
628 Thread->LpcReceivedMsgIdValid = TRUE;
629
630 _SEH2_TRY
631 {
632 /* Check if this was a connection request */
633 if (LpcpGetMessageType(&Message->Request) == LPC_CONNECTION_REQUEST)
634 {
635 /* Get the connection message */
636 ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1);
637 LPCTRACE(LPC_REPLY_DEBUG,
638 "Request Messages: %p/%p\n",
639 Message,
640 ConnectMessage);
641
642 /* Get its length */
643 ConnectionInfoLength = Message->Request.u1.s1.DataLength -
644 sizeof(LPCP_CONNECTION_MESSAGE);
645
646 /* Return it as the receive message */
647 *ReceiveMessage = Message->Request;
648
649 /* Clear our stack variable so the message doesn't get freed */
650 Message = NULL;
651
652 /* Setup the receive message */
653 ReceiveMessage->u1.s1.TotalLength = (CSHORT)(sizeof(LPCP_MESSAGE) +
654 ConnectionInfoLength);
655 ReceiveMessage->u1.s1.DataLength = (CSHORT)ConnectionInfoLength;
656 RtlCopyMemory(ReceiveMessage + 1,
657 ConnectMessage + 1,
658 ConnectionInfoLength);
659
660 /* Clear the port context if the caller requested one */
661 if (PortContext) *PortContext = NULL;
662 }
663 else if (LpcpGetMessageType(&Message->Request) != LPC_REPLY)
664 {
665 /* Otherwise, this is a new message or event */
666 LPCTRACE(LPC_REPLY_DEBUG,
667 "Non-Reply Messages: %p/%p\n",
668 &Message->Request,
669 (&Message->Request) + 1);
670
671 /* Copy it */
672 LpcpMoveMessage(ReceiveMessage,
673 &Message->Request,
674 (&Message->Request) + 1,
675 0,
676 NULL);
677
678 /* Return its context */
679 if (PortContext) *PortContext = Message->PortContext;
680
681 /* And check if it has data information */
682 if (Message->Request.u2.s2.DataInfoOffset)
683 {
684 /* It does, save it, and don't free the message below */
685 LpcpSaveDataInfoMessage(Port, Message, LPCP_LOCK_HELD);
686 Message = NULL;
687 }
688 }
689 else
690 {
691 /* This is a reply message, should never happen! */
692 ASSERT(FALSE);
693 }
694 }
695 _SEH2_EXCEPT(ExSystemExceptionFilter())
696 {
697 DPRINT1("SEH crash [2]\n");
698 DbgBreakPoint();
699 Status = _SEH2_GetExceptionCode();
700 }
701 _SEH2_END;
702
703 /* Check if we have a message pointer here */
704 if (Message)
705 {
706 /* Free it and release the lock */
707 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
708 }
709 else
710 {
711 /* Just release the lock */
712 KeReleaseGuardedMutex(&LpcpLock);
713 }
714
715 Cleanup:
716 /* All done, dereference the port and return the status */
717 LPCTRACE(LPC_REPLY_DEBUG,
718 "Port: %p. Status: %d\n",
719 Port,
720 Status);
721 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
722 ObDereferenceObject(Port);
723 return Status;
724 }
725
726 /*
727 * @implemented
728 */
729 NTSTATUS
730 NTAPI
731 NtReplyWaitReceivePort(IN HANDLE PortHandle,
732 OUT PVOID *PortContext OPTIONAL,
733 IN PPORT_MESSAGE ReplyMessage OPTIONAL,
734 OUT PPORT_MESSAGE ReceiveMessage)
735 {
736 /* Call the newer API */
737 return NtReplyWaitReceivePortEx(PortHandle,
738 PortContext,
739 ReplyMessage,
740 ReceiveMessage,
741 NULL);
742 }
743
744 /*
745 * @unimplemented
746 */
747 NTSTATUS
748 NTAPI
749 NtReplyWaitReplyPort(IN HANDLE PortHandle,
750 IN PPORT_MESSAGE ReplyMessage)
751 {
752 UNIMPLEMENTED;
753 return STATUS_NOT_IMPLEMENTED;
754 }
755
756 NTSTATUS
757 NTAPI
758 LpcpCopyRequestData(
759 IN BOOLEAN Write,
760 IN HANDLE PortHandle,
761 IN PPORT_MESSAGE Message,
762 IN ULONG Index,
763 IN PVOID Buffer,
764 IN ULONG BufferLength,
765 OUT PULONG Returnlength)
766 {
767 KPROCESSOR_MODE PreviousMode;
768 PORT_MESSAGE CapturedMessage;
769 PLPCP_PORT_OBJECT Port = NULL;
770 PETHREAD ClientThread = NULL;
771 ULONG LocalReturnlength;
772 PLPCP_MESSAGE InfoMessage;
773 PLPCP_DATA_INFO DataInfo;
774 PVOID DataInfoBaseAddress;
775 NTSTATUS Status;
776 PAGED_CODE();
777
778 /* Check the previous mode */
779 PreviousMode = ExGetPreviousMode();
780 if (PreviousMode == KernelMode)
781 {
782 CapturedMessage = *Message;
783 }
784 else
785 {
786 _SEH2_TRY
787 {
788 ProbeForRead(Message, sizeof(*Message), sizeof(PVOID));
789 CapturedMessage = *Message;
790 }
791 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
792 {
793 DPRINT1("Got exception!\n");
794 return _SEH2_GetExceptionCode();
795 }
796 _SEH2_END;
797 }
798
799 /* Make sure there is any data to copy */
800 if (CapturedMessage.u2.s2.DataInfoOffset == 0)
801 {
802 return STATUS_INVALID_PARAMETER;
803 }
804
805 /* Reference the port handle */
806 Status = ObReferenceObjectByHandle(PortHandle,
807 PORT_ALL_ACCESS,
808 LpcPortObjectType,
809 PreviousMode,
810 (PVOID*)&Port,
811 NULL);
812 if (!NT_SUCCESS(Status))
813 {
814 DPRINT1("Failed to reference port handle: 0x%ls\n", Status);
815 return Status;
816 }
817
818 /* Look up the client thread */
819 Status = PsLookupProcessThreadByCid(&CapturedMessage.ClientId,
820 NULL,
821 &ClientThread);
822 if (!NT_SUCCESS(Status))
823 {
824 DPRINT1("Failed to lookup client thread for [0x%lx:0x%lx]: 0x%ls\n",
825 CapturedMessage.ClientId.UniqueProcess,
826 CapturedMessage.ClientId.UniqueThread, Status);
827 goto Cleanup;
828 }
829
830 /* Acquire the global LPC lock */
831 KeAcquireGuardedMutex(&LpcpLock);
832
833 /* Check for message id mismatch */
834 if ((ClientThread->LpcReplyMessageId != CapturedMessage.MessageId) ||
835 (CapturedMessage.MessageId == 0))
836 {
837 DPRINT1("LpcReplyMessageId mismatch: 0x%lx/0x%lx.\n",
838 ClientThread->LpcReplyMessageId, CapturedMessage.MessageId);
839 Status = STATUS_REPLY_MESSAGE_MISMATCH;
840 goto CleanupWithLock;
841 }
842
843 /* Validate the port */
844 if (!LpcpValidateClientPort(ClientThread, Port))
845 {
846 DPRINT1("LpcpValidateClientPort failed\n");
847 Status = STATUS_REPLY_MESSAGE_MISMATCH;
848 goto CleanupWithLock;
849 }
850
851 /* Find the message with the data */
852 InfoMessage = LpcpFindDataInfoMessage(Port,
853 CapturedMessage.MessageId,
854 CapturedMessage.ClientId);
855 if (InfoMessage == NULL)
856 {
857 DPRINT1("LpcpFindDataInfoMessage failed\n");
858 Status = STATUS_INVALID_PARAMETER;
859 goto CleanupWithLock;
860 }
861
862 /* Get the data info */
863 DataInfo = LpcpGetDataInfoFromMessage(&InfoMessage->Request);
864
865 /* Check if the index is within bounds */
866 if (Index >= DataInfo->NumberOfEntries)
867 {
868 DPRINT1("Message data index %lu out of bounds (%lu in msg)\n",
869 Index, DataInfo->NumberOfEntries);
870 Status = STATUS_INVALID_PARAMETER;
871 goto CleanupWithLock;
872 }
873
874 /* Check if the caller wants to read/write more data than expected */
875 if (BufferLength > DataInfo->Entries[Index].DataLength)
876 {
877 DPRINT1("Trying to read more data (%lu) than available (%lu)\n",
878 BufferLength, DataInfo->Entries[Index].DataLength);
879 Status = STATUS_INVALID_PARAMETER;
880 goto CleanupWithLock;
881 }
882
883 /* Get the data pointer */
884 DataInfoBaseAddress = DataInfo->Entries[Index].BaseAddress;
885
886 /* Release the lock */
887 KeReleaseGuardedMutex(&LpcpLock);
888
889 if (Write)
890 {
891 /* Copy data from the caller to the message sender */
892 Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
893 Buffer,
894 ClientThread->ThreadsProcess,
895 DataInfoBaseAddress,
896 BufferLength,
897 PreviousMode,
898 &LocalReturnlength);
899 }
900 else
901 {
902 /* Copy data from the message sender to the caller */
903 Status = MmCopyVirtualMemory(ClientThread->ThreadsProcess,
904 DataInfoBaseAddress,
905 PsGetCurrentProcess(),
906 Buffer,
907 BufferLength,
908 PreviousMode,
909 &LocalReturnlength);
910 }
911
912 if (!NT_SUCCESS(Status))
913 {
914 DPRINT1("MmCopyVirtualMemory failed: 0x%ls\n", Status);
915 goto Cleanup;
916 }
917
918 /* Check if the caller asked to return the copied length */
919 if (Returnlength != NULL)
920 {
921 _SEH2_TRY
922 {
923 *Returnlength = LocalReturnlength;
924 }
925 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
926 {
927 /* Ignore */
928 DPRINT1("Exception writing Returnlength, ignoring\n");
929 }
930 _SEH2_END;
931 }
932
933 Cleanup:
934
935 if (ClientThread != NULL)
936 ObDereferenceObject(ClientThread);
937
938 ObDereferenceObject(Port);
939
940 return Status;
941
942 CleanupWithLock:
943
944 /* Release the lock */
945 KeReleaseGuardedMutex(&LpcpLock);
946 goto Cleanup;
947 }
948
949 /*
950 * @unimplemented
951 */
952 NTSTATUS
953 NTAPI
954 NtReadRequestData(IN HANDLE PortHandle,
955 IN PPORT_MESSAGE Message,
956 IN ULONG Index,
957 IN PVOID Buffer,
958 IN ULONG BufferLength,
959 OUT PULONG ReturnLength)
960 {
961 /* Call the internal function */
962 return LpcpCopyRequestData(FALSE,
963 PortHandle,
964 Message,
965 Index,
966 Buffer,
967 BufferLength,
968 ReturnLength);
969 }
970
971 /*
972 * @unimplemented
973 */
974 NTSTATUS
975 NTAPI
976 NtWriteRequestData(IN HANDLE PortHandle,
977 IN PPORT_MESSAGE Message,
978 IN ULONG Index,
979 IN PVOID Buffer,
980 IN ULONG BufferLength,
981 OUT PULONG ReturnLength)
982 {
983 /* Call the internal function */
984 return LpcpCopyRequestData(TRUE,
985 PortHandle,
986 Message,
987 Index,
988 Buffer,
989 BufferLength,
990 ReturnLength);
991 }
992
993 /* EOF */