7a3cd451929492ec1bb7070105e93c32de9606a4
[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_HELD | 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_HELD | 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 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 KeLeaveCriticalRegion();
160
161 /* We're done */
162 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
163 LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", QueuePort, Message);
164 return STATUS_SUCCESS;
165 }
166
167 /* If we got here, then free the message and fail */
168 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
169 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
170 return STATUS_PORT_DISCONNECTED;
171 }
172
173 /*
174 * @implemented
175 */
176 NTSTATUS
177 NTAPI
178 LpcRequestWaitReplyPort(IN PVOID PortObject,
179 IN PPORT_MESSAGE LpcRequest,
180 OUT PPORT_MESSAGE LpcReply)
181 {
182 PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort, ConnectionPort = NULL;
183 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
184 NTSTATUS Status = STATUS_SUCCESS;
185 PLPCP_MESSAGE Message;
186 PETHREAD Thread = PsGetCurrentThread();
187 BOOLEAN Callback = FALSE;
188 PKSEMAPHORE Semaphore;
189 USHORT MessageType;
190 PAGED_CODE();
191
192 Port = (PLPCP_PORT_OBJECT)PortObject;
193
194 LPCTRACE(LPC_SEND_DEBUG,
195 "Port: %p. Messages: %p/%p. Type: %lx\n",
196 Port,
197 LpcRequest,
198 LpcReply,
199 LpcpGetMessageType(LpcRequest));
200
201 /* Check if the thread is dying */
202 if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING;
203
204 /* Check if this is an LPC Request */
205 MessageType = LpcpGetMessageType(LpcRequest);
206 switch (MessageType)
207 {
208 /* No type */
209 case 0:
210
211 /* Assume LPC request */
212 MessageType = LPC_REQUEST;
213 break;
214
215 /* LPC request callback */
216 case LPC_REQUEST:
217
218 /* This is a callback */
219 Callback = TRUE;
220 break;
221
222 /* Anything else */
223 case LPC_CLIENT_DIED:
224 case LPC_PORT_CLOSED:
225 case LPC_EXCEPTION:
226 case LPC_DEBUG_EVENT:
227 case LPC_ERROR_EVENT:
228
229 /* Nothing to do */
230 break;
231
232 default:
233
234 /* Invalid message type */
235 return STATUS_INVALID_PARAMETER;
236 }
237
238 /* Set the request type */
239 LpcRequest->u2.s2.Type = MessageType;
240
241 /* Validate the message length */
242 if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) ||
243 ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength))
244 {
245 /* Fail */
246 return STATUS_PORT_MESSAGE_TOO_LONG;
247 }
248
249 /* Allocate a message from the port zone */
250 Message = LpcpAllocateFromPortZone();
251 if (!Message)
252 {
253 /* Fail if we couldn't allocate a message */
254 return STATUS_NO_MEMORY;
255 }
256
257 /* Check if this is a callback */
258 if (Callback)
259 {
260 /* FIXME: TODO */
261 Semaphore = NULL; // we'd use the Thread Semaphore here
262 ASSERT(FALSE);
263 return STATUS_NOT_IMPLEMENTED;
264 }
265 else
266 {
267 /* No callback, just copy the message */
268 LpcpMoveMessage(&Message->Request,
269 LpcRequest,
270 LpcRequest + 1,
271 0,
272 &Thread->Cid);
273
274 /* Acquire the LPC lock */
275 KeAcquireGuardedMutex(&LpcpLock);
276
277 /* Right now clear the port context */
278 Message->PortContext = NULL;
279
280 /* Check if this is a not connection port */
281 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
282 {
283 /* We want the connected port */
284 QueuePort = Port->ConnectedPort;
285 if (!QueuePort)
286 {
287 /* We have no connected port, fail */
288 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
289 return STATUS_PORT_DISCONNECTED;
290 }
291
292 /* This will be the rundown port */
293 ReplyPort = QueuePort;
294
295 /* Check if this is a communication port */
296 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
297 {
298 /* Copy the port context and use the connection port */
299 Message->PortContext = QueuePort->PortContext;
300 ConnectionPort = QueuePort = Port->ConnectionPort;
301 if (!ConnectionPort)
302 {
303 /* Fail */
304 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
305 return STATUS_PORT_DISCONNECTED;
306 }
307 }
308 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) !=
309 LPCP_COMMUNICATION_PORT)
310 {
311 /* Use the connection port for anything but communication ports */
312 ConnectionPort = QueuePort = Port->ConnectionPort;
313 if (!ConnectionPort)
314 {
315 /* Fail */
316 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
317 return STATUS_PORT_DISCONNECTED;
318 }
319 }
320
321 /* Reference the connection port if it exists */
322 if (ConnectionPort) ObReferenceObject(ConnectionPort);
323 }
324 else
325 {
326 /* Otherwise, for a connection port, use the same port object */
327 QueuePort = ReplyPort = Port;
328 }
329
330 /* No reply thread */
331 Message->RepliedToThread = NULL;
332 Message->SenderPort = Port;
333
334 /* Generate the Message ID and set it */
335 Message->Request.MessageId = LpcpNextMessageId++;
336 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
337 Message->Request.CallbackId = 0;
338
339 /* Set the message ID for our thread now */
340 Thread->LpcReplyMessageId = Message->Request.MessageId;
341 Thread->LpcReplyMessage = NULL;
342
343 /* Insert the message in our chain */
344 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
345 InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain);
346 LpcpSetPortToThread(Thread, Port);
347
348 /* Release the lock and get the semaphore we'll use later */
349 KeEnterCriticalRegion();
350 KeReleaseGuardedMutex(&LpcpLock);
351 Semaphore = QueuePort->MsgQueue.Semaphore;
352
353 /* If this is a waitable port, wake it up */
354 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
355 {
356 /* Wake it */
357 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
358 }
359 }
360
361 /* Now release the semaphore */
362 LpcpCompleteWait(Semaphore);
363 KeLeaveCriticalRegion();
364
365 /* And let's wait for the reply */
366 LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode);
367
368 /* Acquire the LPC lock */
369 KeAcquireGuardedMutex(&LpcpLock);
370
371 /* Get the LPC Message and clear our thread's reply data */
372 Message = LpcpGetMessageFromThread(Thread);
373 Thread->LpcReplyMessage = NULL;
374 Thread->LpcReplyMessageId = 0;
375
376 /* Check if we have anything on the reply chain*/
377 if (!IsListEmpty(&Thread->LpcReplyChain))
378 {
379 /* Remove this thread and reinitialize the list */
380 RemoveEntryList(&Thread->LpcReplyChain);
381 InitializeListHead(&Thread->LpcReplyChain);
382 }
383
384 /* Release the lock */
385 KeReleaseGuardedMutex(&LpcpLock);
386
387 /* Check if we got a reply */
388 if (Status == STATUS_SUCCESS)
389 {
390 /* Check if we have a valid message */
391 if (Message)
392 {
393 LPCTRACE(LPC_SEND_DEBUG,
394 "Reply Messages: %p/%p\n",
395 &Message->Request,
396 (&Message->Request) + 1);
397
398 /* Move the message */
399 LpcpMoveMessage(LpcReply,
400 &Message->Request,
401 (&Message->Request) + 1,
402 0,
403 NULL);
404
405 /* Acquire the lock */
406 KeAcquireGuardedMutex(&LpcpLock);
407
408 /* Check if we replied to a thread */
409 if (Message->RepliedToThread)
410 {
411 /* Dereference */
412 ObDereferenceObject(Message->RepliedToThread);
413 Message->RepliedToThread = NULL;
414 }
415
416 /* Free the message */
417 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
418 }
419 else
420 {
421 /* We don't have a reply */
422 Status = STATUS_LPC_REPLY_LOST;
423 }
424 }
425 else
426 {
427 /* The wait failed, free the message */
428 if (Message) LpcpFreeToPortZone(Message, 0);
429 }
430
431 /* All done */
432 LPCTRACE(LPC_SEND_DEBUG,
433 "Port: %p. Status: %d\n",
434 Port,
435 Status);
436
437 /* Dereference the connection port */
438 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
439 return Status;
440 }
441
442 /*
443 * @implemented
444 */
445 NTSTATUS
446 NTAPI
447 NtRequestPort(IN HANDLE PortHandle,
448 IN PPORT_MESSAGE LpcRequest)
449 {
450 NTSTATUS Status;
451 PLPCP_PORT_OBJECT Port, QueuePort, ConnectionPort = NULL;
452 ULONG MessageType;
453 PLPCP_MESSAGE Message;
454 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
455 PETHREAD Thread = PsGetCurrentThread();
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_HELD | 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_HELD | 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_HELD | 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 the semaphore */
599 KeEnterCriticalRegion();
600 KeReleaseGuardedMutex(&LpcpLock);
601 LpcpCompleteWait(QueuePort->MsgQueue.Semaphore);
602
603 /* If this is a waitable port, wake it up */
604 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
605 {
606 /* Wake it */
607 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
608 }
609
610 KeLeaveCriticalRegion();
611
612 /* Dereference objects */
613 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
614 ObDereferenceObject(QueuePort);
615 ObDereferenceObject(Port);
616 LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", QueuePort, Message);
617 return STATUS_SUCCESS;
618 }
619
620 Status = STATUS_PORT_DISCONNECTED;
621
622 /* All done with a failure*/
623 LPCTRACE(LPC_SEND_DEBUG,
624 "Port: %p. Status: %d\n",
625 Port,
626 Status);
627
628 /* The wait failed, free the message */
629 if (Message) LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
630
631 ObDereferenceObject(Port);
632 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
633 return Status;
634 }
635
636 /*
637 * @implemented
638 */
639 NTSTATUS
640 NTAPI
641 NtRequestWaitReplyPort(IN HANDLE PortHandle,
642 IN PPORT_MESSAGE LpcRequest,
643 IN OUT PPORT_MESSAGE LpcReply)
644 {
645 PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort, ConnectionPort = NULL;
646 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
647 NTSTATUS Status;
648 PLPCP_MESSAGE Message;
649 PETHREAD Thread = PsGetCurrentThread();
650 BOOLEAN Callback;
651 PKSEMAPHORE Semaphore;
652 ULONG MessageType;
653 PAGED_CODE();
654 LPCTRACE(LPC_SEND_DEBUG,
655 "Handle: %p. Messages: %p/%p. Type: %lx\n",
656 PortHandle,
657 LpcRequest,
658 LpcReply,
659 LpcpGetMessageType(LpcRequest));
660
661 /* Check if the thread is dying */
662 if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING;
663
664 /* Check if this is an LPC Request */
665 if (LpcpGetMessageType(LpcRequest) == LPC_REQUEST)
666 {
667 /* Then it's a callback */
668 Callback = TRUE;
669 }
670 else if (LpcpGetMessageType(LpcRequest))
671 {
672 /* This is a not kernel-mode message */
673 return STATUS_INVALID_PARAMETER;
674 }
675 else
676 {
677 /* This is a kernel-mode message without a callback */
678 LpcRequest->u2.s2.Type |= LPC_REQUEST;
679 Callback = FALSE;
680 }
681
682 /* Get the message type */
683 MessageType = LpcRequest->u2.s2.Type;
684
685 /* Validate the length */
686 if (((ULONG)LpcRequest->u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
687 (ULONG)LpcRequest->u1.s1.TotalLength)
688 {
689 /* Fail */
690 return STATUS_INVALID_PARAMETER;
691 }
692
693 /* Reference the object */
694 Status = ObReferenceObjectByHandle(PortHandle,
695 0,
696 LpcPortObjectType,
697 PreviousMode,
698 (PVOID*)&Port,
699 NULL);
700 if (!NT_SUCCESS(Status)) return Status;
701
702 /* Validate the message length */
703 if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) ||
704 ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength))
705 {
706 /* Fail */
707 ObDereferenceObject(Port);
708 return STATUS_PORT_MESSAGE_TOO_LONG;
709 }
710
711 /* Allocate a message from the port zone */
712 Message = LpcpAllocateFromPortZone();
713 if (!Message)
714 {
715 /* Fail if we couldn't allocate a message */
716 ObDereferenceObject(Port);
717 return STATUS_NO_MEMORY;
718 }
719
720 /* Check if this is a callback */
721 if (Callback)
722 {
723 /* FIXME: TODO */
724 Semaphore = NULL; // we'd use the Thread Semaphore here
725 ASSERT(FALSE);
726 }
727 else
728 {
729 /* No callback, just copy the message */
730 _SEH2_TRY
731 {
732 /* Copy it */
733 LpcpMoveMessage(&Message->Request,
734 LpcRequest,
735 LpcRequest + 1,
736 MessageType,
737 &Thread->Cid);
738 }
739 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
740 {
741 /* Fail */
742 LpcpFreeToPortZone(Message, 0);
743 ObDereferenceObject(Port);
744 _SEH2_YIELD(return _SEH2_GetExceptionCode());
745 }
746 _SEH2_END;
747
748 /* Acquire the LPC lock */
749 KeAcquireGuardedMutex(&LpcpLock);
750
751 /* Right now clear the port context */
752 Message->PortContext = NULL;
753
754 /* Check if this is a not connection port */
755 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
756 {
757 /* We want the connected port */
758 QueuePort = Port->ConnectedPort;
759 if (!QueuePort)
760 {
761 /* We have no connected port, fail */
762 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
763 ObDereferenceObject(Port);
764 return STATUS_PORT_DISCONNECTED;
765 }
766
767 /* This will be the rundown port */
768 ReplyPort = QueuePort;
769
770 /* Check if this is a communication port */
771 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
772 {
773 /* Copy the port context and use the connection port */
774 Message->PortContext = QueuePort->PortContext;
775 ConnectionPort = QueuePort = Port->ConnectionPort;
776 if (!ConnectionPort)
777 {
778 /* Fail */
779 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
780 ObDereferenceObject(Port);
781 return STATUS_PORT_DISCONNECTED;
782 }
783 }
784 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) !=
785 LPCP_COMMUNICATION_PORT)
786 {
787 /* Use the connection port for anything but communication ports */
788 ConnectionPort = QueuePort = Port->ConnectionPort;
789 if (!ConnectionPort)
790 {
791 /* Fail */
792 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
793 ObDereferenceObject(Port);
794 return STATUS_PORT_DISCONNECTED;
795 }
796 }
797
798 /* Reference the connection port if it exists */
799 if (ConnectionPort) ObReferenceObject(ConnectionPort);
800 }
801 else
802 {
803 /* Otherwise, for a connection port, use the same port object */
804 QueuePort = ReplyPort = Port;
805 }
806
807 /* No reply thread */
808 Message->RepliedToThread = NULL;
809 Message->SenderPort = Port;
810
811 /* Generate the Message ID and set it */
812 Message->Request.MessageId = LpcpNextMessageId++;
813 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
814 Message->Request.CallbackId = 0;
815
816 /* Set the message ID for our thread now */
817 Thread->LpcReplyMessageId = Message->Request.MessageId;
818 Thread->LpcReplyMessage = NULL;
819
820 /* Insert the message in our chain */
821 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
822 InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain);
823 LpcpSetPortToThread(Thread, Port);
824
825 /* Release the lock and get the semaphore we'll use later */
826 KeEnterCriticalRegion();
827 KeReleaseGuardedMutex(&LpcpLock);
828 Semaphore = QueuePort->MsgQueue.Semaphore;
829
830 /* If this is a waitable port, wake it up */
831 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
832 {
833 /* Wake it */
834 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
835 }
836 }
837
838 /* Now release the semaphore */
839 LpcpCompleteWait(Semaphore);
840 KeLeaveCriticalRegion();
841
842 /* And let's wait for the reply */
843 LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode);
844
845 /* Acquire the LPC lock */
846 KeAcquireGuardedMutex(&LpcpLock);
847
848 /* Get the LPC Message and clear our thread's reply data */
849 Message = LpcpGetMessageFromThread(Thread);
850 Thread->LpcReplyMessage = NULL;
851 Thread->LpcReplyMessageId = 0;
852
853 /* Check if we have anything on the reply chain*/
854 if (!IsListEmpty(&Thread->LpcReplyChain))
855 {
856 /* Remove this thread and reinitialize the list */
857 RemoveEntryList(&Thread->LpcReplyChain);
858 InitializeListHead(&Thread->LpcReplyChain);
859 }
860
861 /* Release the lock */
862 KeReleaseGuardedMutex(&LpcpLock);
863
864 /* Check if we got a reply */
865 if (Status == STATUS_SUCCESS)
866 {
867 /* Check if we have a valid message */
868 if (Message)
869 {
870 LPCTRACE(LPC_SEND_DEBUG,
871 "Reply Messages: %p/%p\n",
872 &Message->Request,
873 (&Message->Request) + 1);
874
875 /* Move the message */
876 _SEH2_TRY
877 {
878 LpcpMoveMessage(LpcReply,
879 &Message->Request,
880 (&Message->Request) + 1,
881 0,
882 NULL);
883 }
884 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
885 {
886 Status = _SEH2_GetExceptionCode();
887 }
888 _SEH2_END;
889
890 /* Check if this is an LPC request with data information */
891 if ((LpcpGetMessageType(&Message->Request) == LPC_REQUEST) &&
892 (Message->Request.u2.s2.DataInfoOffset))
893 {
894 /* Save the data information */
895 LpcpSaveDataInfoMessage(Port, Message, 0);
896 }
897 else
898 {
899 /* Otherwise, just free it */
900 LpcpFreeToPortZone(Message, 0);
901 }
902 }
903 else
904 {
905 /* We don't have a reply */
906 Status = STATUS_LPC_REPLY_LOST;
907 }
908 }
909 else
910 {
911 /* The wait failed, free the message */
912 if (Message) LpcpFreeToPortZone(Message, 0);
913 }
914
915 /* All done */
916 LPCTRACE(LPC_SEND_DEBUG,
917 "Port: %p. Status: %d\n",
918 Port,
919 Status);
920 ObDereferenceObject(Port);
921 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
922 return Status;
923 }
924
925 /* EOF */