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