Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygensend.c
Go to the documentation of this file.
00001 /* 00002 * PROJECT: ReactOS Kernel 00003 * LICENSE: GPL - See COPYING in the top level directory 00004 * FILE: ntoskrnl/lpc/send.c 00005 * PURPOSE: Local Procedure Call: Sending (Requests) 00006 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 00007 */ 00008 00009 /* INCLUDES ******************************************************************/ 00010 00011 #include <ntoskrnl.h> 00012 #define NDEBUG 00013 #include <debug.h> 00014 00015 /* PUBLIC FUNCTIONS **********************************************************/ 00016 00017 /* 00018 * @implemented 00019 */ 00020 NTSTATUS 00021 NTAPI 00022 LpcRequestPort(IN PVOID PortObject, 00023 IN PPORT_MESSAGE LpcMessage) 00024 { 00025 PLPCP_PORT_OBJECT Port = PortObject, QueuePort, ConnectionPort = NULL; 00026 ULONG MessageType; 00027 PLPCP_MESSAGE Message; 00028 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 00029 PAGED_CODE(); 00030 LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", Port, LpcMessage); 00031 00032 /* Check if this is a non-datagram message */ 00033 if (LpcMessage->u2.s2.Type) 00034 { 00035 /* Get the message type */ 00036 MessageType = LpcpGetMessageType(LpcMessage); 00037 00038 /* Validate it */ 00039 if ((MessageType < LPC_DATAGRAM) || (MessageType > LPC_CLIENT_DIED)) 00040 { 00041 /* Fail */ 00042 return STATUS_INVALID_PARAMETER; 00043 } 00044 00045 /* Mark this as a kernel-mode message only if we really came from it */ 00046 if ((PreviousMode == KernelMode) && 00047 (LpcMessage->u2.s2.Type & LPC_KERNELMODE_MESSAGE)) 00048 { 00049 /* We did, this is a kernel mode message */ 00050 MessageType |= LPC_KERNELMODE_MESSAGE; 00051 } 00052 } 00053 else 00054 { 00055 /* This is a datagram */ 00056 MessageType = LPC_DATAGRAM; 00057 } 00058 00059 /* Can't have data information on this type of call */ 00060 if (LpcMessage->u2.s2.DataInfoOffset) return STATUS_INVALID_PARAMETER; 00061 00062 /* Validate message sizes */ 00063 if (((ULONG)LpcMessage->u1.s1.TotalLength > Port->MaxMessageLength) || 00064 ((ULONG)LpcMessage->u1.s1.TotalLength <= (ULONG)LpcMessage->u1.s1.DataLength)) 00065 { 00066 /* Fail */ 00067 return STATUS_PORT_MESSAGE_TOO_LONG; 00068 } 00069 00070 /* Allocate a new message */ 00071 Message = LpcpAllocateFromPortZone(); 00072 if (!Message) return STATUS_NO_MEMORY; 00073 00074 /* Clear the context */ 00075 Message->RepliedToThread = NULL; 00076 Message->PortContext = NULL; 00077 00078 /* Copy the message */ 00079 LpcpMoveMessage(&Message->Request, 00080 LpcMessage, 00081 LpcMessage + 1, 00082 MessageType, 00083 &PsGetCurrentThread()->Cid); 00084 00085 /* Acquire the LPC lock */ 00086 KeAcquireGuardedMutex(&LpcpLock); 00087 00088 /* Check if this is anything but a connection port */ 00089 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) 00090 { 00091 /* The queue port is the connected port */ 00092 QueuePort = Port->ConnectedPort; 00093 if (QueuePort) 00094 { 00095 /* Check if this is a client port */ 00096 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) 00097 { 00098 /* Then copy the context */ 00099 Message->PortContext = QueuePort->PortContext; 00100 ConnectionPort = QueuePort = Port->ConnectionPort; 00101 if (!ConnectionPort) 00102 { 00103 /* Fail */ 00104 LpcpFreeToPortZone(Message, 3); 00105 return STATUS_PORT_DISCONNECTED; 00106 } 00107 } 00108 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT) 00109 { 00110 /* Any other kind of port, use the connection port */ 00111 ConnectionPort = QueuePort = Port->ConnectionPort; 00112 if (!ConnectionPort) 00113 { 00114 /* Fail */ 00115 LpcpFreeToPortZone(Message, 3); 00116 return STATUS_PORT_DISCONNECTED; 00117 } 00118 } 00119 00120 /* If we have a connection port, reference it */ 00121 if (ConnectionPort) ObReferenceObject(ConnectionPort); 00122 } 00123 } 00124 else 00125 { 00126 /* For connection ports, use the port itself */ 00127 QueuePort = PortObject; 00128 } 00129 00130 /* Make sure we have a port */ 00131 if (QueuePort) 00132 { 00133 /* Generate the Message ID and set it */ 00134 Message->Request.MessageId = LpcpNextMessageId++; 00135 if (!LpcpNextMessageId) LpcpNextMessageId = 1; 00136 Message->Request.CallbackId = 0; 00137 00138 /* No Message ID for the thread */ 00139 PsGetCurrentThread()->LpcReplyMessageId = 0; 00140 00141 /* Insert the message in our chain */ 00142 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry); 00143 00144 /* Release the lock and release the semaphore */ 00145 KeEnterCriticalRegion(); 00146 KeReleaseGuardedMutex(&LpcpLock); 00147 LpcpCompleteWait(QueuePort->MsgQueue.Semaphore); 00148 00149 /* If this is a waitable port, wake it up */ 00150 if (QueuePort->Flags & LPCP_WAITABLE_PORT) 00151 { 00152 /* Wake it */ 00153 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); 00154 } 00155 00156 /* We're done */ 00157 KeLeaveCriticalRegion(); 00158 if (ConnectionPort) ObDereferenceObject(ConnectionPort); 00159 LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", QueuePort, Message); 00160 return STATUS_SUCCESS; 00161 } 00162 00163 /* If we got here, then free the message and fail */ 00164 LpcpFreeToPortZone(Message, 3); 00165 if (ConnectionPort) ObDereferenceObject(ConnectionPort); 00166 return STATUS_PORT_DISCONNECTED; 00167 } 00168 00169 /* 00170 * @implemented 00171 */ 00172 NTSTATUS 00173 NTAPI 00174 LpcRequestWaitReplyPort(IN PVOID PortObject, 00175 IN PPORT_MESSAGE LpcRequest, 00176 OUT PPORT_MESSAGE LpcReply) 00177 { 00178 PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort, ConnectionPort = NULL; 00179 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 00180 NTSTATUS Status = STATUS_SUCCESS; 00181 PLPCP_MESSAGE Message; 00182 PETHREAD Thread = PsGetCurrentThread(); 00183 BOOLEAN Callback = FALSE; 00184 PKSEMAPHORE Semaphore; 00185 USHORT MessageType; 00186 PAGED_CODE(); 00187 00188 Port = (PLPCP_PORT_OBJECT)PortObject; 00189 00190 LPCTRACE(LPC_SEND_DEBUG, 00191 "Port: %p. Messages: %p/%p. Type: %lx\n", 00192 Port, 00193 LpcRequest, 00194 LpcReply, 00195 LpcpGetMessageType(LpcRequest)); 00196 00197 /* Check if the thread is dying */ 00198 if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING; 00199 00200 /* Check if this is an LPC Request */ 00201 MessageType = LpcpGetMessageType(LpcRequest); 00202 switch (MessageType) 00203 { 00204 /* No type */ 00205 case 0: 00206 00207 /* Assume LPC request */ 00208 MessageType = LPC_REQUEST; 00209 break; 00210 00211 /* LPC request callback */ 00212 case LPC_REQUEST: 00213 00214 /* This is a callback */ 00215 Callback = TRUE; 00216 break; 00217 00218 /* Anything else */ 00219 case LPC_CLIENT_DIED: 00220 case LPC_PORT_CLOSED: 00221 case LPC_EXCEPTION: 00222 case LPC_DEBUG_EVENT: 00223 case LPC_ERROR_EVENT: 00224 00225 /* Nothing to do */ 00226 break; 00227 00228 default: 00229 00230 /* Invalid message type */ 00231 return STATUS_INVALID_PARAMETER; 00232 } 00233 00234 /* Set the request type */ 00235 LpcRequest->u2.s2.Type = MessageType; 00236 00237 /* Validate the message length */ 00238 if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) || 00239 ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength)) 00240 { 00241 /* Fail */ 00242 return STATUS_PORT_MESSAGE_TOO_LONG; 00243 } 00244 00245 /* Allocate a message from the port zone */ 00246 Message = LpcpAllocateFromPortZone(); 00247 if (!Message) 00248 { 00249 /* Fail if we couldn't allocate a message */ 00250 return STATUS_NO_MEMORY; 00251 } 00252 00253 /* Check if this is a callback */ 00254 if (Callback) 00255 { 00256 /* FIXME: TODO */ 00257 Semaphore = NULL; // we'd use the Thread Semaphore here 00258 ASSERT(FALSE); 00259 return STATUS_NOT_IMPLEMENTED; 00260 } 00261 else 00262 { 00263 /* No callback, just copy the message */ 00264 LpcpMoveMessage(&Message->Request, 00265 LpcRequest, 00266 LpcRequest + 1, 00267 0, 00268 &Thread->Cid); 00269 00270 /* Acquire the LPC lock */ 00271 KeAcquireGuardedMutex(&LpcpLock); 00272 00273 /* Right now clear the port context */ 00274 Message->PortContext = NULL; 00275 00276 /* Check if this is a not connection port */ 00277 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) 00278 { 00279 /* We want the connected port */ 00280 QueuePort = Port->ConnectedPort; 00281 if (!QueuePort) 00282 { 00283 /* We have no connected port, fail */ 00284 LpcpFreeToPortZone(Message, 3); 00285 return STATUS_PORT_DISCONNECTED; 00286 } 00287 00288 /* This will be the rundown port */ 00289 ReplyPort = QueuePort; 00290 00291 /* Check if this is a communication port */ 00292 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) 00293 { 00294 /* Copy the port context and use the connection port */ 00295 Message->PortContext = QueuePort->PortContext; 00296 ConnectionPort = QueuePort = Port->ConnectionPort; 00297 if (!ConnectionPort) 00298 { 00299 /* Fail */ 00300 LpcpFreeToPortZone(Message, 3); 00301 return STATUS_PORT_DISCONNECTED; 00302 } 00303 } 00304 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != 00305 LPCP_COMMUNICATION_PORT) 00306 { 00307 /* Use the connection port for anything but communication ports */ 00308 ConnectionPort = QueuePort = Port->ConnectionPort; 00309 if (!ConnectionPort) 00310 { 00311 /* Fail */ 00312 LpcpFreeToPortZone(Message, 3); 00313 return STATUS_PORT_DISCONNECTED; 00314 } 00315 } 00316 00317 /* Reference the connection port if it exists */ 00318 if (ConnectionPort) ObReferenceObject(ConnectionPort); 00319 } 00320 else 00321 { 00322 /* Otherwise, for a connection port, use the same port object */ 00323 QueuePort = ReplyPort = Port; 00324 } 00325 00326 /* No reply thread */ 00327 Message->RepliedToThread = NULL; 00328 Message->SenderPort = Port; 00329 00330 /* Generate the Message ID and set it */ 00331 Message->Request.MessageId = LpcpNextMessageId++; 00332 if (!LpcpNextMessageId) LpcpNextMessageId = 1; 00333 Message->Request.CallbackId = 0; 00334 00335 /* Set the message ID for our thread now */ 00336 Thread->LpcReplyMessageId = Message->Request.MessageId; 00337 Thread->LpcReplyMessage = NULL; 00338 00339 /* Insert the message in our chain */ 00340 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry); 00341 InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain); 00342 LpcpSetPortToThread(Thread, Port); 00343 00344 /* Release the lock and get the semaphore we'll use later */ 00345 KeEnterCriticalRegion(); 00346 KeReleaseGuardedMutex(&LpcpLock); 00347 Semaphore = QueuePort->MsgQueue.Semaphore; 00348 00349 /* If this is a waitable port, wake it up */ 00350 if (QueuePort->Flags & LPCP_WAITABLE_PORT) 00351 { 00352 /* Wake it */ 00353 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); 00354 } 00355 } 00356 00357 /* Now release the semaphore */ 00358 LpcpCompleteWait(Semaphore); 00359 KeLeaveCriticalRegion(); 00360 00361 /* And let's wait for the reply */ 00362 LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode); 00363 00364 /* Acquire the LPC lock */ 00365 KeAcquireGuardedMutex(&LpcpLock); 00366 00367 /* Get the LPC Message and clear our thread's reply data */ 00368 Message = LpcpGetMessageFromThread(Thread); 00369 Thread->LpcReplyMessage = NULL; 00370 Thread->LpcReplyMessageId = 0; 00371 00372 /* Check if we have anything on the reply chain*/ 00373 if (!IsListEmpty(&Thread->LpcReplyChain)) 00374 { 00375 /* Remove this thread and reinitialize the list */ 00376 RemoveEntryList(&Thread->LpcReplyChain); 00377 InitializeListHead(&Thread->LpcReplyChain); 00378 } 00379 00380 /* Release the lock */ 00381 KeReleaseGuardedMutex(&LpcpLock); 00382 00383 /* Check if we got a reply */ 00384 if (Status == STATUS_SUCCESS) 00385 { 00386 /* Check if we have a valid message */ 00387 if (Message) 00388 { 00389 LPCTRACE(LPC_SEND_DEBUG, 00390 "Reply Messages: %p/%p\n", 00391 &Message->Request, 00392 (&Message->Request) + 1); 00393 00394 /* Move the message */ 00395 LpcpMoveMessage(LpcReply, 00396 &Message->Request, 00397 (&Message->Request) + 1, 00398 0, 00399 NULL); 00400 00401 /* Acquire the lock */ 00402 KeAcquireGuardedMutex(&LpcpLock); 00403 00404 /* Check if we replied to a thread */ 00405 if (Message->RepliedToThread) 00406 { 00407 /* Dereference */ 00408 ObDereferenceObject(Message->RepliedToThread); 00409 Message->RepliedToThread = NULL; 00410 } 00411 00412 00413 /* Free the message */ 00414 LpcpFreeToPortZone(Message, 3); 00415 } 00416 else 00417 { 00418 /* We don't have a reply */ 00419 Status = STATUS_LPC_REPLY_LOST; 00420 } 00421 } 00422 else 00423 { 00424 /* The wait failed, free the message */ 00425 if (Message) LpcpFreeToPortZone(Message, 0); 00426 } 00427 00428 /* All done */ 00429 LPCTRACE(LPC_SEND_DEBUG, 00430 "Port: %p. Status: %p\n", 00431 Port, 00432 Status); 00433 00434 /* Dereference the connection port */ 00435 if (ConnectionPort) ObDereferenceObject(ConnectionPort); 00436 return Status; 00437 } 00438 00439 /* 00440 * @implemented 00441 */ 00442 NTSTATUS 00443 NTAPI 00444 NtRequestPort(IN HANDLE PortHandle, 00445 IN PPORT_MESSAGE LpcRequest) 00446 { 00447 PLPCP_PORT_OBJECT Port, QueuePort, ConnectionPort = NULL; 00448 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 00449 NTSTATUS Status; 00450 PLPCP_MESSAGE Message; 00451 PETHREAD Thread = PsGetCurrentThread(); 00452 00453 PKSEMAPHORE Semaphore; 00454 ULONG MessageType; 00455 PAGED_CODE(); 00456 LPCTRACE(LPC_SEND_DEBUG, 00457 "Handle: %lx. Message: %p. Type: %lx\n", 00458 PortHandle, 00459 LpcRequest, 00460 LpcpGetMessageType(LpcRequest)); 00461 00462 /* Get the message type */ 00463 MessageType = LpcRequest->u2.s2.Type | LPC_DATAGRAM; 00464 00465 /* Can't have data information on this type of call */ 00466 if (LpcRequest->u2.s2.DataInfoOffset) return STATUS_INVALID_PARAMETER; 00467 00468 /* Validate the length */ 00469 if (((ULONG)LpcRequest->u1.s1.DataLength + sizeof(PORT_MESSAGE)) > 00470 (ULONG)LpcRequest->u1.s1.TotalLength) 00471 { 00472 /* Fail */ 00473 return STATUS_INVALID_PARAMETER; 00474 } 00475 00476 /* Reference the object */ 00477 Status = ObReferenceObjectByHandle(PortHandle, 00478 0, 00479 LpcPortObjectType, 00480 PreviousMode, 00481 (PVOID*)&Port, 00482 NULL); 00483 if (!NT_SUCCESS(Status)) return Status; 00484 00485 /* Validate the message length */ 00486 if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) || 00487 ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength)) 00488 { 00489 /* Fail */ 00490 ObDereferenceObject(Port); 00491 return STATUS_PORT_MESSAGE_TOO_LONG; 00492 } 00493 00494 /* Allocate a message from the port zone */ 00495 Message = LpcpAllocateFromPortZone(); 00496 if (!Message) 00497 { 00498 /* Fail if we couldn't allocate a message */ 00499 ObDereferenceObject(Port); 00500 return STATUS_NO_MEMORY; 00501 } 00502 00503 /* No callback, just copy the message */ 00504 _SEH2_TRY 00505 { 00506 /* Copy it */ 00507 LpcpMoveMessage(&Message->Request, 00508 LpcRequest, 00509 LpcRequest + 1, 00510 MessageType, 00511 &Thread->Cid); 00512 } 00513 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 00514 { 00515 /* Fail */ 00516 LpcpFreeToPortZone(Message, 0); 00517 ObDereferenceObject(Port); 00518 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 00519 } 00520 _SEH2_END; 00521 00522 /* Acquire the LPC lock */ 00523 KeAcquireGuardedMutex(&LpcpLock); 00524 00525 /* Right now clear the port context */ 00526 Message->PortContext = NULL; 00527 00528 /* Check if this is a not connection port */ 00529 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) 00530 { 00531 /* We want the connected port */ 00532 QueuePort = Port->ConnectedPort; 00533 if (!QueuePort) 00534 { 00535 /* We have no connected port, fail */ 00536 LpcpFreeToPortZone(Message, 3); 00537 ObDereferenceObject(Port); 00538 return STATUS_PORT_DISCONNECTED; 00539 } 00540 00541 /* Check if this is a communication port */ 00542 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) 00543 { 00544 /* Copy the port context and use the connection port */ 00545 Message->PortContext = QueuePort->PortContext; 00546 ConnectionPort = QueuePort = Port->ConnectionPort; 00547 if (!ConnectionPort) 00548 { 00549 /* Fail */ 00550 LpcpFreeToPortZone(Message, 3); 00551 ObDereferenceObject(Port); 00552 return STATUS_PORT_DISCONNECTED; 00553 } 00554 } 00555 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != 00556 LPCP_COMMUNICATION_PORT) 00557 { 00558 /* Use the connection port for anything but communication ports */ 00559 ConnectionPort = QueuePort = Port->ConnectionPort; 00560 if (!ConnectionPort) 00561 { 00562 /* Fail */ 00563 LpcpFreeToPortZone(Message, 3); 00564 ObDereferenceObject(Port); 00565 return STATUS_PORT_DISCONNECTED; 00566 } 00567 } 00568 00569 /* Reference the connection port if it exists */ 00570 if (ConnectionPort) ObReferenceObject(ConnectionPort); 00571 } 00572 else 00573 { 00574 /* Otherwise, for a connection port, use the same port object */ 00575 QueuePort = Port; 00576 } 00577 00578 /* Reference QueuePort if we have it */ 00579 if (QueuePort && ObReferenceObjectSafe(QueuePort)) 00580 { 00581 /* Set sender's port */ 00582 Message->SenderPort = Port; 00583 00584 /* Generate the Message ID and set it */ 00585 Message->Request.MessageId = LpcpNextMessageId++; 00586 if (!LpcpNextMessageId) LpcpNextMessageId = 1; 00587 Message->Request.CallbackId = 0; 00588 00589 /* No Message ID for the thread */ 00590 PsGetCurrentThread()->LpcReplyMessageId = 0; 00591 00592 /* Insert the message in our chain */ 00593 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry); 00594 00595 /* Release the lock and get the semaphore we'll use later */ 00596 KeEnterCriticalRegion(); 00597 KeReleaseGuardedMutex(&LpcpLock); 00598 00599 /* Now release the semaphore */ 00600 Semaphore = QueuePort->MsgQueue.Semaphore; 00601 LpcpCompleteWait(Semaphore); 00602 00603 /* If this is a waitable port, wake it up */ 00604 if (QueuePort->Flags & LPCP_WAITABLE_PORT) 00605 { 00606 /* Wake it */ 00607 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); 00608 } 00609 00610 KeLeaveCriticalRegion(); 00611 00612 /* Dereference objects */ 00613 if (ConnectionPort) ObDereferenceObject(ConnectionPort); 00614 ObDereferenceObject(QueuePort); 00615 ObDereferenceObject(Port); 00616 LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", QueuePort, Message); 00617 return STATUS_SUCCESS; 00618 } 00619 00620 Status = STATUS_PORT_DISCONNECTED; 00621 00622 /* All done with a failure*/ 00623 LPCTRACE(LPC_SEND_DEBUG, 00624 "Port: %p. Status: %p\n", 00625 Port, 00626 Status); 00627 00628 /* The wait failed, free the message */ 00629 if (Message) LpcpFreeToPortZone(Message, 3); 00630 00631 ObDereferenceObject(Port); 00632 if (ConnectionPort) ObDereferenceObject(ConnectionPort); 00633 return Status; 00634 } 00635 00636 /* 00637 * @implemented 00638 */ 00639 NTSTATUS 00640 NTAPI 00641 NtRequestWaitReplyPort(IN HANDLE PortHandle, 00642 IN PPORT_MESSAGE LpcRequest, 00643 IN OUT PPORT_MESSAGE LpcReply) 00644 { 00645 PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort, ConnectionPort = NULL; 00646 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 00647 NTSTATUS Status; 00648 PLPCP_MESSAGE Message; 00649 PETHREAD Thread = PsGetCurrentThread(); 00650 BOOLEAN Callback; 00651 PKSEMAPHORE Semaphore; 00652 ULONG MessageType; 00653 PAGED_CODE(); 00654 LPCTRACE(LPC_SEND_DEBUG, 00655 "Handle: %lx. Messages: %p/%p. Type: %lx\n", 00656 PortHandle, 00657 LpcRequest, 00658 LpcReply, 00659 LpcpGetMessageType(LpcRequest)); 00660 00661 /* Check if the thread is dying */ 00662 if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING; 00663 00664 /* Check if this is an LPC Request */ 00665 if (LpcpGetMessageType(LpcRequest) == LPC_REQUEST) 00666 { 00667 /* Then it's a callback */ 00668 Callback = TRUE; 00669 } 00670 else if (LpcpGetMessageType(LpcRequest)) 00671 { 00672 /* This is a not kernel-mode message */ 00673 return STATUS_INVALID_PARAMETER; 00674 } 00675 else 00676 { 00677 /* This is a kernel-mode message without a callback */ 00678 LpcRequest->u2.s2.Type |= LPC_REQUEST; 00679 Callback = FALSE; 00680 } 00681 00682 /* Get the message type */ 00683 MessageType = LpcRequest->u2.s2.Type; 00684 00685 /* Validate the length */ 00686 if (((ULONG)LpcRequest->u1.s1.DataLength + sizeof(PORT_MESSAGE)) > 00687 (ULONG)LpcRequest->u1.s1.TotalLength) 00688 { 00689 /* Fail */ 00690 return STATUS_INVALID_PARAMETER; 00691 } 00692 00693 /* Reference the object */ 00694 Status = ObReferenceObjectByHandle(PortHandle, 00695 0, 00696 LpcPortObjectType, 00697 PreviousMode, 00698 (PVOID*)&Port, 00699 NULL); 00700 if (!NT_SUCCESS(Status)) return Status; 00701 00702 /* Validate the message length */ 00703 if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) || 00704 ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength)) 00705 { 00706 /* Fail */ 00707 ObDereferenceObject(Port); 00708 return STATUS_PORT_MESSAGE_TOO_LONG; 00709 } 00710 00711 /* Allocate a message from the port zone */ 00712 Message = LpcpAllocateFromPortZone(); 00713 if (!Message) 00714 { 00715 /* Fail if we couldn't allocate a message */ 00716 ObDereferenceObject(Port); 00717 return STATUS_NO_MEMORY; 00718 } 00719 00720 /* Check if this is a callback */ 00721 if (Callback) 00722 { 00723 /* FIXME: TODO */ 00724 Semaphore = NULL; // we'd use the Thread Semaphore here 00725 ASSERT(FALSE); 00726 } 00727 else 00728 { 00729 /* No callback, just copy the message */ 00730 _SEH2_TRY 00731 { 00732 /* Copy it */ 00733 LpcpMoveMessage(&Message->Request, 00734 LpcRequest, 00735 LpcRequest + 1, 00736 MessageType, 00737 &Thread->Cid); 00738 } 00739 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 00740 { 00741 /* Fail */ 00742 LpcpFreeToPortZone(Message, 0); 00743 ObDereferenceObject(Port); 00744 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 00745 } 00746 _SEH2_END; 00747 00748 /* Acquire the LPC lock */ 00749 KeAcquireGuardedMutex(&LpcpLock); 00750 00751 /* Right now clear the port context */ 00752 Message->PortContext = NULL; 00753 00754 /* Check if this is a not connection port */ 00755 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) 00756 { 00757 /* We want the connected port */ 00758 QueuePort = Port->ConnectedPort; 00759 if (!QueuePort) 00760 { 00761 /* We have no connected port, fail */ 00762 LpcpFreeToPortZone(Message, 3); 00763 ObDereferenceObject(Port); 00764 return STATUS_PORT_DISCONNECTED; 00765 } 00766 00767 /* This will be the rundown port */ 00768 ReplyPort = QueuePort; 00769 00770 /* Check if this is a communication port */ 00771 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) 00772 { 00773 /* Copy the port context and use the connection port */ 00774 Message->PortContext = QueuePort->PortContext; 00775 ConnectionPort = QueuePort = Port->ConnectionPort; 00776 if (!ConnectionPort) 00777 { 00778 /* Fail */ 00779 LpcpFreeToPortZone(Message, 3); 00780 ObDereferenceObject(Port); 00781 return STATUS_PORT_DISCONNECTED; 00782 } 00783 } 00784 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != 00785 LPCP_COMMUNICATION_PORT) 00786 { 00787 /* Use the connection port for anything but communication ports */ 00788 ConnectionPort = QueuePort = Port->ConnectionPort; 00789 if (!ConnectionPort) 00790 { 00791 /* Fail */ 00792 LpcpFreeToPortZone(Message, 3); 00793 ObDereferenceObject(Port); 00794 return STATUS_PORT_DISCONNECTED; 00795 } 00796 } 00797 00798 /* Reference the connection port if it exists */ 00799 if (ConnectionPort) ObReferenceObject(ConnectionPort); 00800 } 00801 else 00802 { 00803 /* Otherwise, for a connection port, use the same port object */ 00804 QueuePort = ReplyPort = Port; 00805 } 00806 00807 /* No reply thread */ 00808 Message->RepliedToThread = NULL; 00809 Message->SenderPort = Port; 00810 00811 /* Generate the Message ID and set it */ 00812 Message->Request.MessageId = LpcpNextMessageId++; 00813 if (!LpcpNextMessageId) LpcpNextMessageId = 1; 00814 Message->Request.CallbackId = 0; 00815 00816 /* Set the message ID for our thread now */ 00817 Thread->LpcReplyMessageId = Message->Request.MessageId; 00818 Thread->LpcReplyMessage = NULL; 00819 00820 /* Insert the message in our chain */ 00821 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry); 00822 InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain); 00823 LpcpSetPortToThread(Thread, Port); 00824 00825 /* Release the lock and get the semaphore we'll use later */ 00826 KeEnterCriticalRegion(); 00827 KeReleaseGuardedMutex(&LpcpLock); 00828 Semaphore = QueuePort->MsgQueue.Semaphore; 00829 00830 /* If this is a waitable port, wake it up */ 00831 if (QueuePort->Flags & LPCP_WAITABLE_PORT) 00832 { 00833 /* Wake it */ 00834 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); 00835 } 00836 } 00837 00838 /* Now release the semaphore */ 00839 LpcpCompleteWait(Semaphore); 00840 KeLeaveCriticalRegion(); 00841 00842 /* And let's wait for the reply */ 00843 LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode); 00844 00845 /* Acquire the LPC lock */ 00846 KeAcquireGuardedMutex(&LpcpLock); 00847 00848 /* Get the LPC Message and clear our thread's reply data */ 00849 Message = LpcpGetMessageFromThread(Thread); 00850 Thread->LpcReplyMessage = NULL; 00851 Thread->LpcReplyMessageId = 0; 00852 00853 /* Check if we have anything on the reply chain*/ 00854 if (!IsListEmpty(&Thread->LpcReplyChain)) 00855 { 00856 /* Remove this thread and reinitialize the list */ 00857 RemoveEntryList(&Thread->LpcReplyChain); 00858 InitializeListHead(&Thread->LpcReplyChain); 00859 } 00860 00861 /* Release the lock */ 00862 KeReleaseGuardedMutex(&LpcpLock); 00863 00864 /* Check if we got a reply */ 00865 if (Status == STATUS_SUCCESS) 00866 { 00867 /* Check if we have a valid message */ 00868 if (Message) 00869 { 00870 LPCTRACE(LPC_SEND_DEBUG, 00871 "Reply Messages: %p/%p\n", 00872 &Message->Request, 00873 (&Message->Request) + 1); 00874 00875 /* Move the message */ 00876 _SEH2_TRY 00877 { 00878 LpcpMoveMessage(LpcReply, 00879 &Message->Request, 00880 (&Message->Request) + 1, 00881 0, 00882 NULL); 00883 } 00884 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 00885 { 00886 Status = _SEH2_GetExceptionCode(); 00887 } 00888 _SEH2_END; 00889 00890 /* Check if this is an LPC request with data information */ 00891 if ((LpcpGetMessageType(&Message->Request) == LPC_REQUEST) && 00892 (Message->Request.u2.s2.DataInfoOffset)) 00893 { 00894 /* Save the data information */ 00895 LpcpSaveDataInfoMessage(Port, Message, 0); 00896 } 00897 else 00898 { 00899 /* Otherwise, just free it */ 00900 LpcpFreeToPortZone(Message, 0); 00901 } 00902 } 00903 else 00904 { 00905 /* We don't have a reply */ 00906 Status = STATUS_LPC_REPLY_LOST; 00907 } 00908 } 00909 else 00910 { 00911 /* The wait failed, free the message */ 00912 if (Message) LpcpFreeToPortZone(Message, 0); 00913 } 00914 00915 /* All done */ 00916 LPCTRACE(LPC_SEND_DEBUG, 00917 "Port: %p. Status: %p\n", 00918 Port, 00919 Status); 00920 ObDereferenceObject(Port); 00921 if (ConnectionPort) ObDereferenceObject(ConnectionPort); 00922 return Status; 00923 } 00924 00925 /* EOF */ Generated on Sat May 26 2012 04:25:40 for ReactOS by
1.7.6.1
|