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
;
169 LPCTRACE(LPC_REPLY_DEBUG
,
170 "Handle: %lx. Messages: %p/%p. Context: %p\n",
176 /* If this is a system thread, then let it page out its stack */
177 if (Thread
->SystemThread
) WaitMode
= UserMode
;
179 /* Check if caller has a reply message */
182 /* Validate its length */
183 if ((ReplyMessage
->u1
.s1
.DataLength
+ sizeof(PORT_MESSAGE
)) >
184 ReplyMessage
->u1
.s1
.TotalLength
)
187 return STATUS_INVALID_PARAMETER
;
190 /* Make sure it has a valid ID */
191 if (!ReplyMessage
->MessageId
) return STATUS_INVALID_PARAMETER
;
194 /* Get the Port object */
195 Status
= ObReferenceObjectByHandle(PortHandle
,
201 if (!NT_SUCCESS(Status
)) return Status
;
203 /* Check if the caller has a reply message */
206 /* Validate its length in respect to the port object */
207 if ((ReplyMessage
->u1
.s1
.TotalLength
> Port
->MaxMessageLength
) ||
208 (ReplyMessage
->u1
.s1
.TotalLength
<= ReplyMessage
->u1
.s1
.DataLength
))
210 /* Too large, fail */
211 ObDereferenceObject(Port
);
212 return STATUS_PORT_MESSAGE_TOO_LONG
;
216 /* Check if this is anything but a client port */
217 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CLIENT_PORT
)
219 /* Check if this is the connection port */
220 if (Port
->ConnectionPort
== Port
)
223 ConnectionPort
= ReceivePort
= Port
;
224 ObReferenceObject(ConnectionPort
);
228 /* Acquire the lock */
229 KeAcquireGuardedMutex(&LpcpLock
);
232 ConnectionPort
= ReceivePort
= Port
->ConnectionPort
;
236 KeReleaseGuardedMutex(&LpcpLock
);
237 ObDereferenceObject(Port
);
238 return STATUS_PORT_DISCONNECTED
;
241 /* Release lock and reference */
242 ObReferenceObject(Port
);
243 KeReleaseGuardedMutex(&LpcpLock
);
248 /* Otherwise, use the port itself */
252 /* Check if the caller gave a reply message */
255 /* Get the ETHREAD corresponding to it */
256 Status
= PsLookupProcessThreadByCid(&ReplyMessage
->ClientId
,
259 if (!NT_SUCCESS(Status
))
261 /* No thread found, fail */
262 ObDereferenceObject(Port
);
263 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
267 /* Allocate a message from the port zone */
268 Message
= LpcpAllocateFromPortZone();
271 /* Fail if we couldn't allocate a message */
272 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
273 ObDereferenceObject(WakeupThread
);
274 ObDereferenceObject(Port
);
275 return STATUS_NO_MEMORY
;
278 /* Keep the lock acquired */
279 KeAcquireGuardedMutex(&LpcpLock
);
281 /* Make sure this is the reply the thread is waiting for */
282 if ((WakeupThread
->LpcReplyMessageId
!= ReplyMessage
->MessageId
) ||
283 ((LpcpGetMessageFromThread(WakeupThread
)) &&
284 (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread
)->
285 Request
) != LPC_REQUEST
)))
288 LpcpFreeToPortZone(Message
, 3);
289 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
290 ObDereferenceObject(WakeupThread
);
291 ObDereferenceObject(Port
);
292 return STATUS_REPLY_MESSAGE_MISMATCH
;
295 /* Copy the message */
296 LpcpMoveMessage(&Message
->Request
,
302 /* Reference the thread while we use it */
303 ObReferenceObject(WakeupThread
);
304 Message
->RepliedToThread
= WakeupThread
;
306 /* Set this as the reply message */
307 WakeupThread
->LpcReplyMessageId
= 0;
308 WakeupThread
->LpcReplyMessage
= (PVOID
)Message
;
310 /* Check if we have messages on the reply chain */
311 if (!(WakeupThread
->LpcExitThreadCalled
) &&
312 !(IsListEmpty(&WakeupThread
->LpcReplyChain
)))
314 /* Remove us from it and reinitialize it */
315 RemoveEntryList(&WakeupThread
->LpcReplyChain
);
316 InitializeListHead(&WakeupThread
->LpcReplyChain
);
319 /* Check if this is the message the thread had received */
320 if ((Thread
->LpcReceivedMsgIdValid
) &&
321 (Thread
->LpcReceivedMessageId
== ReplyMessage
->MessageId
))
323 /* Clear this data */
324 Thread
->LpcReceivedMessageId
= 0;
325 Thread
->LpcReceivedMsgIdValid
= FALSE
;
328 /* Free any data information */
329 LpcpFreeDataInfoMessage(Port
,
330 ReplyMessage
->MessageId
,
331 ReplyMessage
->CallbackId
,
332 ReplyMessage
->ClientId
);
334 /* Release the lock and release the LPC semaphore to wake up waiters */
335 KeReleaseGuardedMutex(&LpcpLock
);
336 LpcpCompleteWait(&WakeupThread
->LpcReplySemaphore
);
338 /* Now we can let go of the thread */
339 ObDereferenceObject(WakeupThread
);
342 /* Now wait for someone to reply to us */
343 LpcpReceiveWait(ReceivePort
->MsgQueue
.Semaphore
, WaitMode
);
344 if (Status
!= STATUS_SUCCESS
) goto Cleanup
;
346 /* Wait done, get the LPC lock */
347 KeAcquireGuardedMutex(&LpcpLock
);
349 /* Check if we've received nothing */
350 if (IsListEmpty(&ReceivePort
->MsgQueue
.ReceiveHead
))
352 /* Check if this was a waitable port and wake it */
353 if (ReceivePort
->Flags
& LPCP_WAITABLE_PORT
)
355 /* Reset its event */
356 KeResetEvent(&ReceivePort
->WaitEvent
);
359 /* Release the lock and fail */
360 KeReleaseGuardedMutex(&LpcpLock
);
361 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
362 ObDereferenceObject(Port
);
363 return STATUS_UNSUCCESSFUL
;
366 /* Get the message on the queue */
367 Message
= CONTAINING_RECORD(RemoveHeadList(&ReceivePort
->
368 MsgQueue
.ReceiveHead
),
372 /* Check if the queue is empty now */
373 if (IsListEmpty(&ReceivePort
->MsgQueue
.ReceiveHead
))
375 /* Check if this was a waitable port */
376 if (ReceivePort
->Flags
& LPCP_WAITABLE_PORT
)
378 /* Reset its event */
379 KeResetEvent(&ReceivePort
->WaitEvent
);
383 /* Re-initialize the message's list entry */
384 InitializeListHead(&Message
->Entry
);
386 /* Set this as the received message */
387 Thread
->LpcReceivedMessageId
= Message
->Request
.MessageId
;
388 Thread
->LpcReceivedMsgIdValid
= TRUE
;
390 /* Check if this was a connection request */
391 if (LpcpGetMessageType(&Message
->Request
) == LPC_CONNECTION_REQUEST
)
393 /* Get the connection message */
394 ConnectMessage
= (PLPCP_CONNECTION_MESSAGE
)(Message
+ 1);
395 LPCTRACE(LPC_REPLY_DEBUG
,
396 "Request Messages: %p/%p\n",
401 ConnectionInfoLength
= Message
->Request
.u1
.s1
.DataLength
-
402 sizeof(LPCP_CONNECTION_MESSAGE
);
404 /* Return it as the receive message */
405 *ReceiveMessage
= Message
->Request
;
407 /* Clear our stack variable so the message doesn't get freed */
410 /* Setup the receive message */
411 ReceiveMessage
->u1
.s1
.TotalLength
= sizeof(LPCP_MESSAGE
) +
412 ConnectionInfoLength
;
413 ReceiveMessage
->u1
.s1
.DataLength
= ConnectionInfoLength
;
414 RtlCopyMemory(ReceiveMessage
+ 1,
416 ConnectionInfoLength
);
418 /* Clear the port context if the caller requested one */
419 if (PortContext
) *PortContext
= NULL
;
421 else if (Message
->Request
.u2
.s2
.Type
!= LPC_REPLY
)
423 /* Otherwise, this is a new message or event */
424 LPCTRACE(LPC_REPLY_DEBUG
,
425 "Non-Reply Messages: %p/%p\n",
427 (&Message
->Request
) + 1);
430 LpcpMoveMessage(ReceiveMessage
,
432 (&Message
->Request
) + 1,
436 /* Return its context */
437 if (PortContext
) *PortContext
= Message
->PortContext
;
439 /* And check if it has data information */
440 if (Message
->Request
.u2
.s2
.DataInfoOffset
)
442 /* It does, save it, and don't free the message below */
443 LpcpSaveDataInfoMessage(Port
, Message
, 1);
449 /* This is a reply message, should never happen! */
453 /* Check if we have a message pointer here */
456 /* Free it and release the lock */
457 LpcpFreeToPortZone(Message
, 3);
461 /* Just release the lock */
462 KeReleaseGuardedMutex(&LpcpLock
);
466 /* All done, dereference the port and return the status */
467 LPCTRACE(LPC_REPLY_DEBUG
,
468 "Port: %p. Status: %p\n",
471 if (ConnectionPort
) ObDereferenceObject(ConnectionPort
);
472 ObDereferenceObject(Port
);
481 NtReplyWaitReceivePort(IN HANDLE PortHandle
,
482 OUT PVOID
*PortContext OPTIONAL
,
483 IN PPORT_MESSAGE ReplyMessage OPTIONAL
,
484 OUT PPORT_MESSAGE ReceiveMessage
)
486 /* Call the newer API */
487 return NtReplyWaitReceivePortEx(PortHandle
,
499 NtReplyWaitReplyPort(IN HANDLE PortHandle
,
500 IN PPORT_MESSAGE ReplyMessage
)
503 return STATUS_NOT_IMPLEMENTED
;
511 NtReadRequestData(IN HANDLE PortHandle
,
512 IN PPORT_MESSAGE Message
,
515 IN ULONG BufferLength
,
516 OUT PULONG Returnlength
)
519 return STATUS_NOT_IMPLEMENTED
;
527 NtWriteRequestData(IN HANDLE PortHandle
,
528 IN PPORT_MESSAGE Message
,
531 IN ULONG BufferLength
,
532 OUT PULONG ReturnLength
)
535 return STATUS_NOT_IMPLEMENTED
;