xref: /reactos/ntoskrnl/lpc/close.c (revision 6afbc8f4)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/lpc/close.c
5  * PURPOSE:         Local Procedure Call: Rundown, Cleanup, Deletion
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 LpcExitThread(IN PETHREAD Thread)
20 {
21     PLPCP_MESSAGE Message;
22     ASSERT(Thread == PsGetCurrentThread());
23 
24     /* Acquire the lock */
25     KeAcquireGuardedMutex(&LpcpLock);
26 
27     /* Make sure that the Reply Chain is empty */
28     if (!IsListEmpty(&Thread->LpcReplyChain))
29     {
30         /* It's not, remove the entry */
31         RemoveEntryList(&Thread->LpcReplyChain);
32     }
33 
34     /* Set the thread in exit mode */
35     Thread->LpcExitThreadCalled = TRUE;
36     Thread->LpcReplyMessageId = 0;
37 
38     /* Check if there's a reply message */
39     Message = LpcpGetMessageFromThread(Thread);
40     if (Message)
41     {
42         /* FIXME: TODO */
43         ASSERT(FALSE);
44     }
45 
46     /* Release the lock */
47     KeReleaseGuardedMutex(&LpcpLock);
48 }
49 
50 VOID
51 NTAPI
52 LpcpFreeToPortZone(IN PLPCP_MESSAGE Message,
53                    IN ULONG Flags)
54 {
55     PLPCP_CONNECTION_MESSAGE ConnectMessage;
56     PLPCP_PORT_OBJECT ClientPort = NULL;
57     PETHREAD Thread = NULL;
58     BOOLEAN LockHeld = Flags & 1, ReleaseLock = Flags & 2;
59     PAGED_CODE();
60     LPCTRACE(LPC_CLOSE_DEBUG, "Message: %p. Flags: %lx\n", Message, Flags);
61 
62     /* Acquire the lock if not already */
63     if (!LockHeld) KeAcquireGuardedMutex(&LpcpLock);
64 
65     /* Check if the queue list is empty */
66     if (!IsListEmpty(&Message->Entry))
67     {
68         /* Remove and re-initialize */
69         RemoveEntryList(&Message->Entry);
70         InitializeListHead(&Message->Entry);
71     }
72 
73     /* Check if we've already replied */
74     if (Message->RepliedToThread)
75     {
76         /* Set thread to dereference and clean up */
77         Thread = Message->RepliedToThread;
78         Message->RepliedToThread = NULL;
79     }
80 
81     /* Check if this is a connection request */
82     if (Message->Request.u2.s2.Type == LPC_CONNECTION_REQUEST)
83     {
84         /* Get the connection message */
85         ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1);
86 
87         /* Clear the client port */
88         ClientPort = ConnectMessage->ClientPort;
89         if (ClientPort) ConnectMessage->ClientPort = NULL;
90     }
91 
92     /* Release the lock */
93     KeReleaseGuardedMutex(&LpcpLock);
94 
95     /* Check if we had anything to dereference */
96     if (Thread) ObDereferenceObject(Thread);
97     if (ClientPort) ObDereferenceObject(ClientPort);
98 
99     /* Free the entry */
100     ExFreeToPagedLookasideList(&LpcpMessagesLookaside, Message);
101 
102     /* Reacquire the lock if needed */
103     if ((LockHeld) && !(ReleaseLock)) KeAcquireGuardedMutex(&LpcpLock);
104 }
105 
106 VOID
107 NTAPI
108 LpcpDestroyPortQueue(IN PLPCP_PORT_OBJECT Port,
109                      IN BOOLEAN Destroy)
110 {
111     PLIST_ENTRY ListHead, NextEntry;
112     PETHREAD Thread;
113     PLPCP_MESSAGE Message;
114     PLPCP_PORT_OBJECT ConnectionPort = NULL;
115     PLPCP_CONNECTION_MESSAGE ConnectMessage;
116     PAGED_CODE();
117     LPCTRACE(LPC_CLOSE_DEBUG, "Port: %p. Flags: %lx\n", Port, Port->Flags);
118 
119     /* Hold the lock */
120     KeAcquireGuardedMutex(&LpcpLock);
121 
122     /* Check if we have a connected port */
123     if (((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_UNCONNECTED_PORT) &&
124         (Port->ConnectedPort))
125     {
126         /* Disconnect it */
127         Port->ConnectedPort->ConnectedPort = NULL;
128         ConnectionPort = Port->ConnectedPort->ConnectionPort;
129         if (ConnectionPort)
130         {
131             /* Clear connection port */
132             Port->ConnectedPort->ConnectionPort = NULL;
133         }
134     }
135 
136     /* Check if this is a connection port */
137     if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CONNECTION_PORT)
138     {
139         /* Delete the name */
140         Port->Flags |= LPCP_NAME_DELETED;
141     }
142 
143     /* Walk all the threads waiting and signal them */
144     ListHead = &Port->LpcReplyChainHead;
145     NextEntry = ListHead->Flink;
146     while ((NextEntry) && (NextEntry != ListHead))
147     {
148         /* Get the Thread */
149         Thread = CONTAINING_RECORD(NextEntry, ETHREAD, LpcReplyChain);
150 
151         /* Make sure we're not in exit */
152         if (Thread->LpcExitThreadCalled) break;
153 
154         /* Move to the next entry */
155         NextEntry = NextEntry->Flink;
156 
157         /* Remove and reinitialize the List */
158         RemoveEntryList(&Thread->LpcReplyChain);
159         InitializeListHead(&Thread->LpcReplyChain);
160 
161         /* Check if someone is waiting */
162         if (!KeReadStateSemaphore(&Thread->LpcReplySemaphore))
163         {
164             /* Get the message */
165             Message = LpcpGetMessageFromThread(Thread);
166             if (Message)
167             {
168                 /* Check if it's a connection request */
169                 if (Message->Request.u2.s2.Type == LPC_CONNECTION_REQUEST)
170                 {
171                     /* Get the connection message */
172                     ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1);
173 
174                     /* Check if it had a section */
175                     if (ConnectMessage->SectionToMap)
176                     {
177                         /* Dereference it */
178                         ObDereferenceObject(ConnectMessage->SectionToMap);
179                     }
180                 }
181 
182                 /* Clear the reply message */
183                 Thread->LpcReplyMessage = NULL;
184 
185                 /* And remove the message from the port zone */
186                 LpcpFreeToPortZone(Message, 1);
187                 NextEntry = Port->LpcReplyChainHead.Flink;
188             }
189 
190             /* Release the semaphore and reset message id count */
191             Thread->LpcReplyMessageId = 0;
192             KeReleaseSemaphore(&Thread->LpcReplySemaphore, 0, 1, FALSE);
193         }
194     }
195 
196     /* Reinitialize the list head */
197     InitializeListHead(&Port->LpcReplyChainHead);
198 
199     /* Loop queued messages */
200     while ((Port->MsgQueue.ReceiveHead.Flink) &&
201            !(IsListEmpty(&Port->MsgQueue.ReceiveHead)))
202     {
203         /* Get the message */
204         Message = CONTAINING_RECORD(Port->MsgQueue.ReceiveHead.Flink,
205                                     LPCP_MESSAGE,
206                                     Entry);
207 
208         /* Free and reinitialize it's list head */
209         RemoveEntryList(&Message->Entry);
210         InitializeListHead(&Message->Entry);
211 
212         /* Remove it from the port zone */
213         LpcpFreeToPortZone(Message, 1);
214     }
215 
216     /* Release the lock */
217     KeReleaseGuardedMutex(&LpcpLock);
218 
219     /* Dereference the connection port */
220     if (ConnectionPort) ObDereferenceObject(ConnectionPort);
221 
222     /* Check if we have to free the port entirely */
223     if (Destroy)
224     {
225         /* Check if the semaphore exists */
226         if (Port->MsgQueue.Semaphore)
227         {
228             /* Use the semaphore to find the port queue and free it */
229             ExFreePool(CONTAINING_RECORD(Port->MsgQueue.Semaphore,
230                                          LPCP_NONPAGED_PORT_QUEUE,
231                                          Semaphore));
232         }
233     }
234 }
235 
236 VOID
237 NTAPI
238 LpcpClosePort(IN PEPROCESS Process OPTIONAL,
239               IN PVOID Object,
240               IN ACCESS_MASK GrantedAccess,
241               IN ULONG ProcessHandleCount,
242               IN ULONG SystemHandleCount)
243 {
244     PLPCP_PORT_OBJECT Port = (PLPCP_PORT_OBJECT)Object;
245     LPCTRACE(LPC_CLOSE_DEBUG, "Port: %p. Flags: %lx\n", Port, Port->Flags);
246 
247     /* Only Server-side Connection Ports need clean up*/
248     if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CONNECTION_PORT)
249     {
250         /* Check the handle count */
251         switch (SystemHandleCount)
252         {
253             /* No handles left */
254             case 0:
255 
256                 /* Destroy the port queue */
257                 LpcpDestroyPortQueue(Port, TRUE);
258                 break;
259 
260             /* Last handle remaining */
261             case 1:
262 
263                 /* Reset the queue only */
264                 LpcpDestroyPortQueue(Port, FALSE);
265 
266             /* More handles remain, do nothing */
267             default:
268                 break;
269         }
270     }
271 }
272 
273 VOID
274 NTAPI
275 LpcpFreePortClientSecurity(IN PLPCP_PORT_OBJECT Port)
276 {
277     /* Check if this is a client port */
278     if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
279     {
280         /* Check if security is static */
281         if (!(Port->Flags & LPCP_SECURITY_DYNAMIC))
282         {
283             /* Check if we have a token */
284             if (Port->StaticSecurity.ClientToken)
285             {
286                 /* Free security */
287                 SeDeleteClientSecurity(&Port->StaticSecurity);
288             }
289         }
290     }
291 }
292 
293 VOID
294 NTAPI
295 LpcpDeletePort(IN PVOID ObjectBody)
296 {
297     LARGE_INTEGER Timeout;
298     PETHREAD Thread;
299     PLPCP_PORT_OBJECT Port = (PLPCP_PORT_OBJECT)ObjectBody;
300     PLPCP_PORT_OBJECT ConnectionPort;
301     PLPCP_MESSAGE Message;
302     PLIST_ENTRY ListHead, NextEntry;
303     HANDLE Pid;
304     CLIENT_DIED_MSG ClientDiedMsg;
305     Timeout.QuadPart = -1000000;
306     PAGED_CODE();
307     LPCTRACE(LPC_CLOSE_DEBUG, "Port: %p. Flags: %lx\n", Port, Port->Flags);
308 
309     /* Check if this is a communication port */
310     if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_COMMUNICATION_PORT)
311     {
312         /* Acquire the lock */
313         KeAcquireGuardedMutex(&LpcpLock);
314 
315         /* Get the thread */
316         Thread = Port->ClientThread;
317         if (Thread)
318         {
319             /* Clear it */
320             Port->ClientThread = NULL;
321 
322             /* Release the lock and dereference */
323             KeReleaseGuardedMutex(&LpcpLock);
324             ObDereferenceObject(Thread);
325         }
326         else
327         {
328             /* Release the lock */
329             KeReleaseGuardedMutex(&LpcpLock);
330         }
331     }
332 
333     /* Check if this is a client-side port */
334     if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
335     {
336         /* Setup the client died message */
337         ClientDiedMsg.h.u1.s1.TotalLength = sizeof(ClientDiedMsg);
338         ClientDiedMsg.h.u1.s1.DataLength = sizeof(ClientDiedMsg.CreateTime);
339         ClientDiedMsg.h.u2.ZeroInit = 0;
340         ClientDiedMsg.h.u2.s2.Type = LPC_PORT_CLOSED;
341         ClientDiedMsg.CreateTime = PsGetCurrentProcess()->CreateTime;
342 
343         /* Send it */
344         for (;;)
345         {
346             /* Send the message */
347             if (LpcRequestPort(Port,
348                                &ClientDiedMsg.h) != STATUS_NO_MEMORY) break;
349 
350             /* Wait until trying again */
351             KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
352         }
353     }
354 
355     /* Destroy the port queue */
356     LpcpDestroyPortQueue(Port, TRUE);
357 
358     /* Check if we had views */
359     if ((Port->ClientSectionBase) || (Port->ServerSectionBase))
360     {
361         /* Check if we had a client view */
362         if (Port->ClientSectionBase)
363         {
364             /* Unmap it */
365             MmUnmapViewOfSection(Port->MappingProcess,
366                                  Port->ClientSectionBase);
367         }
368 
369         /* Check for a server view */
370         if (Port->ServerSectionBase)
371         {
372             /* Unmap it */
373             MmUnmapViewOfSection(Port->MappingProcess,
374                                  Port->ServerSectionBase);
375         }
376 
377         /* Dereference the mapping process */
378         ObDereferenceObject(Port->MappingProcess);
379         Port->MappingProcess = NULL;
380     }
381 
382     /* Acquire the lock */
383     KeAcquireGuardedMutex(&LpcpLock);
384 
385     /* Get the connection port */
386     ConnectionPort = Port->ConnectionPort;
387     if (ConnectionPort)
388     {
389         /* Get the PID */
390         Pid = PsGetCurrentProcessId();
391 
392         /* Loop the data lists */
393         ListHead = &ConnectionPort->LpcDataInfoChainHead;
394         NextEntry = ListHead->Flink;
395         while (NextEntry != ListHead)
396         {
397             /* Get the message */
398             Message = CONTAINING_RECORD(NextEntry, LPCP_MESSAGE, Entry);
399             NextEntry = NextEntry->Flink;
400 
401             /* Check if this is the connection port */
402             if (Port == ConnectionPort)
403             {
404                 /* Free queued messages */
405                 RemoveEntryList(&Message->Entry);
406                 InitializeListHead(&Message->Entry);
407                 LpcpFreeToPortZone(Message, 1);
408 
409                 /* Restart at the head */
410                 NextEntry = ListHead->Flink;
411             }
412             else if ((Message->Request.ClientId.UniqueProcess == Pid) &&
413                      ((Message->SenderPort == Port) ||
414                       (Message->SenderPort == Port->ConnectedPort) ||
415                       (Message->SenderPort == ConnectionPort)))
416             {
417                 /* Remove it */
418                 RemoveEntryList(&Message->Entry);
419                 InitializeListHead(&Message->Entry);
420                 LpcpFreeToPortZone(Message, 1);
421 
422                 /* Restart at the head */
423                 NextEntry = ListHead->Flink;
424             }
425         }
426 
427         /* Release the lock */
428         KeReleaseGuardedMutex(&LpcpLock);
429 
430         /* Dereference the object unless it's the same port */
431         if (ConnectionPort != Port) ObDereferenceObject(ConnectionPort);
432     }
433     else
434     {
435         /* Release the lock */
436         KeReleaseGuardedMutex(&LpcpLock);
437     }
438 
439     /* Check if this is a connection port with a server process*/
440     if (((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CONNECTION_PORT) &&
441         (ConnectionPort->ServerProcess))
442     {
443         /* Dereference the server process */
444         ObDereferenceObject(ConnectionPort->ServerProcess);
445         ConnectionPort->ServerProcess = NULL;
446     }
447 
448     /* Free client security */
449     LpcpFreePortClientSecurity(Port);
450     LPCTRACE(LPC_CLOSE_DEBUG, "Port: %p deleted\n", Port);
451 }
452 
453 /* EOF */
454