move from branch
[reactos.git] / reactos / ntoskrnl / lpc / reply.c
1 /*
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)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* PRIVATE FUNCTIONS *********************************************************/
16
17 VOID
18 NTAPI
19 LpcpFreeDataInfoMessage(IN PLPCP_PORT_OBJECT Port,
20 IN ULONG MessageId,
21 IN ULONG CallbackId,
22 IN CLIENT_ID ClientId)
23 {
24 PLPCP_MESSAGE Message;
25 PLIST_ENTRY ListHead, NextEntry;
26
27 /* Check if the port we want is the connection port */
28 if ((Port->Flags & LPCP_PORT_TYPE_MASK) > LPCP_UNCONNECTED_PORT)
29 {
30 /* Use it */
31 Port = Port->ConnectionPort;
32 if (!Port) return;
33 }
34
35 /* Loop the list */
36 ListHead = &Port->LpcDataInfoChainHead;
37 NextEntry = ListHead->Flink;
38 while (ListHead != NextEntry)
39 {
40 /* Get the message */
41 Message = CONTAINING_RECORD(NextEntry, LPCP_MESSAGE, Entry);
42
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))
47 {
48 /* Unlink and free it */
49 RemoveEntryList(&Message->Entry);
50 InitializeListHead(&Message->Entry);
51 LpcpFreeToPortZone(Message, 1);
52 break;
53 }
54
55 /* Go to the next entry */
56 NextEntry = NextEntry->Flink;
57 }
58 }
59
60 VOID
61 NTAPI
62 LpcpSaveDataInfoMessage(IN PLPCP_PORT_OBJECT Port,
63 IN PLPCP_MESSAGE Message,
64 IN ULONG LockHeld)
65 {
66 PAGED_CODE();
67
68 /* Acquire the lock */
69 if (!LockHeld) KeAcquireGuardedMutex(&LpcpLock);
70
71 /* Check if the port we want is the connection port */
72 if ((Port->Flags & LPCP_PORT_TYPE_MASK) > LPCP_UNCONNECTED_PORT)
73 {
74 /* Use it */
75 Port = Port->ConnectionPort;
76 if (!Port)
77 {
78 /* Release the lock and return */
79 if (!LockHeld) KeReleaseGuardedMutex(&LpcpLock);
80 return;
81 }
82 }
83
84 /* Link the message */
85 InsertTailList(&Port->LpcDataInfoChainHead, &Message->Entry);
86
87 /* Release the lock */
88 if (!LockHeld) KeReleaseGuardedMutex(&LpcpLock);
89 }
90
91 VOID
92 NTAPI
93 LpcpMoveMessage(IN PPORT_MESSAGE Destination,
94 IN PPORT_MESSAGE Origin,
95 IN PVOID Data,
96 IN ULONG MessageType,
97 IN PCLIENT_ID ClientId)
98 {
99 /* Set the Message size */
100 LPCTRACE((LPC_REPLY_DEBUG | LPC_SEND_DEBUG),
101 "Destination/Origin: %p/%p. Data: %p. Length: %lx\n",
102 Destination,
103 Origin,
104 Data,
105 Origin->u1.Length);
106 Destination->u1.Length = Origin->u1.Length;
107
108 /* Set the Message Type */
109 Destination->u2.s2.Type = !MessageType ?
110 Origin->u2.s2.Type : MessageType & 0xFFFF;
111
112 /* Check if we have a Client ID */
113 if (ClientId)
114 {
115 /* Set the Client ID */
116 Destination->ClientId.UniqueProcess = ClientId->UniqueProcess;
117 Destination->ClientId.UniqueThread = ClientId->UniqueThread;
118 }
119 else
120 {
121 /* Otherwise, copy it */
122 Destination->ClientId.UniqueProcess = Origin->ClientId.UniqueProcess;
123 Destination->ClientId.UniqueThread = Origin->ClientId.UniqueThread;
124 }
125
126 /* Copy the MessageId and ClientViewSize */
127 Destination->MessageId = Origin->MessageId;
128 Destination->ClientViewSize = Origin->ClientViewSize;
129
130 /* Copy the Message Data */
131 RtlCopyMemory(Destination + 1,
132 Data,
133 ((Destination->u1.Length & 0xFFFF) + 3) &~3);
134 }
135
136 /* PUBLIC FUNCTIONS **********************************************************/
137
138 /*
139 * @unimplemented
140 */
141 NTSTATUS
142 NTAPI
143 NtReplyPort(IN HANDLE PortHandle,
144 IN PPORT_MESSAGE LpcReply)
145 {
146 UNIMPLEMENTED;
147 return STATUS_NOT_IMPLEMENTED;
148 }
149
150 /*
151 * @implemented
152 */
153 NTSTATUS
154 NTAPI
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)
160 {
161 PLPCP_PORT_OBJECT Port, ReceivePort, ConnectionPort = NULL;
162 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(), WaitMode = PreviousMode;
163 NTSTATUS Status;
164 PLPCP_MESSAGE Message;
165 PETHREAD Thread = PsGetCurrentThread(), WakeupThread;
166 PLPCP_CONNECTION_MESSAGE ConnectMessage;
167 ULONG ConnectionInfoLength;
168 PAGED_CODE();
169 LPCTRACE(LPC_REPLY_DEBUG,
170 "Handle: %lx. Messages: %p/%p. Context: %p\n",
171 PortHandle,
172 ReplyMessage,
173 ReceiveMessage,
174 PortContext);
175
176 /* If this is a system thread, then let it page out its stack */
177 if (Thread->SystemThread) WaitMode = UserMode;
178
179 /* Check if caller has a reply message */
180 if (ReplyMessage)
181 {
182 /* Validate its length */
183 if ((ReplyMessage->u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
184 ReplyMessage->u1.s1.TotalLength)
185 {
186 /* Fail */
187 return STATUS_INVALID_PARAMETER;
188 }
189
190 /* Make sure it has a valid ID */
191 if (!ReplyMessage->MessageId) return STATUS_INVALID_PARAMETER;
192 }
193
194 /* Get the Port object */
195 Status = ObReferenceObjectByHandle(PortHandle,
196 0,
197 LpcPortObjectType,
198 PreviousMode,
199 (PVOID*)&Port,
200 NULL);
201 if (!NT_SUCCESS(Status)) return Status;
202
203 /* Check if the caller has a reply message */
204 if (ReplyMessage)
205 {
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))
209 {
210 /* Too large, fail */
211 ObDereferenceObject(Port);
212 return STATUS_PORT_MESSAGE_TOO_LONG;
213 }
214 }
215
216 /* Check if this is anything but a client port */
217 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CLIENT_PORT)
218 {
219 /* Check if this is the connection port */
220 if (Port->ConnectionPort == Port)
221 {
222 /* Use this port */
223 ConnectionPort = ReceivePort = Port;
224 ObReferenceObject(ConnectionPort);
225 }
226 else
227 {
228 /* Acquire the lock */
229 KeAcquireGuardedMutex(&LpcpLock);
230
231 /* Get the port */
232 ConnectionPort = ReceivePort = Port->ConnectionPort;
233 if (!ConnectionPort)
234 {
235 /* Fail */
236 KeReleaseGuardedMutex(&LpcpLock);
237 ObDereferenceObject(Port);
238 return STATUS_PORT_DISCONNECTED;
239 }
240
241 /* Release lock and reference */
242 ObReferenceObject(Port);
243 KeReleaseGuardedMutex(&LpcpLock);
244 }
245 }
246 else
247 {
248 /* Otherwise, use the port itself */
249 ReceivePort = Port;
250 }
251
252 /* Check if the caller gave a reply message */
253 if (ReplyMessage)
254 {
255 /* Get the ETHREAD corresponding to it */
256 Status = PsLookupProcessThreadByCid(&ReplyMessage->ClientId,
257 NULL,
258 &WakeupThread);
259 if (!NT_SUCCESS(Status))
260 {
261 /* No thread found, fail */
262 ObDereferenceObject(Port);
263 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
264 return Status;
265 }
266
267 /* Allocate a message from the port zone */
268 Message = LpcpAllocateFromPortZone();
269 if (!Message)
270 {
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;
276 }
277
278 /* Keep the lock acquired */
279 KeAcquireGuardedMutex(&LpcpLock);
280
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)))
286 {
287 /* It isn't, fail */
288 LpcpFreeToPortZone(Message, 3);
289 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
290 ObDereferenceObject(WakeupThread);
291 ObDereferenceObject(Port);
292 return STATUS_REPLY_MESSAGE_MISMATCH;
293 }
294
295 /* Copy the message */
296 LpcpMoveMessage(&Message->Request,
297 ReplyMessage,
298 ReplyMessage + 1,
299 LPC_REPLY,
300 NULL);
301
302 /* Reference the thread while we use it */
303 ObReferenceObject(WakeupThread);
304 Message->RepliedToThread = WakeupThread;
305
306 /* Set this as the reply message */
307 WakeupThread->LpcReplyMessageId = 0;
308 WakeupThread->LpcReplyMessage = (PVOID)Message;
309
310 /* Check if we have messages on the reply chain */
311 if (!(WakeupThread->LpcExitThreadCalled) &&
312 !(IsListEmpty(&WakeupThread->LpcReplyChain)))
313 {
314 /* Remove us from it and reinitialize it */
315 RemoveEntryList(&WakeupThread->LpcReplyChain);
316 InitializeListHead(&WakeupThread->LpcReplyChain);
317 }
318
319 /* Check if this is the message the thread had received */
320 if ((Thread->LpcReceivedMsgIdValid) &&
321 (Thread->LpcReceivedMessageId == ReplyMessage->MessageId))
322 {
323 /* Clear this data */
324 Thread->LpcReceivedMessageId = 0;
325 Thread->LpcReceivedMsgIdValid = FALSE;
326 }
327
328 /* Free any data information */
329 LpcpFreeDataInfoMessage(Port,
330 ReplyMessage->MessageId,
331 ReplyMessage->CallbackId,
332 ReplyMessage->ClientId);
333
334 /* Release the lock and release the LPC semaphore to wake up waiters */
335 KeReleaseGuardedMutex(&LpcpLock);
336 LpcpCompleteWait(&WakeupThread->LpcReplySemaphore);
337
338 /* Now we can let go of the thread */
339 ObDereferenceObject(WakeupThread);
340 }
341
342 /* Now wait for someone to reply to us */
343 LpcpReceiveWait(ReceivePort->MsgQueue.Semaphore, WaitMode);
344 if (Status != STATUS_SUCCESS) goto Cleanup;
345
346 /* Wait done, get the LPC lock */
347 KeAcquireGuardedMutex(&LpcpLock);
348
349 /* Check if we've received nothing */
350 if (IsListEmpty(&ReceivePort->MsgQueue.ReceiveHead))
351 {
352 /* Check if this was a waitable port and wake it */
353 if (ReceivePort->Flags & LPCP_WAITABLE_PORT)
354 {
355 /* Reset its event */
356 KeResetEvent(&ReceivePort->WaitEvent);
357 }
358
359 /* Release the lock and fail */
360 KeReleaseGuardedMutex(&LpcpLock);
361 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
362 ObDereferenceObject(Port);
363 return STATUS_UNSUCCESSFUL;
364 }
365
366 /* Get the message on the queue */
367 Message = CONTAINING_RECORD(RemoveHeadList(&ReceivePort->
368 MsgQueue.ReceiveHead),
369 LPCP_MESSAGE,
370 Entry);
371
372 /* Check if the queue is empty now */
373 if (IsListEmpty(&ReceivePort->MsgQueue.ReceiveHead))
374 {
375 /* Check if this was a waitable port */
376 if (ReceivePort->Flags & LPCP_WAITABLE_PORT)
377 {
378 /* Reset its event */
379 KeResetEvent(&ReceivePort->WaitEvent);
380 }
381 }
382
383 /* Re-initialize the message's list entry */
384 InitializeListHead(&Message->Entry);
385
386 /* Set this as the received message */
387 Thread->LpcReceivedMessageId = Message->Request.MessageId;
388 Thread->LpcReceivedMsgIdValid = TRUE;
389
390 /* Check if this was a connection request */
391 if (LpcpGetMessageType(&Message->Request) == LPC_CONNECTION_REQUEST)
392 {
393 /* Get the connection message */
394 ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1);
395 LPCTRACE(LPC_REPLY_DEBUG,
396 "Request Messages: %p/%p\n",
397 Message,
398 ConnectMessage);
399
400 /* Get its length */
401 ConnectionInfoLength = Message->Request.u1.s1.DataLength -
402 sizeof(LPCP_CONNECTION_MESSAGE);
403
404 /* Return it as the receive message */
405 *ReceiveMessage = Message->Request;
406
407 /* Clear our stack variable so the message doesn't get freed */
408 Message = NULL;
409
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,
415 ConnectMessage + 1,
416 ConnectionInfoLength);
417
418 /* Clear the port context if the caller requested one */
419 if (PortContext) *PortContext = NULL;
420 }
421 else if (Message->Request.u2.s2.Type != LPC_REPLY)
422 {
423 /* Otherwise, this is a new message or event */
424 LPCTRACE(LPC_REPLY_DEBUG,
425 "Non-Reply Messages: %p/%p\n",
426 &Message->Request,
427 (&Message->Request) + 1);
428
429 /* Copy it */
430 LpcpMoveMessage(ReceiveMessage,
431 &Message->Request,
432 (&Message->Request) + 1,
433 0,
434 NULL);
435
436 /* Return its context */
437 if (PortContext) *PortContext = Message->PortContext;
438
439 /* And check if it has data information */
440 if (Message->Request.u2.s2.DataInfoOffset)
441 {
442 /* It does, save it, and don't free the message below */
443 LpcpSaveDataInfoMessage(Port, Message, 1);
444 Message = NULL;
445 }
446 }
447 else
448 {
449 /* This is a reply message, should never happen! */
450 ASSERT(FALSE);
451 }
452
453 /* Check if we have a message pointer here */
454 if (Message)
455 {
456 /* Free it and release the lock */
457 LpcpFreeToPortZone(Message, 3);
458 }
459 else
460 {
461 /* Just release the lock */
462 KeReleaseGuardedMutex(&LpcpLock);
463 }
464
465 Cleanup:
466 /* All done, dereference the port and return the status */
467 LPCTRACE(LPC_REPLY_DEBUG,
468 "Port: %p. Status: %p\n",
469 Port,
470 Status);
471 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
472 ObDereferenceObject(Port);
473 return Status;
474 }
475
476 /*
477 * @implemented
478 */
479 NTSTATUS
480 NTAPI
481 NtReplyWaitReceivePort(IN HANDLE PortHandle,
482 OUT PVOID *PortContext OPTIONAL,
483 IN PPORT_MESSAGE ReplyMessage OPTIONAL,
484 OUT PPORT_MESSAGE ReceiveMessage)
485 {
486 /* Call the newer API */
487 return NtReplyWaitReceivePortEx(PortHandle,
488 PortContext,
489 ReplyMessage,
490 ReceiveMessage,
491 NULL);
492 }
493
494 /*
495 * @unimplemented
496 */
497 NTSTATUS
498 NTAPI
499 NtReplyWaitReplyPort(IN HANDLE PortHandle,
500 IN PPORT_MESSAGE ReplyMessage)
501 {
502 UNIMPLEMENTED;
503 return STATUS_NOT_IMPLEMENTED;
504 }
505
506 /*
507 * @unimplemented
508 */
509 NTSTATUS
510 NTAPI
511 NtReadRequestData(IN HANDLE PortHandle,
512 IN PPORT_MESSAGE Message,
513 IN ULONG Index,
514 IN PVOID Buffer,
515 IN ULONG BufferLength,
516 OUT PULONG Returnlength)
517 {
518 UNIMPLEMENTED;
519 return STATUS_NOT_IMPLEMENTED;
520 }
521
522 /*
523 * @unimplemented
524 */
525 NTSTATUS
526 NTAPI
527 NtWriteRequestData(IN HANDLE PortHandle,
528 IN PPORT_MESSAGE Message,
529 IN ULONG Index,
530 IN PVOID Buffer,
531 IN ULONG BufferLength,
532 OUT PULONG ReturnLength)
533 {
534 UNIMPLEMENTED;
535 return STATUS_NOT_IMPLEMENTED;
536 }
537
538 /* EOF */