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)
9 /* INCLUDES ******************************************************************/
15 /* PRIVATE FUNCTIONS *********************************************************/
19 LpcpFreeDataInfoMessage(IN PLPCP_PORT_OBJECT Port
,
22 IN CLIENT_ID ClientId
)
24 PLPCP_MESSAGE Message
;
25 PLIST_ENTRY ListHead
, NextEntry
;
27 /* Check if the port we want is the connection port */
28 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) > LPCP_UNCONNECTED_PORT
)
31 Port
= Port
->ConnectionPort
;
36 ListHead
= &Port
->LpcDataInfoChainHead
;
37 NextEntry
= ListHead
->Flink
;
38 while (ListHead
!= NextEntry
)
41 Message
= CONTAINING_RECORD(NextEntry
, LPCP_MESSAGE
, Entry
);
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
))
48 /* Unlink and free it */
49 RemoveEntryList(&Message
->Entry
);
50 InitializeListHead(&Message
->Entry
);
51 LpcpFreeToPortZone(Message
, 1);
55 /* Go to the next entry */
56 NextEntry
= NextEntry
->Flink
;
62 LpcpSaveDataInfoMessage(IN PLPCP_PORT_OBJECT Port
,
63 IN PLPCP_MESSAGE Message
,
68 /* Acquire the lock */
69 if (!LockHeld
) KeAcquireGuardedMutex(&LpcpLock
);
71 /* Check if the port we want is the connection port */
72 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) > LPCP_UNCONNECTED_PORT
)
75 Port
= Port
->ConnectionPort
;
78 /* Release the lock and return */
79 if (!LockHeld
) KeReleaseGuardedMutex(&LpcpLock
);
84 /* Link the message */
85 InsertTailList(&Port
->LpcDataInfoChainHead
, &Message
->Entry
);
87 /* Release the lock */
88 if (!LockHeld
) KeReleaseGuardedMutex(&LpcpLock
);
93 LpcpMoveMessage(IN PPORT_MESSAGE Destination
,
94 IN PPORT_MESSAGE Origin
,
97 IN PCLIENT_ID ClientId
)
99 /* Set the Message size */
100 LPCTRACE((LPC_REPLY_DEBUG
| LPC_SEND_DEBUG
),
101 "Destination/Origin: %p/%p. Data: %p. Length: %lx\n",
106 Destination
->u1
.Length
= Origin
->u1
.Length
;
108 /* Set the Message Type */
109 Destination
->u2
.s2
.Type
= !MessageType
?
110 Origin
->u2
.s2
.Type
: MessageType
& 0xFFFF;
112 /* Check if we have a Client ID */
115 /* Set the Client ID */
116 Destination
->ClientId
.UniqueProcess
= ClientId
->UniqueProcess
;
117 Destination
->ClientId
.UniqueThread
= ClientId
->UniqueThread
;
121 /* Otherwise, copy it */
122 Destination
->ClientId
.UniqueProcess
= Origin
->ClientId
.UniqueProcess
;
123 Destination
->ClientId
.UniqueThread
= Origin
->ClientId
.UniqueThread
;
126 /* Copy the MessageId and ClientViewSize */
127 Destination
->MessageId
= Origin
->MessageId
;
128 Destination
->ClientViewSize
= Origin
->ClientViewSize
;
130 /* Copy the Message Data */
131 RtlCopyMemory(Destination
+ 1,
133 ((Destination
->u1
.Length
& 0xFFFF) + 3) &~3);
136 /* PUBLIC FUNCTIONS **********************************************************/
143 NtReplyPort(IN HANDLE PortHandle
,
144 IN PPORT_MESSAGE ReplyMessage
)
146 PLPCP_PORT_OBJECT Port
, ConnectionPort
= NULL
;
147 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
149 PLPCP_MESSAGE Message
;
150 PETHREAD Thread
= PsGetCurrentThread(), WakeupThread
;
151 //PORT_MESSAGE CapturedReplyMessage;
154 LPCTRACE(LPC_REPLY_DEBUG
,
155 "Handle: %lx. Message: %p.\n",
159 if (KeGetPreviousMode() == UserMode
)
163 ProbeForRead(ReplyMessage
, sizeof(PORT_MESSAGE
), sizeof(ULONG
));
164 /*RtlCopyMemory(&CapturedReplyMessage, ReplyMessage, sizeof(PORT_MESSAGE));
165 ReplyMessage = &CapturedReplyMessage;*/
167 _SEH2_EXCEPT(ExSystemExceptionFilter())
169 DPRINT1("SEH crash [1]\n");
171 _SEH2_YIELD(return _SEH2_GetExceptionCode());
176 /* Validate its length */
177 if (((ULONG
)ReplyMessage
->u1
.s1
.DataLength
+ sizeof(PORT_MESSAGE
)) >
178 (ULONG
)ReplyMessage
->u1
.s1
.TotalLength
)
181 return STATUS_INVALID_PARAMETER
;
184 /* Make sure it has a valid ID */
185 if (!ReplyMessage
->MessageId
) return STATUS_INVALID_PARAMETER
;
187 /* Get the Port object */
188 Status
= ObReferenceObjectByHandle(PortHandle
,
194 if (!NT_SUCCESS(Status
)) return Status
;
196 /* Validate its length in respect to the port object */
197 if (((ULONG
)ReplyMessage
->u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
198 ((ULONG
)ReplyMessage
->u1
.s1
.TotalLength
<=
199 (ULONG
)ReplyMessage
->u1
.s1
.DataLength
))
201 /* Too large, fail */
202 ObDereferenceObject(Port
);
203 return STATUS_PORT_MESSAGE_TOO_LONG
;
206 /* Get the ETHREAD corresponding to it */
207 Status
= PsLookupProcessThreadByCid(&ReplyMessage
->ClientId
,
210 if (!NT_SUCCESS(Status
))
212 /* No thread found, fail */
213 ObDereferenceObject(Port
);
214 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
218 /* Allocate a message from the port zone */
219 Message
= LpcpAllocateFromPortZone();
222 /* Fail if we couldn't allocate a message */
223 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
224 ObDereferenceObject(WakeupThread
);
225 ObDereferenceObject(Port
);
226 return STATUS_NO_MEMORY
;
229 /* Keep the lock acquired */
230 KeAcquireGuardedMutex(&LpcpLock
);
232 /* Make sure this is the reply the thread is waiting for */
233 if ((WakeupThread
->LpcReplyMessageId
!= ReplyMessage
->MessageId
) ||
234 ((LpcpGetMessageFromThread(WakeupThread
)) &&
235 (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread
)->
236 Request
) != LPC_REQUEST
)))
239 LpcpFreeToPortZone(Message
, 3);
240 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
241 ObDereferenceObject(WakeupThread
);
242 ObDereferenceObject(Port
);
243 return STATUS_REPLY_MESSAGE_MISMATCH
;
246 /* Copy the message */
249 LpcpMoveMessage(&Message
->Request
,
255 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
258 LpcpFreeToPortZone(Message
, 3);
259 ObDereferenceObject(WakeupThread
);
260 ObDereferenceObject(Port
);
261 _SEH2_YIELD(return _SEH2_GetExceptionCode());
265 /* Reference the thread while we use it */
266 ObReferenceObject(WakeupThread
);
267 Message
->RepliedToThread
= WakeupThread
;
269 /* Set this as the reply message */
270 WakeupThread
->LpcReplyMessageId
= 0;
271 WakeupThread
->LpcReplyMessage
= (PVOID
)Message
;
273 /* Check if we have messages on the reply chain */
274 if (!(WakeupThread
->LpcExitThreadCalled
) &&
275 !(IsListEmpty(&WakeupThread
->LpcReplyChain
)))
277 /* Remove us from it and reinitialize it */
278 RemoveEntryList(&WakeupThread
->LpcReplyChain
);
279 InitializeListHead(&WakeupThread
->LpcReplyChain
);
282 /* Check if this is the message the thread had received */
283 if ((Thread
->LpcReceivedMsgIdValid
) &&
284 (Thread
->LpcReceivedMessageId
== ReplyMessage
->MessageId
))
286 /* Clear this data */
287 Thread
->LpcReceivedMessageId
= 0;
288 Thread
->LpcReceivedMsgIdValid
= FALSE
;
291 /* Free any data information */
292 LpcpFreeDataInfoMessage(Port
,
293 ReplyMessage
->MessageId
,
294 ReplyMessage
->CallbackId
,
295 ReplyMessage
->ClientId
);
297 /* Release the lock and release the LPC semaphore to wake up waiters */
298 KeReleaseGuardedMutex(&LpcpLock
);
299 LpcpCompleteWait(&WakeupThread
->LpcReplySemaphore
);
301 /* Now we can let go of the thread */
302 ObDereferenceObject(WakeupThread
);
304 /* Dereference port object */
305 ObDereferenceObject(Port
);
314 NtReplyWaitReceivePortEx(IN HANDLE PortHandle
,
315 OUT PVOID
*PortContext OPTIONAL
,
316 IN PPORT_MESSAGE ReplyMessage OPTIONAL
,
317 OUT PPORT_MESSAGE ReceiveMessage
,
318 IN PLARGE_INTEGER Timeout OPTIONAL
)
320 PLPCP_PORT_OBJECT Port
, ReceivePort
, ConnectionPort
= NULL
;
321 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode(), WaitMode
= PreviousMode
;
323 PLPCP_MESSAGE Message
;
324 PETHREAD Thread
= PsGetCurrentThread(), WakeupThread
;
325 PLPCP_CONNECTION_MESSAGE ConnectMessage
;
326 ULONG ConnectionInfoLength
;
327 //PORT_MESSAGE CapturedReplyMessage;
328 LARGE_INTEGER CapturedTimeout
;
331 LPCTRACE(LPC_REPLY_DEBUG
,
332 "Handle: %lx. Messages: %p/%p. Context: %p\n",
338 if (KeGetPreviousMode() == UserMode
)
342 if (ReplyMessage
!= NULL
)
344 ProbeForRead(ReplyMessage
, sizeof(PORT_MESSAGE
), sizeof(ULONG
));
345 /*RtlCopyMemory(&CapturedReplyMessage, ReplyMessage, sizeof(PORT_MESSAGE));
346 ReplyMessage = &CapturedReplyMessage;*/
351 ProbeForReadLargeInteger(Timeout
);
352 RtlCopyMemory(&CapturedTimeout
, Timeout
, sizeof(LARGE_INTEGER
));
353 Timeout
= &CapturedTimeout
;
356 if (PortContext
!= NULL
)
357 ProbeForWritePointer(PortContext
);
359 _SEH2_EXCEPT(ExSystemExceptionFilter())
361 DPRINT1("SEH crash [1]\n");
363 _SEH2_YIELD(return _SEH2_GetExceptionCode());
369 /* If this is a system thread, then let it page out its stack */
370 if (Thread
->SystemThread
) WaitMode
= UserMode
;
373 /* Check if caller has a reply message */
376 /* Validate its length */
377 if (((ULONG
)ReplyMessage
->u1
.s1
.DataLength
+ sizeof(PORT_MESSAGE
)) >
378 (ULONG
)ReplyMessage
->u1
.s1
.TotalLength
)
381 return STATUS_INVALID_PARAMETER
;
384 /* Make sure it has a valid ID */
385 if (!ReplyMessage
->MessageId
) return STATUS_INVALID_PARAMETER
;
388 /* Get the Port object */
389 Status
= ObReferenceObjectByHandle(PortHandle
,
395 if (!NT_SUCCESS(Status
)) return Status
;
397 /* Check if the caller has a reply message */
400 /* Validate its length in respect to the port object */
401 if (((ULONG
)ReplyMessage
->u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
402 ((ULONG
)ReplyMessage
->u1
.s1
.TotalLength
<=
403 (ULONG
)ReplyMessage
->u1
.s1
.DataLength
))
405 /* Too large, fail */
406 ObDereferenceObject(Port
);
407 return STATUS_PORT_MESSAGE_TOO_LONG
;
411 /* Check if this is anything but a client port */
412 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CLIENT_PORT
)
414 /* Check if this is the connection port */
415 if (Port
->ConnectionPort
== Port
)
418 ConnectionPort
= ReceivePort
= Port
;
419 ObReferenceObject(ConnectionPort
);
423 /* Acquire the lock */
424 KeAcquireGuardedMutex(&LpcpLock
);
427 ConnectionPort
= ReceivePort
= Port
->ConnectionPort
;
431 KeReleaseGuardedMutex(&LpcpLock
);
432 ObDereferenceObject(Port
);
433 return STATUS_PORT_DISCONNECTED
;
436 /* Release lock and reference */
437 ObReferenceObject(ConnectionPort
);
438 KeReleaseGuardedMutex(&LpcpLock
);
443 /* Otherwise, use the port itself */
447 /* Check if the caller gave a reply message */
450 /* Get the ETHREAD corresponding to it */
451 Status
= PsLookupProcessThreadByCid(&ReplyMessage
->ClientId
,
454 if (!NT_SUCCESS(Status
))
456 /* No thread found, fail */
457 ObDereferenceObject(Port
);
458 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
462 /* Allocate a message from the port zone */
463 Message
= LpcpAllocateFromPortZone();
466 /* Fail if we couldn't allocate a message */
467 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
468 ObDereferenceObject(WakeupThread
);
469 ObDereferenceObject(Port
);
470 return STATUS_NO_MEMORY
;
473 /* Keep the lock acquired */
474 KeAcquireGuardedMutex(&LpcpLock
);
476 /* Make sure this is the reply the thread is waiting for */
477 if ((WakeupThread
->LpcReplyMessageId
!= ReplyMessage
->MessageId
) ||
478 ((LpcpGetMessageFromThread(WakeupThread
)) &&
479 (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread
)->
480 Request
) != LPC_REQUEST
)))
483 LpcpFreeToPortZone(Message
, 3);
484 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
485 ObDereferenceObject(WakeupThread
);
486 ObDereferenceObject(Port
);
487 return STATUS_REPLY_MESSAGE_MISMATCH
;
490 /* Copy the message */
491 LpcpMoveMessage(&Message
->Request
,
497 /* Reference the thread while we use it */
498 ObReferenceObject(WakeupThread
);
499 Message
->RepliedToThread
= WakeupThread
;
501 /* Set this as the reply message */
502 WakeupThread
->LpcReplyMessageId
= 0;
503 WakeupThread
->LpcReplyMessage
= (PVOID
)Message
;
505 /* Check if we have messages on the reply chain */
506 if (!(WakeupThread
->LpcExitThreadCalled
) &&
507 !(IsListEmpty(&WakeupThread
->LpcReplyChain
)))
509 /* Remove us from it and reinitialize it */
510 RemoveEntryList(&WakeupThread
->LpcReplyChain
);
511 InitializeListHead(&WakeupThread
->LpcReplyChain
);
514 /* Check if this is the message the thread had received */
515 if ((Thread
->LpcReceivedMsgIdValid
) &&
516 (Thread
->LpcReceivedMessageId
== ReplyMessage
->MessageId
))
518 /* Clear this data */
519 Thread
->LpcReceivedMessageId
= 0;
520 Thread
->LpcReceivedMsgIdValid
= FALSE
;
523 /* Free any data information */
524 LpcpFreeDataInfoMessage(Port
,
525 ReplyMessage
->MessageId
,
526 ReplyMessage
->CallbackId
,
527 ReplyMessage
->ClientId
);
529 /* Release the lock and release the LPC semaphore to wake up waiters */
530 KeReleaseGuardedMutex(&LpcpLock
);
531 LpcpCompleteWait(&WakeupThread
->LpcReplySemaphore
);
533 /* Now we can let go of the thread */
534 ObDereferenceObject(WakeupThread
);
537 /* Now wait for someone to reply to us */
538 LpcpReceiveWait(ReceivePort
->MsgQueue
.Semaphore
, WaitMode
);
539 if (Status
!= STATUS_SUCCESS
) goto Cleanup
;
541 /* Wait done, get the LPC lock */
542 KeAcquireGuardedMutex(&LpcpLock
);
544 /* Check if we've received nothing */
545 if (IsListEmpty(&ReceivePort
->MsgQueue
.ReceiveHead
))
547 /* Check if this was a waitable port and wake it */
548 if (ReceivePort
->Flags
& LPCP_WAITABLE_PORT
)
550 /* Reset its event */
551 KeResetEvent(&ReceivePort
->WaitEvent
);
554 /* Release the lock and fail */
555 KeReleaseGuardedMutex(&LpcpLock
);
556 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
557 ObDereferenceObject(Port
);
558 return STATUS_UNSUCCESSFUL
;
561 /* Get the message on the queue */
562 Message
= CONTAINING_RECORD(RemoveHeadList(&ReceivePort
->
563 MsgQueue
.ReceiveHead
),
567 /* Check if the queue is empty now */
568 if (IsListEmpty(&ReceivePort
->MsgQueue
.ReceiveHead
))
570 /* Check if this was a waitable port */
571 if (ReceivePort
->Flags
& LPCP_WAITABLE_PORT
)
573 /* Reset its event */
574 KeResetEvent(&ReceivePort
->WaitEvent
);
578 /* Re-initialize the message's list entry */
579 InitializeListHead(&Message
->Entry
);
581 /* Set this as the received message */
582 Thread
->LpcReceivedMessageId
= Message
->Request
.MessageId
;
583 Thread
->LpcReceivedMsgIdValid
= TRUE
;
587 /* Check if this was a connection request */
588 if (LpcpGetMessageType(&Message
->Request
) == LPC_CONNECTION_REQUEST
)
590 /* Get the connection message */
591 ConnectMessage
= (PLPCP_CONNECTION_MESSAGE
)(Message
+ 1);
592 LPCTRACE(LPC_REPLY_DEBUG
,
593 "Request Messages: %p/%p\n",
598 ConnectionInfoLength
= Message
->Request
.u1
.s1
.DataLength
-
599 sizeof(LPCP_CONNECTION_MESSAGE
);
601 /* Return it as the receive message */
602 *ReceiveMessage
= Message
->Request
;
604 /* Clear our stack variable so the message doesn't get freed */
607 /* Setup the receive message */
608 ReceiveMessage
->u1
.s1
.TotalLength
= (CSHORT
)(sizeof(LPCP_MESSAGE
) +
609 ConnectionInfoLength
);
610 ReceiveMessage
->u1
.s1
.DataLength
= (CSHORT
)ConnectionInfoLength
;
611 RtlCopyMemory(ReceiveMessage
+ 1,
613 ConnectionInfoLength
);
615 /* Clear the port context if the caller requested one */
616 if (PortContext
) *PortContext
= NULL
;
618 else if (LpcpGetMessageType(&Message
->Request
) != LPC_REPLY
)
620 /* Otherwise, this is a new message or event */
621 LPCTRACE(LPC_REPLY_DEBUG
,
622 "Non-Reply Messages: %p/%p\n",
624 (&Message
->Request
) + 1);
627 LpcpMoveMessage(ReceiveMessage
,
629 (&Message
->Request
) + 1,
633 /* Return its context */
634 if (PortContext
) *PortContext
= Message
->PortContext
;
636 /* And check if it has data information */
637 if (Message
->Request
.u2
.s2
.DataInfoOffset
)
639 /* It does, save it, and don't free the message below */
640 LpcpSaveDataInfoMessage(Port
, Message
, 1);
646 /* This is a reply message, should never happen! */
650 _SEH2_EXCEPT(ExSystemExceptionFilter())
652 DPRINT1("SEH crash [2]\n");
654 Status
= _SEH2_GetExceptionCode();
658 /* Check if we have a message pointer here */
661 /* Free it and release the lock */
662 LpcpFreeToPortZone(Message
, 3);
666 /* Just release the lock */
667 KeReleaseGuardedMutex(&LpcpLock
);
671 /* All done, dereference the port and return the status */
672 LPCTRACE(LPC_REPLY_DEBUG
,
673 "Port: %p. Status: %p\n",
676 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
677 ObDereferenceObject(Port
);
686 NtReplyWaitReceivePort(IN HANDLE PortHandle
,
687 OUT PVOID
*PortContext OPTIONAL
,
688 IN PPORT_MESSAGE ReplyMessage OPTIONAL
,
689 OUT PPORT_MESSAGE ReceiveMessage
)
691 /* Call the newer API */
692 return NtReplyWaitReceivePortEx(PortHandle
,
704 NtReplyWaitReplyPort(IN HANDLE PortHandle
,
705 IN PPORT_MESSAGE ReplyMessage
)
708 return STATUS_NOT_IMPLEMENTED
;
716 NtReadRequestData(IN HANDLE PortHandle
,
717 IN PPORT_MESSAGE Message
,
720 IN ULONG BufferLength
,
721 OUT PULONG Returnlength
)
724 return STATUS_NOT_IMPLEMENTED
;
732 NtWriteRequestData(IN HANDLE PortHandle
,
733 IN PPORT_MESSAGE Message
,
736 IN ULONG BufferLength
,
737 OUT PULONG ReturnLength
)
740 return STATUS_NOT_IMPLEMENTED
;