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 if (ReplyMessage
!= NULL
)
165 ProbeForRead(ReplyMessage
, sizeof(PORT_MESSAGE
), sizeof(ULONG
));
166 /*RtlCopyMemory(&CapturedReplyMessage, ReplyMessage, sizeof(PORT_MESSAGE));
167 ReplyMessage = &CapturedReplyMessage;*/
170 _SEH2_EXCEPT(ExSystemExceptionFilter())
172 DPRINT1("SEH crash [1]\n");
174 _SEH2_YIELD(return _SEH2_GetExceptionCode());
179 /* Validate its length */
180 if (((ULONG
)ReplyMessage
->u1
.s1
.DataLength
+ sizeof(PORT_MESSAGE
)) >
181 (ULONG
)ReplyMessage
->u1
.s1
.TotalLength
)
184 return STATUS_INVALID_PARAMETER
;
187 /* Make sure it has a valid ID */
188 if (!ReplyMessage
->MessageId
) return STATUS_INVALID_PARAMETER
;
190 /* Get the Port object */
191 Status
= ObReferenceObjectByHandle(PortHandle
,
197 if (!NT_SUCCESS(Status
)) return Status
;
199 /* Validate its length in respect to the port object */
200 if (((ULONG
)ReplyMessage
->u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
201 ((ULONG
)ReplyMessage
->u1
.s1
.TotalLength
<=
202 (ULONG
)ReplyMessage
->u1
.s1
.DataLength
))
204 /* Too large, fail */
205 ObDereferenceObject(Port
);
206 return STATUS_PORT_MESSAGE_TOO_LONG
;
209 /* Get the ETHREAD corresponding to it */
210 Status
= PsLookupProcessThreadByCid(&ReplyMessage
->ClientId
,
213 if (!NT_SUCCESS(Status
))
215 /* No thread found, fail */
216 ObDereferenceObject(Port
);
217 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
221 /* Allocate a message from the port zone */
222 Message
= LpcpAllocateFromPortZone();
225 /* Fail if we couldn't allocate a message */
226 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
227 ObDereferenceObject(WakeupThread
);
228 ObDereferenceObject(Port
);
229 return STATUS_NO_MEMORY
;
232 /* Keep the lock acquired */
233 KeAcquireGuardedMutex(&LpcpLock
);
235 /* Make sure this is the reply the thread is waiting for */
236 if ((WakeupThread
->LpcReplyMessageId
!= ReplyMessage
->MessageId
) ||
237 ((LpcpGetMessageFromThread(WakeupThread
)) &&
238 (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread
)->
239 Request
) != LPC_REQUEST
)))
242 LpcpFreeToPortZone(Message
, 3);
243 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
244 ObDereferenceObject(WakeupThread
);
245 ObDereferenceObject(Port
);
246 return STATUS_REPLY_MESSAGE_MISMATCH
;
249 /* Copy the message */
252 LpcpMoveMessage(&Message
->Request
,
258 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
261 LpcpFreeToPortZone(Message
, 3);
262 ObDereferenceObject(WakeupThread
);
263 ObDereferenceObject(Port
);
264 _SEH2_YIELD(return _SEH2_GetExceptionCode());
268 /* Reference the thread while we use it */
269 ObReferenceObject(WakeupThread
);
270 Message
->RepliedToThread
= WakeupThread
;
272 /* Set this as the reply message */
273 WakeupThread
->LpcReplyMessageId
= 0;
274 WakeupThread
->LpcReplyMessage
= (PVOID
)Message
;
276 /* Check if we have messages on the reply chain */
277 if (!(WakeupThread
->LpcExitThreadCalled
) &&
278 !(IsListEmpty(&WakeupThread
->LpcReplyChain
)))
280 /* Remove us from it and reinitialize it */
281 RemoveEntryList(&WakeupThread
->LpcReplyChain
);
282 InitializeListHead(&WakeupThread
->LpcReplyChain
);
285 /* Check if this is the message the thread had received */
286 if ((Thread
->LpcReceivedMsgIdValid
) &&
287 (Thread
->LpcReceivedMessageId
== ReplyMessage
->MessageId
))
289 /* Clear this data */
290 Thread
->LpcReceivedMessageId
= 0;
291 Thread
->LpcReceivedMsgIdValid
= FALSE
;
294 /* Free any data information */
295 LpcpFreeDataInfoMessage(Port
,
296 ReplyMessage
->MessageId
,
297 ReplyMessage
->CallbackId
,
298 ReplyMessage
->ClientId
);
300 /* Release the lock and release the LPC semaphore to wake up waiters */
301 KeReleaseGuardedMutex(&LpcpLock
);
302 LpcpCompleteWait(&WakeupThread
->LpcReplySemaphore
);
304 /* Now we can let go of the thread */
305 ObDereferenceObject(WakeupThread
);
307 /* Dereference port object */
308 ObDereferenceObject(Port
);
317 NtReplyWaitReceivePortEx(IN HANDLE PortHandle
,
318 OUT PVOID
*PortContext OPTIONAL
,
319 IN PPORT_MESSAGE ReplyMessage OPTIONAL
,
320 OUT PPORT_MESSAGE ReceiveMessage
,
321 IN PLARGE_INTEGER Timeout OPTIONAL
)
323 PLPCP_PORT_OBJECT Port
, ReceivePort
, ConnectionPort
= NULL
;
324 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode(), WaitMode
= PreviousMode
;
326 PLPCP_MESSAGE Message
;
327 PETHREAD Thread
= PsGetCurrentThread(), WakeupThread
;
328 PLPCP_CONNECTION_MESSAGE ConnectMessage
;
329 ULONG ConnectionInfoLength
;
330 //PORT_MESSAGE CapturedReplyMessage;
331 LARGE_INTEGER CapturedTimeout
;
334 LPCTRACE(LPC_REPLY_DEBUG
,
335 "Handle: %lx. Messages: %p/%p. Context: %p\n",
341 if (KeGetPreviousMode() == UserMode
)
345 if (ReplyMessage
!= NULL
)
347 ProbeForRead(ReplyMessage
, sizeof(PORT_MESSAGE
), sizeof(ULONG
));
348 /*RtlCopyMemory(&CapturedReplyMessage, ReplyMessage, sizeof(PORT_MESSAGE));
349 ReplyMessage = &CapturedReplyMessage;*/
354 ProbeForReadLargeInteger(Timeout
);
355 RtlCopyMemory(&CapturedTimeout
, Timeout
, sizeof(LARGE_INTEGER
));
356 Timeout
= &CapturedTimeout
;
359 if (PortContext
!= NULL
)
360 ProbeForWritePointer(PortContext
);
362 _SEH2_EXCEPT(ExSystemExceptionFilter())
364 DPRINT1("SEH crash [1]\n");
366 _SEH2_YIELD(return _SEH2_GetExceptionCode());
372 /* If this is a system thread, then let it page out its stack */
373 if (Thread
->SystemThread
) WaitMode
= UserMode
;
376 /* Check if caller has a reply message */
379 /* Validate its length */
380 if (((ULONG
)ReplyMessage
->u1
.s1
.DataLength
+ sizeof(PORT_MESSAGE
)) >
381 (ULONG
)ReplyMessage
->u1
.s1
.TotalLength
)
384 return STATUS_INVALID_PARAMETER
;
387 /* Make sure it has a valid ID */
388 if (!ReplyMessage
->MessageId
) return STATUS_INVALID_PARAMETER
;
391 /* Get the Port object */
392 Status
= ObReferenceObjectByHandle(PortHandle
,
398 if (!NT_SUCCESS(Status
)) return Status
;
400 /* Check if the caller has a reply message */
403 /* Validate its length in respect to the port object */
404 if (((ULONG
)ReplyMessage
->u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
405 ((ULONG
)ReplyMessage
->u1
.s1
.TotalLength
<=
406 (ULONG
)ReplyMessage
->u1
.s1
.DataLength
))
408 /* Too large, fail */
409 ObDereferenceObject(Port
);
410 return STATUS_PORT_MESSAGE_TOO_LONG
;
414 /* Check if this is anything but a client port */
415 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CLIENT_PORT
)
417 /* Check if this is the connection port */
418 if (Port
->ConnectionPort
== Port
)
421 ConnectionPort
= ReceivePort
= Port
;
422 ObReferenceObject(ConnectionPort
);
426 /* Acquire the lock */
427 KeAcquireGuardedMutex(&LpcpLock
);
430 ConnectionPort
= ReceivePort
= Port
->ConnectionPort
;
434 KeReleaseGuardedMutex(&LpcpLock
);
435 ObDereferenceObject(Port
);
436 return STATUS_PORT_DISCONNECTED
;
439 /* Release lock and reference */
440 ObReferenceObject(ConnectionPort
);
441 KeReleaseGuardedMutex(&LpcpLock
);
446 /* Otherwise, use the port itself */
450 /* Check if the caller gave a reply message */
453 /* Get the ETHREAD corresponding to it */
454 Status
= PsLookupProcessThreadByCid(&ReplyMessage
->ClientId
,
457 if (!NT_SUCCESS(Status
))
459 /* No thread found, fail */
460 ObDereferenceObject(Port
);
461 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
465 /* Allocate a message from the port zone */
466 Message
= LpcpAllocateFromPortZone();
469 /* Fail if we couldn't allocate a message */
470 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
471 ObDereferenceObject(WakeupThread
);
472 ObDereferenceObject(Port
);
473 return STATUS_NO_MEMORY
;
476 /* Keep the lock acquired */
477 KeAcquireGuardedMutex(&LpcpLock
);
479 /* Make sure this is the reply the thread is waiting for */
480 if ((WakeupThread
->LpcReplyMessageId
!= ReplyMessage
->MessageId
) ||
481 ((LpcpGetMessageFromThread(WakeupThread
)) &&
482 (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread
)->
483 Request
) != LPC_REQUEST
)))
486 LpcpFreeToPortZone(Message
, 3);
487 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
488 ObDereferenceObject(WakeupThread
);
489 ObDereferenceObject(Port
);
490 return STATUS_REPLY_MESSAGE_MISMATCH
;
493 /* Copy the message */
494 LpcpMoveMessage(&Message
->Request
,
500 /* Reference the thread while we use it */
501 ObReferenceObject(WakeupThread
);
502 Message
->RepliedToThread
= WakeupThread
;
504 /* Set this as the reply message */
505 WakeupThread
->LpcReplyMessageId
= 0;
506 WakeupThread
->LpcReplyMessage
= (PVOID
)Message
;
508 /* Check if we have messages on the reply chain */
509 if (!(WakeupThread
->LpcExitThreadCalled
) &&
510 !(IsListEmpty(&WakeupThread
->LpcReplyChain
)))
512 /* Remove us from it and reinitialize it */
513 RemoveEntryList(&WakeupThread
->LpcReplyChain
);
514 InitializeListHead(&WakeupThread
->LpcReplyChain
);
517 /* Check if this is the message the thread had received */
518 if ((Thread
->LpcReceivedMsgIdValid
) &&
519 (Thread
->LpcReceivedMessageId
== ReplyMessage
->MessageId
))
521 /* Clear this data */
522 Thread
->LpcReceivedMessageId
= 0;
523 Thread
->LpcReceivedMsgIdValid
= FALSE
;
526 /* Free any data information */
527 LpcpFreeDataInfoMessage(Port
,
528 ReplyMessage
->MessageId
,
529 ReplyMessage
->CallbackId
,
530 ReplyMessage
->ClientId
);
532 /* Release the lock and release the LPC semaphore to wake up waiters */
533 KeReleaseGuardedMutex(&LpcpLock
);
534 LpcpCompleteWait(&WakeupThread
->LpcReplySemaphore
);
536 /* Now we can let go of the thread */
537 ObDereferenceObject(WakeupThread
);
540 /* Now wait for someone to reply to us */
541 LpcpReceiveWait(ReceivePort
->MsgQueue
.Semaphore
, WaitMode
);
542 if (Status
!= STATUS_SUCCESS
) goto Cleanup
;
544 /* Wait done, get the LPC lock */
545 KeAcquireGuardedMutex(&LpcpLock
);
547 /* Check if we've received nothing */
548 if (IsListEmpty(&ReceivePort
->MsgQueue
.ReceiveHead
))
550 /* Check if this was a waitable port and wake it */
551 if (ReceivePort
->Flags
& LPCP_WAITABLE_PORT
)
553 /* Reset its event */
554 KeResetEvent(&ReceivePort
->WaitEvent
);
557 /* Release the lock and fail */
558 KeReleaseGuardedMutex(&LpcpLock
);
559 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
560 ObDereferenceObject(Port
);
561 return STATUS_UNSUCCESSFUL
;
564 /* Get the message on the queue */
565 Message
= CONTAINING_RECORD(RemoveHeadList(&ReceivePort
->
566 MsgQueue
.ReceiveHead
),
570 /* Check if the queue is empty now */
571 if (IsListEmpty(&ReceivePort
->MsgQueue
.ReceiveHead
))
573 /* Check if this was a waitable port */
574 if (ReceivePort
->Flags
& LPCP_WAITABLE_PORT
)
576 /* Reset its event */
577 KeResetEvent(&ReceivePort
->WaitEvent
);
581 /* Re-initialize the message's list entry */
582 InitializeListHead(&Message
->Entry
);
584 /* Set this as the received message */
585 Thread
->LpcReceivedMessageId
= Message
->Request
.MessageId
;
586 Thread
->LpcReceivedMsgIdValid
= TRUE
;
590 /* Check if this was a connection request */
591 if (LpcpGetMessageType(&Message
->Request
) == LPC_CONNECTION_REQUEST
)
593 /* Get the connection message */
594 ConnectMessage
= (PLPCP_CONNECTION_MESSAGE
)(Message
+ 1);
595 LPCTRACE(LPC_REPLY_DEBUG
,
596 "Request Messages: %p/%p\n",
601 ConnectionInfoLength
= Message
->Request
.u1
.s1
.DataLength
-
602 sizeof(LPCP_CONNECTION_MESSAGE
);
604 /* Return it as the receive message */
605 *ReceiveMessage
= Message
->Request
;
607 /* Clear our stack variable so the message doesn't get freed */
610 /* Setup the receive message */
611 ReceiveMessage
->u1
.s1
.TotalLength
= (CSHORT
)(sizeof(LPCP_MESSAGE
) +
612 ConnectionInfoLength
);
613 ReceiveMessage
->u1
.s1
.DataLength
= (CSHORT
)ConnectionInfoLength
;
614 RtlCopyMemory(ReceiveMessage
+ 1,
616 ConnectionInfoLength
);
618 /* Clear the port context if the caller requested one */
619 if (PortContext
) *PortContext
= NULL
;
621 else if (LpcpGetMessageType(&Message
->Request
) != LPC_REPLY
)
623 /* Otherwise, this is a new message or event */
624 LPCTRACE(LPC_REPLY_DEBUG
,
625 "Non-Reply Messages: %p/%p\n",
627 (&Message
->Request
) + 1);
630 LpcpMoveMessage(ReceiveMessage
,
632 (&Message
->Request
) + 1,
636 /* Return its context */
637 if (PortContext
) *PortContext
= Message
->PortContext
;
639 /* And check if it has data information */
640 if (Message
->Request
.u2
.s2
.DataInfoOffset
)
642 /* It does, save it, and don't free the message below */
643 LpcpSaveDataInfoMessage(Port
, Message
, 1);
649 /* This is a reply message, should never happen! */
653 _SEH2_EXCEPT(ExSystemExceptionFilter())
655 DPRINT1("SEH crash [2]\n");
657 Status
= _SEH2_GetExceptionCode();
661 /* Check if we have a message pointer here */
664 /* Free it and release the lock */
665 LpcpFreeToPortZone(Message
, 3);
669 /* Just release the lock */
670 KeReleaseGuardedMutex(&LpcpLock
);
674 /* All done, dereference the port and return the status */
675 LPCTRACE(LPC_REPLY_DEBUG
,
676 "Port: %p. Status: %p\n",
679 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
680 ObDereferenceObject(Port
);
689 NtReplyWaitReceivePort(IN HANDLE PortHandle
,
690 OUT PVOID
*PortContext OPTIONAL
,
691 IN PPORT_MESSAGE ReplyMessage OPTIONAL
,
692 OUT PPORT_MESSAGE ReceiveMessage
)
694 /* Call the newer API */
695 return NtReplyWaitReceivePortEx(PortHandle
,
707 NtReplyWaitReplyPort(IN HANDLE PortHandle
,
708 IN PPORT_MESSAGE ReplyMessage
)
711 return STATUS_NOT_IMPLEMENTED
;
719 NtReadRequestData(IN HANDLE PortHandle
,
720 IN PPORT_MESSAGE Message
,
723 IN ULONG BufferLength
,
724 OUT PULONG Returnlength
)
727 return STATUS_NOT_IMPLEMENTED
;
735 NtWriteRequestData(IN HANDLE PortHandle
,
736 IN PPORT_MESSAGE Message
,
739 IN ULONG BufferLength
,
740 OUT PULONG ReturnLength
)
743 return STATUS_NOT_IMPLEMENTED
;