- Synchronize up to trunk's revision r57864.
[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 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 */