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
, LPCP_LOCK_HELD
);
55 /* Go to the next entry */
56 NextEntry
= NextEntry
->Flink
;
62 LpcpSaveDataInfoMessage(IN PLPCP_PORT_OBJECT Port
,
63 IN PLPCP_MESSAGE Message
,
66 BOOLEAN LockHeld
= (LockFlags
& LPCP_LOCK_HELD
);
70 /* Acquire the lock */
71 if (!LockHeld
) KeAcquireGuardedMutex(&LpcpLock
);
73 /* Check if the port we want is the connection port */
74 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) > LPCP_UNCONNECTED_PORT
)
77 Port
= Port
->ConnectionPort
;
80 /* Release the lock and return */
81 if (!LockHeld
) KeReleaseGuardedMutex(&LpcpLock
);
86 /* Link the message */
87 InsertTailList(&Port
->LpcDataInfoChainHead
, &Message
->Entry
);
89 /* Release the lock */
90 if (!LockHeld
) KeReleaseGuardedMutex(&LpcpLock
);
95 LpcpMoveMessage(IN PPORT_MESSAGE Destination
,
96 IN PPORT_MESSAGE Origin
,
99 IN PCLIENT_ID ClientId
)
101 /* Set the Message size */
102 LPCTRACE((LPC_REPLY_DEBUG
| LPC_SEND_DEBUG
),
103 "Destination/Origin: %p/%p. Data: %p. Length: %lx\n",
108 Destination
->u1
.Length
= Origin
->u1
.Length
;
110 /* Set the Message Type */
111 Destination
->u2
.s2
.Type
= !MessageType
?
112 Origin
->u2
.s2
.Type
: MessageType
& 0xFFFF;
114 /* Check if we have a Client ID */
117 /* Set the Client ID */
118 Destination
->ClientId
.UniqueProcess
= ClientId
->UniqueProcess
;
119 Destination
->ClientId
.UniqueThread
= ClientId
->UniqueThread
;
123 /* Otherwise, copy it */
124 Destination
->ClientId
.UniqueProcess
= Origin
->ClientId
.UniqueProcess
;
125 Destination
->ClientId
.UniqueThread
= Origin
->ClientId
.UniqueThread
;
128 /* Copy the MessageId and ClientViewSize */
129 Destination
->MessageId
= Origin
->MessageId
;
130 Destination
->ClientViewSize
= Origin
->ClientViewSize
;
132 /* Copy the Message Data */
133 RtlCopyMemory(Destination
+ 1,
135 ((Destination
->u1
.Length
& 0xFFFF) + 3) &~3);
138 /* PUBLIC FUNCTIONS **********************************************************/
145 NtReplyPort(IN HANDLE PortHandle
,
146 IN PPORT_MESSAGE ReplyMessage
)
148 PLPCP_PORT_OBJECT Port
, ConnectionPort
= NULL
;
149 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
151 PLPCP_MESSAGE Message
;
152 PETHREAD Thread
= PsGetCurrentThread(), WakeupThread
;
153 //PORT_MESSAGE CapturedReplyMessage;
156 LPCTRACE(LPC_REPLY_DEBUG
,
157 "Handle: %p. Message: %p.\n",
161 if (KeGetPreviousMode() == UserMode
)
165 ProbeForRead(ReplyMessage
, sizeof(PORT_MESSAGE
), sizeof(ULONG
));
166 /*RtlCopyMemory(&CapturedReplyMessage, ReplyMessage, sizeof(PORT_MESSAGE));
167 ReplyMessage = &CapturedReplyMessage;*/
169 _SEH2_EXCEPT(ExSystemExceptionFilter())
171 DPRINT1("SEH crash [1]\n");
173 _SEH2_YIELD(return _SEH2_GetExceptionCode());
178 /* Validate its length */
179 if (((ULONG
)ReplyMessage
->u1
.s1
.DataLength
+ sizeof(PORT_MESSAGE
)) >
180 (ULONG
)ReplyMessage
->u1
.s1
.TotalLength
)
183 return STATUS_INVALID_PARAMETER
;
186 /* Make sure it has a valid ID */
187 if (!ReplyMessage
->MessageId
) return STATUS_INVALID_PARAMETER
;
189 /* Get the Port object */
190 Status
= ObReferenceObjectByHandle(PortHandle
,
196 if (!NT_SUCCESS(Status
)) return Status
;
198 /* Validate its length in respect to the port object */
199 if (((ULONG
)ReplyMessage
->u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
200 ((ULONG
)ReplyMessage
->u1
.s1
.TotalLength
<=
201 (ULONG
)ReplyMessage
->u1
.s1
.DataLength
))
203 /* Too large, fail */
204 ObDereferenceObject(Port
);
205 return STATUS_PORT_MESSAGE_TOO_LONG
;
208 /* Get the ETHREAD corresponding to it */
209 Status
= PsLookupProcessThreadByCid(&ReplyMessage
->ClientId
,
212 if (!NT_SUCCESS(Status
))
214 /* No thread found, fail */
215 ObDereferenceObject(Port
);
216 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
220 /* Allocate a message from the port zone */
221 Message
= LpcpAllocateFromPortZone();
224 /* Fail if we couldn't allocate a message */
225 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
226 ObDereferenceObject(WakeupThread
);
227 ObDereferenceObject(Port
);
228 return STATUS_NO_MEMORY
;
231 /* Keep the lock acquired */
232 KeAcquireGuardedMutex(&LpcpLock
);
234 /* Make sure this is the reply the thread is waiting for */
235 if ((WakeupThread
->LpcReplyMessageId
!= ReplyMessage
->MessageId
) ||
236 ((LpcpGetMessageFromThread(WakeupThread
)) &&
237 (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread
)->
238 Request
) != LPC_REQUEST
)))
241 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
242 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
243 ObDereferenceObject(WakeupThread
);
244 ObDereferenceObject(Port
);
245 return STATUS_REPLY_MESSAGE_MISMATCH
;
248 /* Copy the message */
251 LpcpMoveMessage(&Message
->Request
,
257 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
260 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
261 ObDereferenceObject(WakeupThread
);
262 ObDereferenceObject(Port
);
263 _SEH2_YIELD(return _SEH2_GetExceptionCode());
267 /* Reference the thread while we use it */
268 ObReferenceObject(WakeupThread
);
269 Message
->RepliedToThread
= WakeupThread
;
271 /* Set this as the reply message */
272 WakeupThread
->LpcReplyMessageId
= 0;
273 WakeupThread
->LpcReplyMessage
= (PVOID
)Message
;
275 /* Check if we have messages on the reply chain */
276 if (!(WakeupThread
->LpcExitThreadCalled
) &&
277 !(IsListEmpty(&WakeupThread
->LpcReplyChain
)))
279 /* Remove us from it and reinitialize it */
280 RemoveEntryList(&WakeupThread
->LpcReplyChain
);
281 InitializeListHead(&WakeupThread
->LpcReplyChain
);
284 /* Check if this is the message the thread had received */
285 if ((Thread
->LpcReceivedMsgIdValid
) &&
286 (Thread
->LpcReceivedMessageId
== ReplyMessage
->MessageId
))
288 /* Clear this data */
289 Thread
->LpcReceivedMessageId
= 0;
290 Thread
->LpcReceivedMsgIdValid
= FALSE
;
293 /* Free any data information */
294 LpcpFreeDataInfoMessage(Port
,
295 ReplyMessage
->MessageId
,
296 ReplyMessage
->CallbackId
,
297 ReplyMessage
->ClientId
);
299 /* Release the lock and release the LPC semaphore to wake up waiters */
300 KeReleaseGuardedMutex(&LpcpLock
);
301 LpcpCompleteWait(&WakeupThread
->LpcReplySemaphore
);
303 /* Now we can let go of the thread */
304 ObDereferenceObject(WakeupThread
);
306 /* Dereference port object */
307 ObDereferenceObject(Port
);
316 NtReplyWaitReceivePortEx(IN HANDLE PortHandle
,
317 OUT PVOID
*PortContext OPTIONAL
,
318 IN PPORT_MESSAGE ReplyMessage OPTIONAL
,
319 OUT PPORT_MESSAGE ReceiveMessage
,
320 IN PLARGE_INTEGER Timeout OPTIONAL
)
322 PLPCP_PORT_OBJECT Port
, ReceivePort
, ConnectionPort
= NULL
;
323 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode(), WaitMode
= PreviousMode
;
325 PLPCP_MESSAGE Message
;
326 PETHREAD Thread
= PsGetCurrentThread(), WakeupThread
;
327 PLPCP_CONNECTION_MESSAGE ConnectMessage
;
328 ULONG ConnectionInfoLength
;
329 //PORT_MESSAGE CapturedReplyMessage;
330 LARGE_INTEGER CapturedTimeout
;
333 LPCTRACE(LPC_REPLY_DEBUG
,
334 "Handle: %p. Messages: %p/%p. Context: %p\n",
340 if (KeGetPreviousMode() == UserMode
)
344 if (ReplyMessage
!= NULL
)
346 ProbeForRead(ReplyMessage
, sizeof(PORT_MESSAGE
), sizeof(ULONG
));
347 /*RtlCopyMemory(&CapturedReplyMessage, ReplyMessage, sizeof(PORT_MESSAGE));
348 ReplyMessage = &CapturedReplyMessage;*/
353 ProbeForReadLargeInteger(Timeout
);
354 RtlCopyMemory(&CapturedTimeout
, Timeout
, sizeof(LARGE_INTEGER
));
355 Timeout
= &CapturedTimeout
;
358 if (PortContext
!= NULL
)
359 ProbeForWritePointer(PortContext
);
361 _SEH2_EXCEPT(ExSystemExceptionFilter())
363 DPRINT1("SEH crash [1]\n");
365 _SEH2_YIELD(return _SEH2_GetExceptionCode());
371 /* If this is a system thread, then let it page out its stack */
372 if (Thread
->SystemThread
) WaitMode
= UserMode
;
375 /* Check if caller has a reply message */
378 /* Validate its length */
379 if (((ULONG
)ReplyMessage
->u1
.s1
.DataLength
+ sizeof(PORT_MESSAGE
)) >
380 (ULONG
)ReplyMessage
->u1
.s1
.TotalLength
)
383 return STATUS_INVALID_PARAMETER
;
386 /* Make sure it has a valid ID */
387 if (!ReplyMessage
->MessageId
) return STATUS_INVALID_PARAMETER
;
390 /* Get the Port object */
391 Status
= ObReferenceObjectByHandle(PortHandle
,
397 if (!NT_SUCCESS(Status
)) return Status
;
399 /* Check if the caller has a reply message */
402 /* Validate its length in respect to the port object */
403 if (((ULONG
)ReplyMessage
->u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
404 ((ULONG
)ReplyMessage
->u1
.s1
.TotalLength
<=
405 (ULONG
)ReplyMessage
->u1
.s1
.DataLength
))
407 /* Too large, fail */
408 ObDereferenceObject(Port
);
409 return STATUS_PORT_MESSAGE_TOO_LONG
;
413 /* Check if this is anything but a client port */
414 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CLIENT_PORT
)
416 /* Check if this is the connection port */
417 if (Port
->ConnectionPort
== Port
)
420 ConnectionPort
= ReceivePort
= Port
;
421 ObReferenceObject(ConnectionPort
);
425 /* Acquire the lock */
426 KeAcquireGuardedMutex(&LpcpLock
);
429 ConnectionPort
= ReceivePort
= Port
->ConnectionPort
;
433 KeReleaseGuardedMutex(&LpcpLock
);
434 ObDereferenceObject(Port
);
435 return STATUS_PORT_DISCONNECTED
;
438 /* Release lock and reference */
439 ObReferenceObject(ConnectionPort
);
440 KeReleaseGuardedMutex(&LpcpLock
);
445 /* Otherwise, use the port itself */
449 /* Check if the caller gave a reply message */
452 /* Get the ETHREAD corresponding to it */
453 Status
= PsLookupProcessThreadByCid(&ReplyMessage
->ClientId
,
456 if (!NT_SUCCESS(Status
))
458 /* No thread found, fail */
459 ObDereferenceObject(Port
);
460 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
464 /* Allocate a message from the port zone */
465 Message
= LpcpAllocateFromPortZone();
468 /* Fail if we couldn't allocate a message */
469 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
470 ObDereferenceObject(WakeupThread
);
471 ObDereferenceObject(Port
);
472 return STATUS_NO_MEMORY
;
475 /* Keep the lock acquired */
476 KeAcquireGuardedMutex(&LpcpLock
);
478 /* Make sure this is the reply the thread is waiting for */
479 if ((WakeupThread
->LpcReplyMessageId
!= ReplyMessage
->MessageId
) ||
480 ((LpcpGetMessageFromThread(WakeupThread
)) &&
481 (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread
)->
482 Request
) != LPC_REQUEST
)))
485 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
486 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
487 ObDereferenceObject(WakeupThread
);
488 ObDereferenceObject(Port
);
489 return STATUS_REPLY_MESSAGE_MISMATCH
;
492 /* Copy the message */
493 LpcpMoveMessage(&Message
->Request
,
499 /* Reference the thread while we use it */
500 ObReferenceObject(WakeupThread
);
501 Message
->RepliedToThread
= WakeupThread
;
503 /* Set this as the reply message */
504 WakeupThread
->LpcReplyMessageId
= 0;
505 WakeupThread
->LpcReplyMessage
= (PVOID
)Message
;
507 /* Check if we have messages on the reply chain */
508 if (!(WakeupThread
->LpcExitThreadCalled
) &&
509 !(IsListEmpty(&WakeupThread
->LpcReplyChain
)))
511 /* Remove us from it and reinitialize it */
512 RemoveEntryList(&WakeupThread
->LpcReplyChain
);
513 InitializeListHead(&WakeupThread
->LpcReplyChain
);
516 /* Check if this is the message the thread had received */
517 if ((Thread
->LpcReceivedMsgIdValid
) &&
518 (Thread
->LpcReceivedMessageId
== ReplyMessage
->MessageId
))
520 /* Clear this data */
521 Thread
->LpcReceivedMessageId
= 0;
522 Thread
->LpcReceivedMsgIdValid
= FALSE
;
525 /* Free any data information */
526 LpcpFreeDataInfoMessage(Port
,
527 ReplyMessage
->MessageId
,
528 ReplyMessage
->CallbackId
,
529 ReplyMessage
->ClientId
);
531 /* Release the lock and release the LPC semaphore to wake up waiters */
532 KeReleaseGuardedMutex(&LpcpLock
);
533 LpcpCompleteWait(&WakeupThread
->LpcReplySemaphore
);
535 /* Now we can let go of the thread */
536 ObDereferenceObject(WakeupThread
);
539 /* Now wait for someone to reply to us */
540 LpcpReceiveWait(ReceivePort
->MsgQueue
.Semaphore
, WaitMode
);
541 if (Status
!= STATUS_SUCCESS
) goto Cleanup
;
543 /* Wait done, get the LPC lock */
544 KeAcquireGuardedMutex(&LpcpLock
);
546 /* Check if we've received nothing */
547 if (IsListEmpty(&ReceivePort
->MsgQueue
.ReceiveHead
))
549 /* Check if this was a waitable port and wake it */
550 if (ReceivePort
->Flags
& LPCP_WAITABLE_PORT
)
552 /* Reset its event */
553 KeResetEvent(&ReceivePort
->WaitEvent
);
556 /* Release the lock and fail */
557 KeReleaseGuardedMutex(&LpcpLock
);
558 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
559 ObDereferenceObject(Port
);
560 return STATUS_UNSUCCESSFUL
;
563 /* Get the message on the queue */
564 Message
= CONTAINING_RECORD(RemoveHeadList(&ReceivePort
->
565 MsgQueue
.ReceiveHead
),
569 /* Check if the queue is empty now */
570 if (IsListEmpty(&ReceivePort
->MsgQueue
.ReceiveHead
))
572 /* Check if this was a waitable port */
573 if (ReceivePort
->Flags
& LPCP_WAITABLE_PORT
)
575 /* Reset its event */
576 KeResetEvent(&ReceivePort
->WaitEvent
);
580 /* Re-initialize the message's list entry */
581 InitializeListHead(&Message
->Entry
);
583 /* Set this as the received message */
584 Thread
->LpcReceivedMessageId
= Message
->Request
.MessageId
;
585 Thread
->LpcReceivedMsgIdValid
= TRUE
;
589 /* Check if this was a connection request */
590 if (LpcpGetMessageType(&Message
->Request
) == LPC_CONNECTION_REQUEST
)
592 /* Get the connection message */
593 ConnectMessage
= (PLPCP_CONNECTION_MESSAGE
)(Message
+ 1);
594 LPCTRACE(LPC_REPLY_DEBUG
,
595 "Request Messages: %p/%p\n",
600 ConnectionInfoLength
= Message
->Request
.u1
.s1
.DataLength
-
601 sizeof(LPCP_CONNECTION_MESSAGE
);
603 /* Return it as the receive message */
604 *ReceiveMessage
= Message
->Request
;
606 /* Clear our stack variable so the message doesn't get freed */
609 /* Setup the receive message */
610 ReceiveMessage
->u1
.s1
.TotalLength
= (CSHORT
)(sizeof(LPCP_MESSAGE
) +
611 ConnectionInfoLength
);
612 ReceiveMessage
->u1
.s1
.DataLength
= (CSHORT
)ConnectionInfoLength
;
613 RtlCopyMemory(ReceiveMessage
+ 1,
615 ConnectionInfoLength
);
617 /* Clear the port context if the caller requested one */
618 if (PortContext
) *PortContext
= NULL
;
620 else if (LpcpGetMessageType(&Message
->Request
) != LPC_REPLY
)
622 /* Otherwise, this is a new message or event */
623 LPCTRACE(LPC_REPLY_DEBUG
,
624 "Non-Reply Messages: %p/%p\n",
626 (&Message
->Request
) + 1);
629 LpcpMoveMessage(ReceiveMessage
,
631 (&Message
->Request
) + 1,
635 /* Return its context */
636 if (PortContext
) *PortContext
= Message
->PortContext
;
638 /* And check if it has data information */
639 if (Message
->Request
.u2
.s2
.DataInfoOffset
)
641 /* It does, save it, and don't free the message below */
642 LpcpSaveDataInfoMessage(Port
, Message
, LPCP_LOCK_HELD
);
648 /* This is a reply message, should never happen! */
652 _SEH2_EXCEPT(ExSystemExceptionFilter())
654 DPRINT1("SEH crash [2]\n");
656 Status
= _SEH2_GetExceptionCode();
660 /* Check if we have a message pointer here */
663 /* Free it and release the lock */
664 LpcpFreeToPortZone(Message
, LPCP_LOCK_HELD
| LPCP_LOCK_RELEASE
);
668 /* Just release the lock */
669 KeReleaseGuardedMutex(&LpcpLock
);
673 /* All done, dereference the port and return the status */
674 LPCTRACE(LPC_REPLY_DEBUG
,
675 "Port: %p. Status: %d\n",
678 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
679 ObDereferenceObject(Port
);
688 NtReplyWaitReceivePort(IN HANDLE PortHandle
,
689 OUT PVOID
*PortContext OPTIONAL
,
690 IN PPORT_MESSAGE ReplyMessage OPTIONAL
,
691 OUT PPORT_MESSAGE ReceiveMessage
)
693 /* Call the newer API */
694 return NtReplyWaitReceivePortEx(PortHandle
,
706 NtReplyWaitReplyPort(IN HANDLE PortHandle
,
707 IN PPORT_MESSAGE ReplyMessage
)
710 return STATUS_NOT_IMPLEMENTED
;
718 NtReadRequestData(IN HANDLE PortHandle
,
719 IN PPORT_MESSAGE Message
,
722 IN ULONG BufferLength
,
723 OUT PULONG Returnlength
)
726 return STATUS_NOT_IMPLEMENTED
;
734 NtWriteRequestData(IN HANDLE PortHandle
,
735 IN PPORT_MESSAGE Message
,
738 IN ULONG BufferLength
,
739 OUT PULONG ReturnLength
)
742 return STATUS_NOT_IMPLEMENTED
;