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 LpcReply
)
147 return STATUS_NOT_IMPLEMENTED
;
155 NtReplyWaitReceivePortEx(IN HANDLE PortHandle
,
156 OUT PVOID
*PortContext OPTIONAL
,
157 IN PPORT_MESSAGE ReplyMessage OPTIONAL
,
158 OUT PPORT_MESSAGE ReceiveMessage
,
159 IN PLARGE_INTEGER Timeout OPTIONAL
)
161 PLPCP_PORT_OBJECT Port
, ReceivePort
, ConnectionPort
= NULL
;
162 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode(), WaitMode
= PreviousMode
;
164 PLPCP_MESSAGE Message
;
165 PETHREAD Thread
= PsGetCurrentThread(), WakeupThread
;
166 PLPCP_CONNECTION_MESSAGE ConnectMessage
;
167 ULONG ConnectionInfoLength
;
168 //PORT_MESSAGE CapturedReplyMessage;
169 LARGE_INTEGER CapturedTimeout
;
172 LPCTRACE(LPC_REPLY_DEBUG
,
173 "Handle: %lx. Messages: %p/%p. Context: %p\n",
179 if (KeGetPreviousMode() == UserMode
)
183 if (ReplyMessage
!= NULL
)
185 ProbeForRead(ReplyMessage
, sizeof(PORT_MESSAGE
), sizeof(ULONG
));
186 /*RtlCopyMemory(&CapturedReplyMessage, ReplyMessage, sizeof(PORT_MESSAGE));
187 ReplyMessage = &CapturedReplyMessage;*/
192 ProbeForReadLargeInteger(Timeout
);
193 RtlCopyMemory(&CapturedTimeout
, Timeout
, sizeof(LARGE_INTEGER
));
194 Timeout
= &CapturedTimeout
;
197 if (PortContext
!= NULL
)
198 ProbeForWritePointer(PortContext
);
200 _SEH2_EXCEPT(ExSystemExceptionFilter())
202 DPRINT1("SEH crash [1]\n");
204 _SEH2_YIELD(return _SEH2_GetExceptionCode());
210 /* If this is a system thread, then let it page out its stack */
211 if (Thread
->SystemThread
) WaitMode
= UserMode
;
214 /* Check if caller has a reply message */
217 /* Validate its length */
218 if (((ULONG
)ReplyMessage
->u1
.s1
.DataLength
+ sizeof(PORT_MESSAGE
)) >
219 (ULONG
)ReplyMessage
->u1
.s1
.TotalLength
)
222 return STATUS_INVALID_PARAMETER
;
225 /* Make sure it has a valid ID */
226 if (!ReplyMessage
->MessageId
) return STATUS_INVALID_PARAMETER
;
229 /* Get the Port object */
230 Status
= ObReferenceObjectByHandle(PortHandle
,
236 if (!NT_SUCCESS(Status
)) return Status
;
238 /* Check if the caller has a reply message */
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
))
246 /* Too large, fail */
247 ObDereferenceObject(Port
);
248 return STATUS_PORT_MESSAGE_TOO_LONG
;
252 /* Check if this is anything but a client port */
253 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CLIENT_PORT
)
255 /* Check if this is the connection port */
256 if (Port
->ConnectionPort
== Port
)
259 ConnectionPort
= ReceivePort
= Port
;
260 ObReferenceObject(ConnectionPort
);
264 /* Acquire the lock */
265 KeAcquireGuardedMutex(&LpcpLock
);
268 ConnectionPort
= ReceivePort
= Port
->ConnectionPort
;
272 KeReleaseGuardedMutex(&LpcpLock
);
273 ObDereferenceObject(Port
);
274 return STATUS_PORT_DISCONNECTED
;
277 /* Release lock and reference */
278 ObReferenceObject(ConnectionPort
);
279 KeReleaseGuardedMutex(&LpcpLock
);
284 /* Otherwise, use the port itself */
288 /* Check if the caller gave a reply message */
291 /* Get the ETHREAD corresponding to it */
292 Status
= PsLookupProcessThreadByCid(&ReplyMessage
->ClientId
,
295 if (!NT_SUCCESS(Status
))
297 /* No thread found, fail */
298 ObDereferenceObject(Port
);
299 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
303 /* Allocate a message from the port zone */
304 Message
= LpcpAllocateFromPortZone();
307 /* Fail if we couldn't allocate a message */
308 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
309 ObDereferenceObject(WakeupThread
);
310 ObDereferenceObject(Port
);
311 return STATUS_NO_MEMORY
;
314 /* Keep the lock acquired */
315 KeAcquireGuardedMutex(&LpcpLock
);
317 /* Make sure this is the reply the thread is waiting for */
318 if ((WakeupThread
->LpcReplyMessageId
!= ReplyMessage
->MessageId
) ||
319 ((LpcpGetMessageFromThread(WakeupThread
)) &&
320 (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread
)->
321 Request
) != LPC_REQUEST
)))
324 LpcpFreeToPortZone(Message
, 3);
325 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
326 ObDereferenceObject(WakeupThread
);
327 ObDereferenceObject(Port
);
328 return STATUS_REPLY_MESSAGE_MISMATCH
;
331 /* Copy the message */
332 LpcpMoveMessage(&Message
->Request
,
338 /* Reference the thread while we use it */
339 ObReferenceObject(WakeupThread
);
340 Message
->RepliedToThread
= WakeupThread
;
342 /* Set this as the reply message */
343 WakeupThread
->LpcReplyMessageId
= 0;
344 WakeupThread
->LpcReplyMessage
= (PVOID
)Message
;
346 /* Check if we have messages on the reply chain */
347 if (!(WakeupThread
->LpcExitThreadCalled
) &&
348 !(IsListEmpty(&WakeupThread
->LpcReplyChain
)))
350 /* Remove us from it and reinitialize it */
351 RemoveEntryList(&WakeupThread
->LpcReplyChain
);
352 InitializeListHead(&WakeupThread
->LpcReplyChain
);
355 /* Check if this is the message the thread had received */
356 if ((Thread
->LpcReceivedMsgIdValid
) &&
357 (Thread
->LpcReceivedMessageId
== ReplyMessage
->MessageId
))
359 /* Clear this data */
360 Thread
->LpcReceivedMessageId
= 0;
361 Thread
->LpcReceivedMsgIdValid
= FALSE
;
364 /* Free any data information */
365 LpcpFreeDataInfoMessage(Port
,
366 ReplyMessage
->MessageId
,
367 ReplyMessage
->CallbackId
,
368 ReplyMessage
->ClientId
);
370 /* Release the lock and release the LPC semaphore to wake up waiters */
371 KeReleaseGuardedMutex(&LpcpLock
);
372 LpcpCompleteWait(&WakeupThread
->LpcReplySemaphore
);
374 /* Now we can let go of the thread */
375 ObDereferenceObject(WakeupThread
);
378 /* Now wait for someone to reply to us */
379 LpcpReceiveWait(ReceivePort
->MsgQueue
.Semaphore
, WaitMode
);
380 if (Status
!= STATUS_SUCCESS
) goto Cleanup
;
382 /* Wait done, get the LPC lock */
383 KeAcquireGuardedMutex(&LpcpLock
);
385 /* Check if we've received nothing */
386 if (IsListEmpty(&ReceivePort
->MsgQueue
.ReceiveHead
))
388 /* Check if this was a waitable port and wake it */
389 if (ReceivePort
->Flags
& LPCP_WAITABLE_PORT
)
391 /* Reset its event */
392 KeResetEvent(&ReceivePort
->WaitEvent
);
395 /* Release the lock and fail */
396 KeReleaseGuardedMutex(&LpcpLock
);
397 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
398 ObDereferenceObject(Port
);
399 return STATUS_UNSUCCESSFUL
;
402 /* Get the message on the queue */
403 Message
= CONTAINING_RECORD(RemoveHeadList(&ReceivePort
->
404 MsgQueue
.ReceiveHead
),
408 /* Check if the queue is empty now */
409 if (IsListEmpty(&ReceivePort
->MsgQueue
.ReceiveHead
))
411 /* Check if this was a waitable port */
412 if (ReceivePort
->Flags
& LPCP_WAITABLE_PORT
)
414 /* Reset its event */
415 KeResetEvent(&ReceivePort
->WaitEvent
);
419 /* Re-initialize the message's list entry */
420 InitializeListHead(&Message
->Entry
);
422 /* Set this as the received message */
423 Thread
->LpcReceivedMessageId
= Message
->Request
.MessageId
;
424 Thread
->LpcReceivedMsgIdValid
= TRUE
;
428 /* Check if this was a connection request */
429 if (LpcpGetMessageType(&Message
->Request
) == LPC_CONNECTION_REQUEST
)
431 /* Get the connection message */
432 ConnectMessage
= (PLPCP_CONNECTION_MESSAGE
)(Message
+ 1);
433 LPCTRACE(LPC_REPLY_DEBUG
,
434 "Request Messages: %p/%p\n",
439 ConnectionInfoLength
= Message
->Request
.u1
.s1
.DataLength
-
440 sizeof(LPCP_CONNECTION_MESSAGE
);
442 /* Return it as the receive message */
443 *ReceiveMessage
= Message
->Request
;
445 /* Clear our stack variable so the message doesn't get freed */
448 /* Setup the receive message */
449 ReceiveMessage
->u1
.s1
.TotalLength
= (CSHORT
)(sizeof(LPCP_MESSAGE
) +
450 ConnectionInfoLength
);
451 ReceiveMessage
->u1
.s1
.DataLength
= (CSHORT
)ConnectionInfoLength
;
452 RtlCopyMemory(ReceiveMessage
+ 1,
454 ConnectionInfoLength
);
456 /* Clear the port context if the caller requested one */
457 if (PortContext
) *PortContext
= NULL
;
459 else if (LpcpGetMessageType(&Message
->Request
) != LPC_REPLY
)
461 /* Otherwise, this is a new message or event */
462 LPCTRACE(LPC_REPLY_DEBUG
,
463 "Non-Reply Messages: %p/%p\n",
465 (&Message
->Request
) + 1);
468 LpcpMoveMessage(ReceiveMessage
,
470 (&Message
->Request
) + 1,
474 /* Return its context */
475 if (PortContext
) *PortContext
= Message
->PortContext
;
477 /* And check if it has data information */
478 if (Message
->Request
.u2
.s2
.DataInfoOffset
)
480 /* It does, save it, and don't free the message below */
481 LpcpSaveDataInfoMessage(Port
, Message
, 1);
487 /* This is a reply message, should never happen! */
491 _SEH2_EXCEPT(ExSystemExceptionFilter())
493 DPRINT1("SEH crash [2]\n");
495 Status
= _SEH2_GetExceptionCode();
499 /* Check if we have a message pointer here */
502 /* Free it and release the lock */
503 LpcpFreeToPortZone(Message
, 3);
507 /* Just release the lock */
508 KeReleaseGuardedMutex(&LpcpLock
);
512 /* All done, dereference the port and return the status */
513 LPCTRACE(LPC_REPLY_DEBUG
,
514 "Port: %p. Status: %p\n",
517 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
518 ObDereferenceObject(Port
);
527 NtReplyWaitReceivePort(IN HANDLE PortHandle
,
528 OUT PVOID
*PortContext OPTIONAL
,
529 IN PPORT_MESSAGE ReplyMessage OPTIONAL
,
530 OUT PPORT_MESSAGE ReceiveMessage
)
532 /* Call the newer API */
533 return NtReplyWaitReceivePortEx(PortHandle
,
545 NtReplyWaitReplyPort(IN HANDLE PortHandle
,
546 IN PPORT_MESSAGE ReplyMessage
)
549 return STATUS_NOT_IMPLEMENTED
;
557 NtReadRequestData(IN HANDLE PortHandle
,
558 IN PPORT_MESSAGE Message
,
561 IN ULONG BufferLength
,
562 OUT PULONG Returnlength
)
565 return STATUS_NOT_IMPLEMENTED
;
573 NtWriteRequestData(IN HANDLE PortHandle
,
574 IN PPORT_MESSAGE Message
,
577 IN ULONG BufferLength
,
578 OUT PULONG ReturnLength
)
581 return STATUS_NOT_IMPLEMENTED
;