72ae7dde0ec614b32ca85541f9e92caa539625ba
[reactos.git] / reactos / ntoskrnl / lpc / send.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/lpc/send.c
5 * PURPOSE: Local Procedure Call: Sending (Requests)
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 /* PUBLIC FUNCTIONS **********************************************************/
16
17 /*
18 * @implemented
19 */
20 NTSTATUS
21 NTAPI
22 LpcRequestPort(IN PVOID PortObject,
23 IN PPORT_MESSAGE LpcMessage)
24 {
25 PLPCP_PORT_OBJECT Port = PortObject, QueuePort, ConnectionPort = NULL;
26 ULONG MessageType;
27 PLPCP_MESSAGE Message;
28 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
29 PETHREAD Thread = PsGetCurrentThread();
30
31 PAGED_CODE();
32
33 LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", Port, LpcMessage);
34
35 /* Check if this is a non-datagram message */
36 if (LpcMessage->u2.s2.Type)
37 {
38 /* Get the message type */
39 MessageType = LpcpGetMessageType(LpcMessage);
40
41 /* Validate it */
42 if ((MessageType < LPC_DATAGRAM) || (MessageType > LPC_CLIENT_DIED))
43 {
44 /* Fail */
45 return STATUS_INVALID_PARAMETER;
46 }
47
48 /* Mark this as a kernel-mode message only if we really came from it */
49 if ((PreviousMode == KernelMode) &&
50 (LpcMessage->u2.s2.Type & LPC_KERNELMODE_MESSAGE))
51 {
52 /* We did, this is a kernel mode message */
53 MessageType |= LPC_KERNELMODE_MESSAGE;
54 }
55 }
56 else
57 {
58 /* This is a datagram */
59 MessageType = LPC_DATAGRAM;
60 }
61
62 /* Can't have data information on this type of call */
63 if (LpcMessage->u2.s2.DataInfoOffset) return STATUS_INVALID_PARAMETER;
64
65 /* Validate the message length */
66 if (((ULONG)LpcMessage->u1.s1.TotalLength > Port->MaxMessageLength) ||
67 ((ULONG)LpcMessage->u1.s1.TotalLength <= (ULONG)LpcMessage->u1.s1.DataLength))
68 {
69 /* Fail */
70 return STATUS_PORT_MESSAGE_TOO_LONG;
71 }
72
73 /* Allocate a new message */
74 Message = LpcpAllocateFromPortZone();
75 if (!Message) return STATUS_NO_MEMORY;
76
77 /* Clear the context */
78 Message->RepliedToThread = NULL;
79 Message->PortContext = NULL;
80
81 /* Copy the message */
82 LpcpMoveMessage(&Message->Request,
83 LpcMessage,
84 LpcMessage + 1,
85 MessageType,
86 &Thread->Cid);
87
88 /* Acquire the LPC lock */
89 KeAcquireGuardedMutex(&LpcpLock);
90
91 /* Check if this is anything but a connection port */
92 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
93 {
94 /* The queue port is the connected port */
95 QueuePort = Port->ConnectedPort;
96 if (QueuePort)
97 {
98 /* Check if this is a client port */
99 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
100 {
101 /* Then copy the context */
102 Message->PortContext = QueuePort->PortContext;
103 ConnectionPort = QueuePort = Port->ConnectionPort;
104 if (!ConnectionPort)
105 {
106 /* Fail */
107 LpcpFreeToPortZone(Message, LPCP_LOCK_OWNED | LPCP_LOCK_RELEASE);
108 return STATUS_PORT_DISCONNECTED;
109 }
110 }
111 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT)
112 {
113 /* Any other kind of port, use the connection port */
114 ConnectionPort = QueuePort = Port->ConnectionPort;
115 if (!ConnectionPort)
116 {
117 /* Fail */
118 LpcpFreeToPortZone(Message, LPCP_LOCK_OWNED | LPCP_LOCK_RELEASE);
119 return STATUS_PORT_DISCONNECTED;
120 }
121 }
122
123 /* If we have a connection port, reference it */
124 if (ConnectionPort) ObReferenceObject(ConnectionPort);
125 }
126 }
127 else
128 {
129 /* For connection ports, use the port itself */
130 QueuePort = PortObject;
131 }
132
133 /* Make sure we have a port */
134 if (QueuePort)
135 {
136 /* Generate the Message ID and set it */
137 Message->Request.MessageId = LpcpNextMessageId++;
138 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
139 Message->Request.CallbackId = 0;
140
141 /* No Message ID for the thread */
142 Thread->LpcReplyMessageId = 0;
143
144 /* Insert the message in our chain */
145 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
146
147 /* Release the lock and release the semaphore */
148 KeEnterCriticalRegion();
149 KeReleaseGuardedMutex(&LpcpLock);
150 LpcpCompleteWait(QueuePort->MsgQueue.Semaphore);
151
152 /* If this is a waitable port, wake it up */
153 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
154 {
155 /* Wake it */
156 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
157 }
158
159 /* We're done */
160 KeLeaveCriticalRegion();
161 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
162 LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", QueuePort, Message);
163 return STATUS_SUCCESS;
164 }
165
166 /* If we got here, then free the message and fail */
167 LpcpFreeToPortZone(Message, LPCP_LOCK_OWNED | LPCP_LOCK_RELEASE);
168 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
169 return STATUS_PORT_DISCONNECTED;
170 }
171
172 /*
173 * @implemented
174 */
175 NTSTATUS
176 NTAPI
177 LpcRequestWaitReplyPort(IN PVOID PortObject,
178 IN PPORT_MESSAGE LpcRequest,
179 OUT PPORT_MESSAGE LpcReply)
180 {
181 PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort, ConnectionPort = NULL;
182 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
183 NTSTATUS Status = STATUS_SUCCESS;
184 PLPCP_MESSAGE Message;
185 PETHREAD Thread = PsGetCurrentThread();
186 BOOLEAN Callback = FALSE;
187 PKSEMAPHORE Semaphore;
188 USHORT MessageType;
189 PAGED_CODE();
190
191 Port = (PLPCP_PORT_OBJECT)PortObject;
192
193 LPCTRACE(LPC_SEND_DEBUG,
194 "Port: %p. Messages: %p/%p. Type: %lx\n",
195 Port,
196 LpcRequest,
197 LpcReply,
198 LpcpGetMessageType(LpcRequest));
199
200 /* Check if the thread is dying */
201 if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING;
202
203 /* Check if this is an LPC Request */
204 MessageType = LpcpGetMessageType(LpcRequest);
205 switch (MessageType)
206 {
207 /* No type */
208 case 0:
209
210 /* Assume LPC request */
211 MessageType = LPC_REQUEST;
212 break;
213
214 /* LPC request callback */
215 case LPC_REQUEST:
216
217 /* This is a callback */
218 Callback = TRUE;
219 break;
220
221 /* Anything else */
222 case LPC_CLIENT_DIED:
223 case LPC_PORT_CLOSED:
224 case LPC_EXCEPTION:
225 case LPC_DEBUG_EVENT:
226 case LPC_ERROR_EVENT:
227
228 /* Nothing to do */
229 break;
230
231 default:
232
233 /* Invalid message type */
234 return STATUS_INVALID_PARAMETER;
235 }
236
237 /* Set the request type */
238 LpcRequest->u2.s2.Type = MessageType;
239
240 /* Validate the message length */
241 if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) ||
242 ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength))
243 {
244 /* Fail */
245 return STATUS_PORT_MESSAGE_TOO_LONG;
246 }
247
248 /* Allocate a message from the port zone */
249 Message = LpcpAllocateFromPortZone();
250 if (!Message)
251 {
252 /* Fail if we couldn't allocate a message */
253 return STATUS_NO_MEMORY;
254 }
255
256 /* Check if this is a callback */
257 if (Callback)
258 {
259 /* FIXME: TODO */
260 Semaphore = NULL; // we'd use the Thread Semaphore here
261 ASSERT(FALSE);
262 return STATUS_NOT_IMPLEMENTED;
263 }
264 else
265 {
266 /* No callback, just copy the message */
267 LpcpMoveMessage(&Message->Request,
268 LpcRequest,
269 LpcRequest + 1,
270 0,
271 &Thread->Cid);
272
273 /* Acquire the LPC lock */
274 KeAcquireGuardedMutex(&LpcpLock);
275
276 /* Right now clear the port context */
277 Message->PortContext = NULL;
278
279 /* Check if this is a not connection port */
280 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
281 {
282 /* We want the connected port */
283 QueuePort = Port->ConnectedPort;
284 if (!QueuePort)
285 {
286 /* We have no connected port, fail */
287 LpcpFreeToPortZone(Message, LPCP_LOCK_OWNED | LPCP_LOCK_RELEASE);
288 return STATUS_PORT_DISCONNECTED;
289 }
290
291 /* This will be the rundown port */
292 ReplyPort = QueuePort;
293
294 /* Check if this is a communication port */
295 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
296 {
297 /* Copy the port context and use the connection port */
298 Message->PortContext = QueuePort->PortContext;
299 ConnectionPort = QueuePort = Port->ConnectionPort;
300 if (!ConnectionPort)
301 {
302 /* Fail */
303 LpcpFreeToPortZone(Message, LPCP_LOCK_OWNED | LPCP_LOCK_RELEASE);
304 return STATUS_PORT_DISCONNECTED;
305 }
306 }
307 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) !=
308 LPCP_COMMUNICATION_PORT)
309 {
310 /* Use the connection port for anything but communication ports */
311 ConnectionPort = QueuePort = Port->ConnectionPort;
312 if (!ConnectionPort)
313 {
314 /* Fail */
315 LpcpFreeToPortZone(Message, LPCP_LOCK_OWNED | LPCP_LOCK_RELEASE);
316 return STATUS_PORT_DISCONNECTED;
317 }
318 }
319
320 /* Reference the connection port if it exists */
321 if (ConnectionPort) ObReferenceObject(ConnectionPort);
322 }
323 else
324 {
325 /* Otherwise, for a connection port, use the same port object */
326 QueuePort = ReplyPort = Port;
327 }
328
329 /* No reply thread */
330 Message->RepliedToThread = NULL;
331 Message->SenderPort = Port;
332
333 /* Generate the Message ID and set it */
334 Message->Request.MessageId = LpcpNextMessageId++;
335 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
336 Message->Request.CallbackId = 0;
337
338 /* Set the message ID for our thread now */
339 Thread->LpcReplyMessageId = Message->Request.MessageId;
340 Thread->LpcReplyMessage = NULL;
341
342 /* Insert the message in our chain */
343 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
344 InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain);
345 LpcpSetPortToThread(Thread, Port);
346
347 /* Release the lock and get the semaphore we'll use later */
348 KeEnterCriticalRegion();
349 KeReleaseGuardedMutex(&LpcpLock);
350 Semaphore = QueuePort->MsgQueue.Semaphore;
351
352 /* If this is a waitable port, wake it up */
353 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
354 {
355 /* Wake it */
356 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
357 }
358 }
359
360 /* Now release the semaphore */
361 LpcpCompleteWait(Semaphore);
362 KeLeaveCriticalRegion();
363
364 /* And let's wait for the reply */
365 LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode);
366
367 /* Acquire the LPC lock */
368 KeAcquireGuardedMutex(&LpcpLock);
369
370 /* Get the LPC Message and clear our thread's reply data */
371 Message = LpcpGetMessageFromThread(Thread);
372 Thread->LpcReplyMessage = NULL;
373 Thread->LpcReplyMessageId = 0;
374
375 /* Check if we have anything on the reply chain*/
376 if (!IsListEmpty(&Thread->LpcReplyChain))
377 {
378 /* Remove this thread and reinitialize the list */
379 RemoveEntryList(&Thread->LpcReplyChain);
380 InitializeListHead(&Thread->LpcReplyChain);
381 }
382
383 /* Release the lock */
384 KeReleaseGuardedMutex(&LpcpLock);
385
386 /* Check if we got a reply */
387 if (Status == STATUS_SUCCESS)
388 {
389 /* Check if we have a valid message */
390 if (Message)
391 {
392 LPCTRACE(LPC_SEND_DEBUG,
393 "Reply Messages: %p/%p\n",
394 &Message->Request,
395 (&Message->Request) + 1);
396
397 /* Move the message */
398 LpcpMoveMessage(LpcReply,
399 &Message->Request,
400 (&Message->Request) + 1,
401 0,
402 NULL);
403
404 /* Acquire the lock */
405 KeAcquireGuardedMutex(&LpcpLock);
406
407 /* Check if we replied to a thread */
408 if (Message->RepliedToThread)
409 {
410 /* Dereference */
411 ObDereferenceObject(Message->RepliedToThread);
412 Message->RepliedToThread = NULL;
413 }
414
415 /* Free the message */
416 LpcpFreeToPortZone(Message, LPCP_LOCK_OWNED | LPCP_LOCK_RELEASE);
417 }
418 else
419 {
420 /* We don't have a reply */
421 Status = STATUS_LPC_REPLY_LOST;
422 }
423 }
424 else
425 {
426 /* The wait failed, free the message */
427 if (Message) LpcpFreeToPortZone(Message, 0);
428 }
429
430 /* All done */
431 LPCTRACE(LPC_SEND_DEBUG,
432 "Port: %p. Status: %d\n",
433 Port,
434 Status);
435
436 /* Dereference the connection port */
437 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
438 return Status;
439 }
440
441 /*
442 * @implemented
443 */
444 NTSTATUS
445 NTAPI
446 NtRequestPort(IN HANDLE PortHandle,
447 IN PPORT_MESSAGE LpcRequest)
448 {
449 NTSTATUS Status;
450 PLPCP_PORT_OBJECT Port, QueuePort, ConnectionPort = NULL;
451 ULONG MessageType;
452 PLPCP_MESSAGE Message;
453 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
454 PETHREAD Thread = PsGetCurrentThread();
455 PKSEMAPHORE Semaphore;
456
457 PAGED_CODE();
458
459 LPCTRACE(LPC_SEND_DEBUG,
460 "Handle: %p. Message: %p. Type: %lx\n",
461 PortHandle,
462 LpcRequest,
463 LpcpGetMessageType(LpcRequest));
464
465 /* Get the message type */
466 MessageType = LpcRequest->u2.s2.Type | LPC_DATAGRAM;
467
468 /* Can't have data information on this type of call */
469 if (LpcRequest->u2.s2.DataInfoOffset) return STATUS_INVALID_PARAMETER;
470
471 /* Validate the length */
472 if (((ULONG)LpcRequest->u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
473 (ULONG)LpcRequest->u1.s1.TotalLength)
474 {
475 /* Fail */
476 return STATUS_INVALID_PARAMETER;
477 }
478
479 /* Reference the object */
480 Status = ObReferenceObjectByHandle(PortHandle,
481 0,
482 LpcPortObjectType,
483 PreviousMode,
484 (PVOID*)&Port,
485 NULL);
486 if (!NT_SUCCESS(Status)) return Status;
487
488 /* Validate the message length */
489 if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) ||
490 ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength))
491 {
492 /* Fail */
493 ObDereferenceObject(Port);
494 return STATUS_PORT_MESSAGE_TOO_LONG;
495 }
496
497 /* Allocate a message from the port zone */
498 Message = LpcpAllocateFromPortZone();
499 if (!Message)
500 {
501 /* Fail if we couldn't allocate a message */
502 ObDereferenceObject(Port);
503 return STATUS_NO_MEMORY;
504 }
505
506 /* No callback, just copy the message */
507 _SEH2_TRY
508 {
509 /* Copy it */
510 LpcpMoveMessage(&Message->Request,
511 LpcRequest,
512 LpcRequest + 1,
513 MessageType,
514 &Thread->Cid);
515 }
516 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
517 {
518 /* Fail */
519 LpcpFreeToPortZone(Message, 0);
520 ObDereferenceObject(Port);
521 _SEH2_YIELD(return _SEH2_GetExceptionCode());
522 }
523 _SEH2_END;
524
525 /* Acquire the LPC lock */
526 KeAcquireGuardedMutex(&LpcpLock);
527
528 /* Right now clear the port context */
529 Message->PortContext = NULL;
530
531 /* Check if this is a not connection port */
532 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
533 {
534 /* We want the connected port */
535 QueuePort = Port->ConnectedPort;
536 if (!QueuePort)
537 {
538 /* We have no connected port, fail */
539 LpcpFreeToPortZone(Message, LPCP_LOCK_OWNED | LPCP_LOCK_RELEASE);
540 ObDereferenceObject(Port);
541 return STATUS_PORT_DISCONNECTED;
542 }
543
544 /* Check if this is a communication port */
545 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
546 {
547 /* Copy the port context and use the connection port */
548 Message->PortContext = QueuePort->PortContext;
549 ConnectionPort = QueuePort = Port->ConnectionPort;
550 if (!ConnectionPort)
551 {
552 /* Fail */
553 LpcpFreeToPortZone(Message, LPCP_LOCK_OWNED | LPCP_LOCK_RELEASE);
554 ObDereferenceObject(Port);
555 return STATUS_PORT_DISCONNECTED;
556 }
557 }
558 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) !=
559 LPCP_COMMUNICATION_PORT)
560 {
561 /* Use the connection port for anything but communication ports */
562 ConnectionPort = QueuePort = Port->ConnectionPort;
563 if (!ConnectionPort)
564 {
565 /* Fail */
566 LpcpFreeToPortZone(Message, LPCP_LOCK_OWNED | LPCP_LOCK_RELEASE);
567 ObDereferenceObject(Port);
568 return STATUS_PORT_DISCONNECTED;
569 }
570 }
571
572 /* Reference the connection port if it exists */
573 if (ConnectionPort) ObReferenceObject(ConnectionPort);
574 }
575 else
576 {
577 /* Otherwise, for a connection port, use the same port object */
578 QueuePort = Port;
579 }
580
581 /* Reference QueuePort if we have it */
582 if (QueuePort && ObReferenceObjectSafe(QueuePort))
583 {
584 /* Set sender's port */
585 Message->SenderPort = Port;
586
587 /* Generate the Message ID and set it */
588 Message->Request.MessageId = LpcpNextMessageId++;
589 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
590 Message->Request.CallbackId = 0;
591
592 /* No Message ID for the thread */
593 Thread->LpcReplyMessageId = 0;
594
595 /* Insert the message in our chain */
596 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
597
598 /* Release the lock and get the semaphore we'll use later */
599 KeEnterCriticalRegion();
600 KeReleaseGuardedMutex(&LpcpLock);
601
602 /* Now release the semaphore */
603 Semaphore = QueuePort->MsgQueue.Semaphore;
604 LpcpCompleteWait(Semaphore);
605
606 /* If this is a waitable port, wake it up */
607 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
608 {
609 /* Wake it */
610 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
611 }
612
613 KeLeaveCriticalRegion();
614
615 /* Dereference objects */
616 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
617 ObDereferenceObject(QueuePort);
618 ObDereferenceObject(Port);
619 LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", QueuePort, Message);
620 return STATUS_SUCCESS;
621 }
622
623 Status = STATUS_PORT_DISCONNECTED;
624
625 /* All done with a failure*/
626 LPCTRACE(LPC_SEND_DEBUG,
627 "Port: %p. Status: %d\n",
628 Port,
629 Status);
630
631 /* The wait failed, free the message */
632 if (Message) LpcpFreeToPortZone(Message, LPCP_LOCK_OWNED | LPCP_LOCK_RELEASE);
633
634 ObDereferenceObject(Port);
635 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
636 return Status;
637 }
638
639 /*
640 * @implemented
641 */
642 NTSTATUS
643 NTAPI
644 NtRequestWaitReplyPort(IN HANDLE PortHandle,
645 IN PPORT_MESSAGE LpcRequest,
646 IN OUT PPORT_MESSAGE LpcReply)
647 {
648 PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort, ConnectionPort = NULL;
649 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
650 NTSTATUS Status;
651 PLPCP_MESSAGE Message;
652 PETHREAD Thread = PsGetCurrentThread();
653 BOOLEAN Callback;
654 PKSEMAPHORE Semaphore;
655 ULONG MessageType;
656 PAGED_CODE();
657 LPCTRACE(LPC_SEND_DEBUG,
658 "Handle: %p. Messages: %p/%p. Type: %lx\n",
659 PortHandle,
660 LpcRequest,
661 LpcReply,
662 LpcpGetMessageType(LpcRequest));
663
664 /* Check if the thread is dying */
665 if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING;
666
667 /* Check if this is an LPC Request */
668 if (LpcpGetMessageType(LpcRequest) == LPC_REQUEST)
669 {
670 /* Then it's a callback */
671 Callback = TRUE;
672 }
673 else if (LpcpGetMessageType(LpcRequest))
674 {
675 /* This is a not kernel-mode message */
676 return STATUS_INVALID_PARAMETER;
677 }
678 else
679 {
680 /* This is a kernel-mode message without a callback */
681 LpcRequest->u2.s2.Type |= LPC_REQUEST;
682 Callback = FALSE;
683 }
684
685 /* Get the message type */
686 MessageType = LpcRequest->u2.s2.Type;
687
688 /* Validate the length */
689 if (((ULONG)LpcRequest->u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
690 (ULONG)LpcRequest->u1.s1.TotalLength)
691 {
692 /* Fail */
693 return STATUS_INVALID_PARAMETER;
694 }
695
696 /* Reference the object */
697 Status = ObReferenceObjectByHandle(PortHandle,
698 0,
699 LpcPortObjectType,
700 PreviousMode,
701 (PVOID*)&Port,
702 NULL);
703 if (!NT_SUCCESS(Status)) return Status;
704
705 /* Validate the message length */
706 if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) ||
707 ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength))
708 {
709 /* Fail */
710 ObDereferenceObject(Port);
711 return STATUS_PORT_MESSAGE_TOO_LONG;
712 }
713
714 /* Allocate a message from the port zone */
715 Message = LpcpAllocateFromPortZone();
716 if (!Message)
717 {
718 /* Fail if we couldn't allocate a message */
719 ObDereferenceObject(Port);
720 return STATUS_NO_MEMORY;
721 }
722
723 /* Check if this is a callback */
724 if (Callback)
725 {
726 /* FIXME: TODO */
727 Semaphore = NULL; // we'd use the Thread Semaphore here
728 ASSERT(FALSE);
729 }
730 else
731 {
732 /* No callback, just copy the message */
733 _SEH2_TRY
734 {
735 /* Copy it */
736 LpcpMoveMessage(&Message->Request,
737 LpcRequest,
738 LpcRequest + 1,
739 MessageType,
740 &Thread->Cid);
741 }
742 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
743 {
744 /* Fail */
745 LpcpFreeToPortZone(Message, 0);
746 ObDereferenceObject(Port);
747 _SEH2_YIELD(return _SEH2_GetExceptionCode());
748 }
749 _SEH2_END;
750
751 /* Acquire the LPC lock */
752 KeAcquireGuardedMutex(&LpcpLock);
753
754 /* Right now clear the port context */
755 Message->PortContext = NULL;
756
757 /* Check if this is a not connection port */
758 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
759 {
760 /* We want the connected port */
761 QueuePort = Port->ConnectedPort;
762 if (!QueuePort)
763 {
764 /* We have no connected port, fail */
765 LpcpFreeToPortZone(Message, LPCP_LOCK_OWNED | LPCP_LOCK_RELEASE);
766 ObDereferenceObject(Port);
767 return STATUS_PORT_DISCONNECTED;
768 }
769
770 /* This will be the rundown port */
771 ReplyPort = QueuePort;
772
773 /* Check if this is a communication port */
774 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
775 {
776 /* Copy the port context and use the connection port */
777 Message->PortContext = QueuePort->PortContext;
778 ConnectionPort = QueuePort = Port->ConnectionPort;
779 if (!ConnectionPort)
780 {
781 /* Fail */
782 LpcpFreeToPortZone(Message, LPCP_LOCK_OWNED | LPCP_LOCK_RELEASE);
783 ObDereferenceObject(Port);
784 return STATUS_PORT_DISCONNECTED;
785 }
786 }
787 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) !=
788 LPCP_COMMUNICATION_PORT)
789 {
790 /* Use the connection port for anything but communication ports */
791 ConnectionPort = QueuePort = Port->ConnectionPort;
792 if (!ConnectionPort)
793 {
794 /* Fail */
795 LpcpFreeToPortZone(Message, LPCP_LOCK_OWNED | LPCP_LOCK_RELEASE);
796 ObDereferenceObject(Port);
797 return STATUS_PORT_DISCONNECTED;
798 }
799 }
800
801 /* Reference the connection port if it exists */
802 if (ConnectionPort) ObReferenceObject(ConnectionPort);
803 }
804 else
805 {
806 /* Otherwise, for a connection port, use the same port object */
807 QueuePort = ReplyPort = Port;
808 }
809
810 /* No reply thread */
811 Message->RepliedToThread = NULL;
812 Message->SenderPort = Port;
813
814 /* Generate the Message ID and set it */
815 Message->Request.MessageId = LpcpNextMessageId++;
816 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
817 Message->Request.CallbackId = 0;
818
819 /* Set the message ID for our thread now */
820 Thread->LpcReplyMessageId = Message->Request.MessageId;
821 Thread->LpcReplyMessage = NULL;
822
823 /* Insert the message in our chain */
824 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
825 InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain);
826 LpcpSetPortToThread(Thread, Port);
827
828 /* Release the lock and get the semaphore we'll use later */
829 KeEnterCriticalRegion();
830 KeReleaseGuardedMutex(&LpcpLock);
831 Semaphore = QueuePort->MsgQueue.Semaphore;
832
833 /* If this is a waitable port, wake it up */
834 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
835 {
836 /* Wake it */
837 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
838 }
839 }
840
841 /* Now release the semaphore */
842 LpcpCompleteWait(Semaphore);
843 KeLeaveCriticalRegion();
844
845 /* And let's wait for the reply */
846 LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode);
847
848 /* Acquire the LPC lock */
849 KeAcquireGuardedMutex(&LpcpLock);
850
851 /* Get the LPC Message and clear our thread's reply data */
852 Message = LpcpGetMessageFromThread(Thread);
853 Thread->LpcReplyMessage = NULL;
854 Thread->LpcReplyMessageId = 0;
855
856 /* Check if we have anything on the reply chain*/
857 if (!IsListEmpty(&Thread->LpcReplyChain))
858 {
859 /* Remove this thread and reinitialize the list */
860 RemoveEntryList(&Thread->LpcReplyChain);
861 InitializeListHead(&Thread->LpcReplyChain);
862 }
863
864 /* Release the lock */
865 KeReleaseGuardedMutex(&LpcpLock);
866
867 /* Check if we got a reply */
868 if (Status == STATUS_SUCCESS)
869 {
870 /* Check if we have a valid message */
871 if (Message)
872 {
873 LPCTRACE(LPC_SEND_DEBUG,
874 "Reply Messages: %p/%p\n",
875 &Message->Request,
876 (&Message->Request) + 1);
877
878 /* Move the message */
879 _SEH2_TRY
880 {
881 LpcpMoveMessage(LpcReply,
882 &Message->Request,
883 (&Message->Request) + 1,
884 0,
885 NULL);
886 }
887 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
888 {
889 Status = _SEH2_GetExceptionCode();
890 }
891 _SEH2_END;
892
893 /* Check if this is an LPC request with data information */
894 if ((LpcpGetMessageType(&Message->Request) == LPC_REQUEST) &&
895 (Message->Request.u2.s2.DataInfoOffset))
896 {
897 /* Save the data information */
898 LpcpSaveDataInfoMessage(Port, Message, 0);
899 }
900 else
901 {
902 /* Otherwise, just free it */
903 LpcpFreeToPortZone(Message, 0);
904 }
905 }
906 else
907 {
908 /* We don't have a reply */
909 Status = STATUS_LPC_REPLY_LOST;
910 }
911 }
912 else
913 {
914 /* The wait failed, free the message */
915 if (Message) LpcpFreeToPortZone(Message, 0);
916 }
917
918 /* All done */
919 LPCTRACE(LPC_SEND_DEBUG,
920 "Port: %p. Status: %d\n",
921 Port,
922 Status);
923 ObDereferenceObject(Port);
924 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
925 return Status;
926 }
927
928 /* EOF */