[ROSTESTS]
[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 * @implemented
140 */
141 NTSTATUS
142 NTAPI
143 NtReplyPort(IN HANDLE PortHandle,
144 IN PPORT_MESSAGE ReplyMessage)
145 {
146 PLPCP_PORT_OBJECT Port, ConnectionPort = NULL;
147 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
148 NTSTATUS Status;
149 PLPCP_MESSAGE Message;
150 PETHREAD Thread = PsGetCurrentThread(), WakeupThread;
151 //PORT_MESSAGE CapturedReplyMessage;
152
153 PAGED_CODE();
154 LPCTRACE(LPC_REPLY_DEBUG,
155 "Handle: %lx. Message: %p.\n",
156 PortHandle,
157 ReplyMessage);
158
159 if (KeGetPreviousMode() == UserMode)
160 {
161 _SEH2_TRY
162 {
163 if (ReplyMessage != NULL)
164 {
165 ProbeForRead(ReplyMessage, sizeof(PORT_MESSAGE), sizeof(ULONG));
166 /*RtlCopyMemory(&CapturedReplyMessage, ReplyMessage, sizeof(PORT_MESSAGE));
167 ReplyMessage = &CapturedReplyMessage;*/
168 }
169 }
170 _SEH2_EXCEPT(ExSystemExceptionFilter())
171 {
172 DPRINT1("SEH crash [1]\n");
173 DbgBreakPoint();
174 _SEH2_YIELD(return _SEH2_GetExceptionCode());
175 }
176 _SEH2_END;
177 }
178
179 /* Validate its length */
180 if (((ULONG)ReplyMessage->u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
181 (ULONG)ReplyMessage->u1.s1.TotalLength)
182 {
183 /* Fail */
184 return STATUS_INVALID_PARAMETER;
185 }
186
187 /* Make sure it has a valid ID */
188 if (!ReplyMessage->MessageId) return STATUS_INVALID_PARAMETER;
189
190 /* Get the Port object */
191 Status = ObReferenceObjectByHandle(PortHandle,
192 0,
193 LpcPortObjectType,
194 PreviousMode,
195 (PVOID*)&Port,
196 NULL);
197 if (!NT_SUCCESS(Status)) return Status;
198
199 /* Validate its length in respect to the port object */
200 if (((ULONG)ReplyMessage->u1.s1.TotalLength > Port->MaxMessageLength) ||
201 ((ULONG)ReplyMessage->u1.s1.TotalLength <=
202 (ULONG)ReplyMessage->u1.s1.DataLength))
203 {
204 /* Too large, fail */
205 ObDereferenceObject(Port);
206 return STATUS_PORT_MESSAGE_TOO_LONG;
207 }
208
209 /* Get the ETHREAD corresponding to it */
210 Status = PsLookupProcessThreadByCid(&ReplyMessage->ClientId,
211 NULL,
212 &WakeupThread);
213 if (!NT_SUCCESS(Status))
214 {
215 /* No thread found, fail */
216 ObDereferenceObject(Port);
217 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
218 return Status;
219 }
220
221 /* Allocate a message from the port zone */
222 Message = LpcpAllocateFromPortZone();
223 if (!Message)
224 {
225 /* Fail if we couldn't allocate a message */
226 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
227 ObDereferenceObject(WakeupThread);
228 ObDereferenceObject(Port);
229 return STATUS_NO_MEMORY;
230 }
231
232 /* Keep the lock acquired */
233 KeAcquireGuardedMutex(&LpcpLock);
234
235 /* Make sure this is the reply the thread is waiting for */
236 if ((WakeupThread->LpcReplyMessageId != ReplyMessage->MessageId) ||
237 ((LpcpGetMessageFromThread(WakeupThread)) &&
238 (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread)->
239 Request) != LPC_REQUEST)))
240 {
241 /* It isn't, fail */
242 LpcpFreeToPortZone(Message, 3);
243 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
244 ObDereferenceObject(WakeupThread);
245 ObDereferenceObject(Port);
246 return STATUS_REPLY_MESSAGE_MISMATCH;
247 }
248
249 /* Copy the message */
250 _SEH2_TRY
251 {
252 LpcpMoveMessage(&Message->Request,
253 ReplyMessage,
254 ReplyMessage + 1,
255 LPC_REPLY,
256 NULL);
257 }
258 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
259 {
260 /* Fail */
261 LpcpFreeToPortZone(Message, 3);
262 ObDereferenceObject(WakeupThread);
263 ObDereferenceObject(Port);
264 _SEH2_YIELD(return _SEH2_GetExceptionCode());
265 }
266 _SEH2_END;
267
268 /* Reference the thread while we use it */
269 ObReferenceObject(WakeupThread);
270 Message->RepliedToThread = WakeupThread;
271
272 /* Set this as the reply message */
273 WakeupThread->LpcReplyMessageId = 0;
274 WakeupThread->LpcReplyMessage = (PVOID)Message;
275
276 /* Check if we have messages on the reply chain */
277 if (!(WakeupThread->LpcExitThreadCalled) &&
278 !(IsListEmpty(&WakeupThread->LpcReplyChain)))
279 {
280 /* Remove us from it and reinitialize it */
281 RemoveEntryList(&WakeupThread->LpcReplyChain);
282 InitializeListHead(&WakeupThread->LpcReplyChain);
283 }
284
285 /* Check if this is the message the thread had received */
286 if ((Thread->LpcReceivedMsgIdValid) &&
287 (Thread->LpcReceivedMessageId == ReplyMessage->MessageId))
288 {
289 /* Clear this data */
290 Thread->LpcReceivedMessageId = 0;
291 Thread->LpcReceivedMsgIdValid = FALSE;
292 }
293
294 /* Free any data information */
295 LpcpFreeDataInfoMessage(Port,
296 ReplyMessage->MessageId,
297 ReplyMessage->CallbackId,
298 ReplyMessage->ClientId);
299
300 /* Release the lock and release the LPC semaphore to wake up waiters */
301 KeReleaseGuardedMutex(&LpcpLock);
302 LpcpCompleteWait(&WakeupThread->LpcReplySemaphore);
303
304 /* Now we can let go of the thread */
305 ObDereferenceObject(WakeupThread);
306
307 /* Dereference port object */
308 ObDereferenceObject(Port);
309 return Status;
310 }
311
312 /*
313 * @implemented
314 */
315 NTSTATUS
316 NTAPI
317 NtReplyWaitReceivePortEx(IN HANDLE PortHandle,
318 OUT PVOID *PortContext OPTIONAL,
319 IN PPORT_MESSAGE ReplyMessage OPTIONAL,
320 OUT PPORT_MESSAGE ReceiveMessage,
321 IN PLARGE_INTEGER Timeout OPTIONAL)
322 {
323 PLPCP_PORT_OBJECT Port, ReceivePort, ConnectionPort = NULL;
324 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(), WaitMode = PreviousMode;
325 NTSTATUS Status;
326 PLPCP_MESSAGE Message;
327 PETHREAD Thread = PsGetCurrentThread(), WakeupThread;
328 PLPCP_CONNECTION_MESSAGE ConnectMessage;
329 ULONG ConnectionInfoLength;
330 //PORT_MESSAGE CapturedReplyMessage;
331 LARGE_INTEGER CapturedTimeout;
332
333 PAGED_CODE();
334 LPCTRACE(LPC_REPLY_DEBUG,
335 "Handle: %lx. Messages: %p/%p. Context: %p\n",
336 PortHandle,
337 ReplyMessage,
338 ReceiveMessage,
339 PortContext);
340
341 if (KeGetPreviousMode() == UserMode)
342 {
343 _SEH2_TRY
344 {
345 if (ReplyMessage != NULL)
346 {
347 ProbeForRead(ReplyMessage, sizeof(PORT_MESSAGE), sizeof(ULONG));
348 /*RtlCopyMemory(&CapturedReplyMessage, ReplyMessage, sizeof(PORT_MESSAGE));
349 ReplyMessage = &CapturedReplyMessage;*/
350 }
351
352 if (Timeout != NULL)
353 {
354 ProbeForReadLargeInteger(Timeout);
355 RtlCopyMemory(&CapturedTimeout, Timeout, sizeof(LARGE_INTEGER));
356 Timeout = &CapturedTimeout;
357 }
358
359 if (PortContext != NULL)
360 ProbeForWritePointer(PortContext);
361 }
362 _SEH2_EXCEPT(ExSystemExceptionFilter())
363 {
364 DPRINT1("SEH crash [1]\n");
365 DbgBreakPoint();
366 _SEH2_YIELD(return _SEH2_GetExceptionCode());
367 }
368 _SEH2_END;
369 }
370 else
371 {
372 /* If this is a system thread, then let it page out its stack */
373 if (Thread->SystemThread) WaitMode = UserMode;
374 }
375
376 /* Check if caller has a reply message */
377 if (ReplyMessage)
378 {
379 /* Validate its length */
380 if (((ULONG)ReplyMessage->u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
381 (ULONG)ReplyMessage->u1.s1.TotalLength)
382 {
383 /* Fail */
384 return STATUS_INVALID_PARAMETER;
385 }
386
387 /* Make sure it has a valid ID */
388 if (!ReplyMessage->MessageId) return STATUS_INVALID_PARAMETER;
389 }
390
391 /* Get the Port object */
392 Status = ObReferenceObjectByHandle(PortHandle,
393 0,
394 LpcPortObjectType,
395 PreviousMode,
396 (PVOID*)&Port,
397 NULL);
398 if (!NT_SUCCESS(Status)) return Status;
399
400 /* Check if the caller has a reply message */
401 if (ReplyMessage)
402 {
403 /* Validate its length in respect to the port object */
404 if (((ULONG)ReplyMessage->u1.s1.TotalLength > Port->MaxMessageLength) ||
405 ((ULONG)ReplyMessage->u1.s1.TotalLength <=
406 (ULONG)ReplyMessage->u1.s1.DataLength))
407 {
408 /* Too large, fail */
409 ObDereferenceObject(Port);
410 return STATUS_PORT_MESSAGE_TOO_LONG;
411 }
412 }
413
414 /* Check if this is anything but a client port */
415 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CLIENT_PORT)
416 {
417 /* Check if this is the connection port */
418 if (Port->ConnectionPort == Port)
419 {
420 /* Use this port */
421 ConnectionPort = ReceivePort = Port;
422 ObReferenceObject(ConnectionPort);
423 }
424 else
425 {
426 /* Acquire the lock */
427 KeAcquireGuardedMutex(&LpcpLock);
428
429 /* Get the port */
430 ConnectionPort = ReceivePort = Port->ConnectionPort;
431 if (!ConnectionPort)
432 {
433 /* Fail */
434 KeReleaseGuardedMutex(&LpcpLock);
435 ObDereferenceObject(Port);
436 return STATUS_PORT_DISCONNECTED;
437 }
438
439 /* Release lock and reference */
440 ObReferenceObject(ConnectionPort);
441 KeReleaseGuardedMutex(&LpcpLock);
442 }
443 }
444 else
445 {
446 /* Otherwise, use the port itself */
447 ReceivePort = Port;
448 }
449
450 /* Check if the caller gave a reply message */
451 if (ReplyMessage)
452 {
453 /* Get the ETHREAD corresponding to it */
454 Status = PsLookupProcessThreadByCid(&ReplyMessage->ClientId,
455 NULL,
456 &WakeupThread);
457 if (!NT_SUCCESS(Status))
458 {
459 /* No thread found, fail */
460 ObDereferenceObject(Port);
461 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
462 return Status;
463 }
464
465 /* Allocate a message from the port zone */
466 Message = LpcpAllocateFromPortZone();
467 if (!Message)
468 {
469 /* Fail if we couldn't allocate a message */
470 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
471 ObDereferenceObject(WakeupThread);
472 ObDereferenceObject(Port);
473 return STATUS_NO_MEMORY;
474 }
475
476 /* Keep the lock acquired */
477 KeAcquireGuardedMutex(&LpcpLock);
478
479 /* Make sure this is the reply the thread is waiting for */
480 if ((WakeupThread->LpcReplyMessageId != ReplyMessage->MessageId) ||
481 ((LpcpGetMessageFromThread(WakeupThread)) &&
482 (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread)->
483 Request) != LPC_REQUEST)))
484 {
485 /* It isn't, fail */
486 LpcpFreeToPortZone(Message, 3);
487 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
488 ObDereferenceObject(WakeupThread);
489 ObDereferenceObject(Port);
490 return STATUS_REPLY_MESSAGE_MISMATCH;
491 }
492
493 /* Copy the message */
494 LpcpMoveMessage(&Message->Request,
495 ReplyMessage,
496 ReplyMessage + 1,
497 LPC_REPLY,
498 NULL);
499
500 /* Reference the thread while we use it */
501 ObReferenceObject(WakeupThread);
502 Message->RepliedToThread = WakeupThread;
503
504 /* Set this as the reply message */
505 WakeupThread->LpcReplyMessageId = 0;
506 WakeupThread->LpcReplyMessage = (PVOID)Message;
507
508 /* Check if we have messages on the reply chain */
509 if (!(WakeupThread->LpcExitThreadCalled) &&
510 !(IsListEmpty(&WakeupThread->LpcReplyChain)))
511 {
512 /* Remove us from it and reinitialize it */
513 RemoveEntryList(&WakeupThread->LpcReplyChain);
514 InitializeListHead(&WakeupThread->LpcReplyChain);
515 }
516
517 /* Check if this is the message the thread had received */
518 if ((Thread->LpcReceivedMsgIdValid) &&
519 (Thread->LpcReceivedMessageId == ReplyMessage->MessageId))
520 {
521 /* Clear this data */
522 Thread->LpcReceivedMessageId = 0;
523 Thread->LpcReceivedMsgIdValid = FALSE;
524 }
525
526 /* Free any data information */
527 LpcpFreeDataInfoMessage(Port,
528 ReplyMessage->MessageId,
529 ReplyMessage->CallbackId,
530 ReplyMessage->ClientId);
531
532 /* Release the lock and release the LPC semaphore to wake up waiters */
533 KeReleaseGuardedMutex(&LpcpLock);
534 LpcpCompleteWait(&WakeupThread->LpcReplySemaphore);
535
536 /* Now we can let go of the thread */
537 ObDereferenceObject(WakeupThread);
538 }
539
540 /* Now wait for someone to reply to us */
541 LpcpReceiveWait(ReceivePort->MsgQueue.Semaphore, WaitMode);
542 if (Status != STATUS_SUCCESS) goto Cleanup;
543
544 /* Wait done, get the LPC lock */
545 KeAcquireGuardedMutex(&LpcpLock);
546
547 /* Check if we've received nothing */
548 if (IsListEmpty(&ReceivePort->MsgQueue.ReceiveHead))
549 {
550 /* Check if this was a waitable port and wake it */
551 if (ReceivePort->Flags & LPCP_WAITABLE_PORT)
552 {
553 /* Reset its event */
554 KeResetEvent(&ReceivePort->WaitEvent);
555 }
556
557 /* Release the lock and fail */
558 KeReleaseGuardedMutex(&LpcpLock);
559 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
560 ObDereferenceObject(Port);
561 return STATUS_UNSUCCESSFUL;
562 }
563
564 /* Get the message on the queue */
565 Message = CONTAINING_RECORD(RemoveHeadList(&ReceivePort->
566 MsgQueue.ReceiveHead),
567 LPCP_MESSAGE,
568 Entry);
569
570 /* Check if the queue is empty now */
571 if (IsListEmpty(&ReceivePort->MsgQueue.ReceiveHead))
572 {
573 /* Check if this was a waitable port */
574 if (ReceivePort->Flags & LPCP_WAITABLE_PORT)
575 {
576 /* Reset its event */
577 KeResetEvent(&ReceivePort->WaitEvent);
578 }
579 }
580
581 /* Re-initialize the message's list entry */
582 InitializeListHead(&Message->Entry);
583
584 /* Set this as the received message */
585 Thread->LpcReceivedMessageId = Message->Request.MessageId;
586 Thread->LpcReceivedMsgIdValid = TRUE;
587
588 _SEH2_TRY
589 {
590 /* Check if this was a connection request */
591 if (LpcpGetMessageType(&Message->Request) == LPC_CONNECTION_REQUEST)
592 {
593 /* Get the connection message */
594 ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1);
595 LPCTRACE(LPC_REPLY_DEBUG,
596 "Request Messages: %p/%p\n",
597 Message,
598 ConnectMessage);
599
600 /* Get its length */
601 ConnectionInfoLength = Message->Request.u1.s1.DataLength -
602 sizeof(LPCP_CONNECTION_MESSAGE);
603
604 /* Return it as the receive message */
605 *ReceiveMessage = Message->Request;
606
607 /* Clear our stack variable so the message doesn't get freed */
608 Message = NULL;
609
610 /* Setup the receive message */
611 ReceiveMessage->u1.s1.TotalLength = (CSHORT)(sizeof(LPCP_MESSAGE) +
612 ConnectionInfoLength);
613 ReceiveMessage->u1.s1.DataLength = (CSHORT)ConnectionInfoLength;
614 RtlCopyMemory(ReceiveMessage + 1,
615 ConnectMessage + 1,
616 ConnectionInfoLength);
617
618 /* Clear the port context if the caller requested one */
619 if (PortContext) *PortContext = NULL;
620 }
621 else if (LpcpGetMessageType(&Message->Request) != LPC_REPLY)
622 {
623 /* Otherwise, this is a new message or event */
624 LPCTRACE(LPC_REPLY_DEBUG,
625 "Non-Reply Messages: %p/%p\n",
626 &Message->Request,
627 (&Message->Request) + 1);
628
629 /* Copy it */
630 LpcpMoveMessage(ReceiveMessage,
631 &Message->Request,
632 (&Message->Request) + 1,
633 0,
634 NULL);
635
636 /* Return its context */
637 if (PortContext) *PortContext = Message->PortContext;
638
639 /* And check if it has data information */
640 if (Message->Request.u2.s2.DataInfoOffset)
641 {
642 /* It does, save it, and don't free the message below */
643 LpcpSaveDataInfoMessage(Port, Message, 1);
644 Message = NULL;
645 }
646 }
647 else
648 {
649 /* This is a reply message, should never happen! */
650 ASSERT(FALSE);
651 }
652 }
653 _SEH2_EXCEPT(ExSystemExceptionFilter())
654 {
655 DPRINT1("SEH crash [2]\n");
656 DbgBreakPoint();
657 Status = _SEH2_GetExceptionCode();
658 }
659 _SEH2_END;
660
661 /* Check if we have a message pointer here */
662 if (Message)
663 {
664 /* Free it and release the lock */
665 LpcpFreeToPortZone(Message, 3);
666 }
667 else
668 {
669 /* Just release the lock */
670 KeReleaseGuardedMutex(&LpcpLock);
671 }
672
673 Cleanup:
674 /* All done, dereference the port and return the status */
675 LPCTRACE(LPC_REPLY_DEBUG,
676 "Port: %p. Status: %p\n",
677 Port,
678 Status);
679 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
680 ObDereferenceObject(Port);
681 return Status;
682 }
683
684 /*
685 * @implemented
686 */
687 NTSTATUS
688 NTAPI
689 NtReplyWaitReceivePort(IN HANDLE PortHandle,
690 OUT PVOID *PortContext OPTIONAL,
691 IN PPORT_MESSAGE ReplyMessage OPTIONAL,
692 OUT PPORT_MESSAGE ReceiveMessage)
693 {
694 /* Call the newer API */
695 return NtReplyWaitReceivePortEx(PortHandle,
696 PortContext,
697 ReplyMessage,
698 ReceiveMessage,
699 NULL);
700 }
701
702 /*
703 * @unimplemented
704 */
705 NTSTATUS
706 NTAPI
707 NtReplyWaitReplyPort(IN HANDLE PortHandle,
708 IN PPORT_MESSAGE ReplyMessage)
709 {
710 UNIMPLEMENTED;
711 return STATUS_NOT_IMPLEMENTED;
712 }
713
714 /*
715 * @unimplemented
716 */
717 NTSTATUS
718 NTAPI
719 NtReadRequestData(IN HANDLE PortHandle,
720 IN PPORT_MESSAGE Message,
721 IN ULONG Index,
722 IN PVOID Buffer,
723 IN ULONG BufferLength,
724 OUT PULONG Returnlength)
725 {
726 UNIMPLEMENTED;
727 return STATUS_NOT_IMPLEMENTED;
728 }
729
730 /*
731 * @unimplemented
732 */
733 NTSTATUS
734 NTAPI
735 NtWriteRequestData(IN HANDLE PortHandle,
736 IN PPORT_MESSAGE Message,
737 IN ULONG Index,
738 IN PVOID Buffer,
739 IN ULONG BufferLength,
740 OUT PULONG ReturnLength)
741 {
742 UNIMPLEMENTED;
743 return STATUS_NOT_IMPLEMENTED;
744 }
745
746 /* EOF */