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