ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

send.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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.