implemented sweeping of handle tables
[reactos.git] / reactos / ntoskrnl / ex / handle.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ex/handle.c
6 * PURPOSE: Generic Executive Handle Tables
7 *
8 * PROGRAMMERS: Thomas Weidenmueller <w3seek@reactos.com>
9 *
10 * TODO:
11 *
12 * - the last entry of a subhandle list should be reserved for auditing
13 *
14 * ExReferenceHandleDebugInfo
15 * ExSnapShotHandleTables
16 * ExpMoveFreeHandles (???)
17 */
18
19 /* INCLUDES *****************************************************************/
20
21 #include <ntoskrnl.h>
22
23 #define NDEBUG
24 #include <internal/debug.h>
25
26 static LIST_ENTRY ExpHandleTableHead;
27 static FAST_MUTEX ExpHandleTableListLock;
28 static LARGE_INTEGER ExpHandleShortWait;
29
30 #define ExAcquireHandleTableListLock() \
31 ExAcquireFastMutexUnsafe(&ExpHandleTableListLock)
32
33 #define ExReleaseHandleTableListLock() \
34 ExReleaseFastMutexUnsafe(&ExpHandleTableListLock)
35
36 #define ExAcquireHandleTableLockExclusive(HandleTable) \
37 ExAcquireResourceExclusiveLite(&(HandleTable)->HandleTableLock, TRUE)
38
39 #define ExAcquireHandleTableLockShared(HandleTable) \
40 ExAcquireResourceSharedLite(&(HandleTable)->HandleTableLock, TRUE)
41
42 #define ExReleaseHandleTableLock(HandleTable) \
43 ExReleaseResourceLite(&(HandleTable)->HandleTableLock)
44
45 /*
46 5 bits: reserved
47 8 bits: top level index
48 10 bits: middle level index
49 9 bits: sub handle index
50 */
51 #define N_TLI_BITS 8 /* top level index */
52 #define N_MLI_BITS 10 /* middle level index */
53 #define N_EI_BITS 9 /* sub handle index */
54 #define TLI_OFFSET (N_MLI_BITS + N_EI_BITS)
55 #define MLI_OFFSET N_EI_BITS
56 #define EI_OFFSET 0
57
58 #define N_TOPLEVEL_POINTERS (1 << N_TLI_BITS)
59 #define N_MIDDLELEVEL_POINTERS (1 << N_MLI_BITS)
60 #define N_SUBHANDLE_ENTRIES (1 << N_EI_BITS)
61 #define EX_MAX_HANDLES (N_TOPLEVEL_POINTERS * N_MIDDLELEVEL_POINTERS * N_SUBHANDLE_ENTRIES)
62
63 #define VALID_HANDLE_MASK (((N_TOPLEVEL_POINTERS - 1) << TLI_OFFSET) | \
64 ((N_MIDDLELEVEL_POINTERS - 1) << MLI_OFFSET) | ((N_SUBHANDLE_ENTRIES - 1) << EI_OFFSET))
65 #define TLI_FROM_HANDLE(index) (ULONG)(((index) >> TLI_OFFSET) & (N_TOPLEVEL_POINTERS - 1))
66 #define MLI_FROM_HANDLE(index) (ULONG)(((index) >> MLI_OFFSET) & (N_MIDDLELEVEL_POINTERS - 1))
67 #define ELI_FROM_HANDLE(index) (ULONG)(((index) >> EI_OFFSET) & (N_SUBHANDLE_ENTRIES - 1))
68
69 #define N_MAX_HANDLE (N_TOPLEVEL_POINTERS * N_MIDDLELEVEL_POINTERS * N_SUBHANDLE_ENTRIES)
70
71 #define BUILD_HANDLE(tli, mli, eli) ((((tli) & (N_TOPLEVEL_POINTERS - 1)) << TLI_OFFSET) | \
72 (((mli) & (N_MIDDLELEVEL_POINTERS - 1)) << MLI_OFFSET) | (((eli) & (N_SUBHANDLE_ENTRIES - 1)) << EI_OFFSET))
73
74 #define IS_INVALID_EX_HANDLE(index) \
75 (((index) & ~VALID_HANDLE_MASK) != 0)
76 #define IS_VALID_EX_HANDLE(index) \
77 (((index) & ~VALID_HANDLE_MASK) == 0)
78
79 #define HANDLE_TO_EX_HANDLE(handle) \
80 (LONG)(((LONG)(handle) >> 2) - 1)
81 #define EX_HANDLE_TO_HANDLE(exhandle) \
82 (HANDLE)(((exhandle) + 1) << 2)
83
84 static BOOLEAN ExpInitialized = FALSE;
85
86 /******************************************************************************/
87
88 VOID
89 ExpInitializeHandleTables(VOID)
90 {
91 ExpHandleShortWait.QuadPart = -50000;
92 InitializeListHead(&ExpHandleTableHead);
93 ExInitializeFastMutex(&ExpHandleTableListLock);
94
95 ExpInitialized = TRUE;
96 }
97
98 PHANDLE_TABLE
99 ExCreateHandleTable(IN PEPROCESS QuotaProcess OPTIONAL)
100 {
101 PHANDLE_TABLE HandleTable;
102
103 PAGED_CODE();
104
105 if(!ExpInitialized)
106 {
107 KEBUGCHECK(0);
108 }
109
110 if(QuotaProcess != NULL)
111 {
112 /* FIXME - Charge process quota before allocating the handle table! */
113 }
114
115 /* allocate enough memory for the handle table and the lowest level */
116 HandleTable = ExAllocatePoolWithTag(NonPagedPool,
117 sizeof(HANDLE_TABLE) + (N_TOPLEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY*)),
118 TAG('E', 'x', 'H', 't'));
119 if(HandleTable != NULL)
120 {
121 /* initialize the handle table */
122 HandleTable->Flags = 0;
123 HandleTable->HandleCount = 0;
124 HandleTable->Table = (PHANDLE_TABLE_ENTRY**)(HandleTable + 1);
125 HandleTable->QuotaProcess = QuotaProcess;
126 HandleTable->FirstFreeTableEntry = -1; /* no entries freed so far */
127 HandleTable->NextIndexNeedingPool = 0; /* no entries freed so far, so we have to allocate already for the first handle */
128 HandleTable->UniqueProcessId = (QuotaProcess ? QuotaProcess->UniqueProcessId : NULL);
129
130 ExInitializeResource(&HandleTable->HandleTableLock);
131
132 KeInitializeEvent(&HandleTable->HandleContentionEvent,
133 NotificationEvent,
134 FALSE);
135
136 RtlZeroMemory(HandleTable->Table, N_TOPLEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY*));
137
138 /* during bootup KeGetCurrentThread() might be NULL, needs to be fixed... */
139 if(KeGetCurrentThread() != NULL)
140 {
141 /* insert it into the global handle table list */
142 KeEnterCriticalRegion();
143
144 ExAcquireHandleTableListLock();
145 InsertTailList(&ExpHandleTableHead,
146 &HandleTable->HandleTableList);
147 ExReleaseHandleTableListLock();
148
149 KeLeaveCriticalRegion();
150 }
151 else
152 {
153 InsertTailList(&ExpHandleTableHead,
154 &HandleTable->HandleTableList);
155 }
156 }
157 else
158 {
159 /* FIXME - return the quota to the process */
160 }
161
162 return HandleTable;
163 }
164
165 VOID
166 ExSweepHandleTable(IN PHANDLE_TABLE HandleTable,
167 IN PEX_SWEEP_HANDLE_CALLBACK SweepHandleCallback OPTIONAL,
168 IN PVOID Context OPTIONAL)
169 {
170 PHANDLE_TABLE_ENTRY **tlp, **lasttlp, *mlp, *lastmlp;
171
172 PAGED_CODE();
173
174 ASSERT(HandleTable);
175
176 KeEnterCriticalRegion();
177
178 /* ensure there's no other operations going by acquiring an exclusive lock */
179 ExAcquireHandleTableLockExclusive(HandleTable);
180
181 ASSERT(!(HandleTable->Flags & EX_HANDLE_TABLE_CLOSING));
182
183 HandleTable->Flags |= EX_HANDLE_TABLE_CLOSING;
184
185 KePulseEvent(&HandleTable->HandleContentionEvent,
186 EVENT_INCREMENT,
187 FALSE);
188
189 /* call the callback function to cleanup the objects associated with the
190 handle table */
191 for(tlp = HandleTable->Table, lasttlp = HandleTable->Table + N_TOPLEVEL_POINTERS;
192 tlp != lasttlp;
193 tlp++)
194 {
195 if((*tlp) != NULL)
196 {
197 for(mlp = *tlp, lastmlp = (*tlp) + N_MIDDLELEVEL_POINTERS;
198 mlp != lastmlp;
199 mlp++)
200 {
201 if((*mlp) != NULL)
202 {
203 PHANDLE_TABLE_ENTRY curee, laste;
204
205 for(curee = *mlp, laste = *mlp + N_SUBHANDLE_ENTRIES;
206 curee != laste;
207 curee++)
208 {
209 if(curee->u1.Object != NULL && SweepHandleCallback != NULL)
210 {
211 curee->u1.ObAttributes |= EX_HANDLE_ENTRY_LOCKED;
212 SweepHandleCallback(HandleTable, curee->u1.Object, curee->u2.GrantedAccess, Context);
213 }
214 }
215 }
216 }
217 }
218 }
219
220 ExReleaseHandleTableLock(HandleTable);
221
222 KeLeaveCriticalRegion();
223 }
224
225 VOID
226 ExDestroyHandleTable(IN PHANDLE_TABLE HandleTable)
227 {
228 PHANDLE_TABLE_ENTRY **tlp, **lasttlp, *mlp, *lastmlp;
229 PEPROCESS QuotaProcess;
230
231 PAGED_CODE();
232
233 ASSERT(HandleTable);
234 ASSERT(HandleTable->Flags & EX_HANDLE_TABLE_CLOSING);
235
236 KeEnterCriticalRegion();
237
238 /* at this point the table should not be queried or altered anymore,
239 no locks should be necessary */
240
241 ASSERT(HandleTable->Flags & EX_HANDLE_TABLE_CLOSING);
242
243 /* remove the handle table from the global handle table list */
244 ExAcquireHandleTableListLock();
245 RemoveEntryList(&HandleTable->HandleTableList);
246 ExReleaseHandleTableListLock();
247
248 QuotaProcess = HandleTable->QuotaProcess;
249
250 /* free the tables */
251 for(tlp = HandleTable->Table, lasttlp = HandleTable->Table + N_TOPLEVEL_POINTERS;
252 tlp != lasttlp;
253 tlp++)
254 {
255 if((*tlp) != NULL)
256 {
257 for(mlp = *tlp, lastmlp = (*tlp) + N_MIDDLELEVEL_POINTERS;
258 mlp != lastmlp;
259 mlp++)
260 {
261 if((*mlp) != NULL)
262 {
263 ExFreePool(*mlp);
264
265 if(QuotaProcess != NULL)
266 {
267 /* FIXME - return the quota to the process */
268 }
269 }
270 }
271
272 ExFreePool(*tlp);
273
274 if(QuotaProcess != NULL)
275 {
276 /* FIXME - return the quota to the process */
277 }
278 }
279 }
280
281 KeLeaveCriticalRegion();
282
283 /* free the handle table */
284 ExDeleteResource(&HandleTable->HandleTableLock);
285 ExFreePool(HandleTable);
286
287 if(QuotaProcess != NULL)
288 {
289 /* FIXME - return the quota to the process */
290 }
291 }
292
293 PHANDLE_TABLE
294 ExDupHandleTable(IN PEPROCESS QuotaProcess OPTIONAL,
295 IN PEX_DUPLICATE_HANDLE_CALLBACK DuplicateHandleCallback OPTIONAL,
296 IN PVOID Context OPTIONAL,
297 IN PHANDLE_TABLE SourceHandleTable)
298 {
299 PHANDLE_TABLE HandleTable;
300
301 PAGED_CODE();
302
303 ASSERT(SourceHandleTable);
304
305 HandleTable = ExCreateHandleTable(QuotaProcess);
306 if(HandleTable != NULL)
307 {
308 PHANDLE_TABLE_ENTRY **tlp, **srctlp, **etlp, *mlp, *srcmlp, *emlp, stbl, srcstbl, estbl;
309 LONG tli, mli, eli;
310
311 tli = mli = eli = 0;
312
313 /* make sure the other handle table isn't being changed during the duplication */
314 ExAcquireHandleTableLockShared(SourceHandleTable);
315
316 /* allocate enough tables */
317 etlp = SourceHandleTable->Table + N_TOPLEVEL_POINTERS;
318 for(srctlp = SourceHandleTable->Table, tlp = HandleTable->Table;
319 srctlp != etlp;
320 srctlp++, tlp++)
321 {
322 if(*srctlp != NULL)
323 {
324 /* allocate middle level entry tables */
325 if(QuotaProcess != NULL)
326 {
327 /* FIXME - Charge process quota before allocating the handle table! */
328 }
329
330 *tlp = ExAllocatePoolWithTag(PagedPool,
331 N_MIDDLELEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY),
332 TAG('E', 'x', 'H', 't'));
333 if(*tlp != NULL)
334 {
335 RtlZeroMemory(*tlp, N_MIDDLELEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY));
336
337 KeMemoryBarrier();
338
339 emlp = *srctlp + N_MIDDLELEVEL_POINTERS;
340 for(srcmlp = *srctlp, mlp = *tlp;
341 srcmlp != emlp;
342 srcmlp++, mlp++)
343 {
344 if(*srcmlp != NULL)
345 {
346 /* allocate subhandle tables */
347 if(QuotaProcess != NULL)
348 {
349 /* FIXME - Charge process quota before allocating the handle table! */
350 }
351
352 *mlp = ExAllocatePoolWithTag(PagedPool,
353 N_SUBHANDLE_ENTRIES * sizeof(HANDLE_TABLE_ENTRY),
354 TAG('E', 'x', 'H', 't'));
355 if(*mlp != NULL)
356 {
357 RtlZeroMemory(*mlp, N_SUBHANDLE_ENTRIES * sizeof(HANDLE_TABLE_ENTRY));
358 }
359 else
360 {
361 goto freehandletable;
362 }
363 }
364 else
365 {
366 *mlp = NULL;
367 }
368 }
369 }
370 else
371 {
372 freehandletable:
373 DPRINT1("Failed to duplicate handle table 0x%p\n", SourceHandleTable);
374
375 ExReleaseHandleTableLock(SourceHandleTable);
376
377 ExDestroyHandleTable(HandleTable);
378 /* allocate an empty handle table */
379 return ExCreateHandleTable(QuotaProcess);
380 }
381 }
382 }
383
384 /* duplicate the handles */
385 HandleTable->HandleCount = SourceHandleTable->HandleCount;
386 HandleTable->FirstFreeTableEntry = SourceHandleTable->FirstFreeTableEntry;
387 HandleTable->NextIndexNeedingPool = SourceHandleTable->NextIndexNeedingPool;
388
389 /* make sure all tables are zeroed */
390 KeMemoryBarrier();
391
392 etlp = SourceHandleTable->Table + N_TOPLEVEL_POINTERS;
393 for(srctlp = SourceHandleTable->Table, tlp = HandleTable->Table;
394 srctlp != etlp;
395 srctlp++, tlp++, tli++)
396 {
397 if(*srctlp != NULL)
398 {
399 ASSERT(*tlp != NULL);
400
401 emlp = *srctlp + N_MIDDLELEVEL_POINTERS;
402 for(srcmlp = *srctlp, mlp = *tlp;
403 srcmlp != emlp;
404 srcmlp++, mlp++, mli++)
405 {
406 if(*srcmlp != NULL)
407 {
408 ASSERT(*mlp != NULL);
409
410 /* walk all handle entries and duplicate them if wanted */
411 estbl = *srcmlp + N_SUBHANDLE_ENTRIES;
412 for(srcstbl = *srcmlp, stbl = *mlp;
413 srcstbl != estbl;
414 srcstbl++, stbl++, eli++)
415 {
416 /* try to duplicate the source handle */
417 if(srcstbl->u1.Object != NULL &&
418 ExLockHandleTableEntry(SourceHandleTable,
419 srcstbl))
420 {
421 /* ask the caller if this handle should be duplicated */
422 if(DuplicateHandleCallback != NULL &&
423 !DuplicateHandleCallback(HandleTable,
424 srcstbl,
425 Context))
426 {
427 /* free the entry and chain it into the free list */
428 HandleTable->HandleCount--;
429 stbl->u1.Object = NULL;
430 stbl->u2.NextFreeTableEntry = HandleTable->FirstFreeTableEntry;
431 HandleTable->FirstFreeTableEntry = BUILD_HANDLE(tli, mli, eli);
432 }
433 else
434 {
435 /* duplicate the handle and unlock it */
436 stbl->u2.GrantedAccess = srcstbl->u2.GrantedAccess;
437 stbl->u1.ObAttributes = srcstbl->u1.ObAttributes & ~EX_HANDLE_ENTRY_LOCKED;
438 }
439 ExUnlockHandleTableEntry(SourceHandleTable,
440 srcstbl);
441 }
442 else
443 {
444 /* this is a free handle table entry, copy over the entire
445 structure as-is */
446 *stbl = *srcstbl;
447 }
448 }
449 }
450 }
451 }
452 }
453
454 /* release the source handle table */
455 ExReleaseHandleTableLock(SourceHandleTable);
456 }
457
458 return HandleTable;
459 }
460
461 static PHANDLE_TABLE_ENTRY
462 ExpAllocateHandleTableEntry(IN PHANDLE_TABLE HandleTable,
463 OUT PHANDLE Handle)
464 {
465 PHANDLE_TABLE_ENTRY Entry = NULL;
466
467 PAGED_CODE();
468
469 ASSERT(HandleTable);
470 ASSERT(Handle);
471 ASSERT(KeGetCurrentThread() != NULL);
472
473 DPRINT("HT[0x%p]: HandleCount: %d\n", HandleTable, HandleTable->HandleCount);
474
475 if(HandleTable->HandleCount < EX_MAX_HANDLES)
476 {
477 ULONG tli, mli, eli;
478
479 if(HandleTable->FirstFreeTableEntry != -1)
480 {
481 /* there's a free handle entry we can just grab and use */
482 tli = TLI_FROM_HANDLE(HandleTable->FirstFreeTableEntry);
483 mli = MLI_FROM_HANDLE(HandleTable->FirstFreeTableEntry);
484 eli = ELI_FROM_HANDLE(HandleTable->FirstFreeTableEntry);
485
486 /* the pointer should be valid in any way!!! */
487 ASSERT(HandleTable->Table[tli]);
488 ASSERT(HandleTable->Table[tli][mli]);
489
490 Entry = &HandleTable->Table[tli][mli][eli];
491
492 *Handle = EX_HANDLE_TO_HANDLE(HandleTable->FirstFreeTableEntry);
493
494 /* save the index to the next free handle (if available) */
495 HandleTable->FirstFreeTableEntry = Entry->u2.NextFreeTableEntry;
496 Entry->u2.NextFreeTableEntry = 0;
497 Entry->u1.Object = NULL;
498
499 HandleTable->HandleCount++;
500 }
501 else
502 {
503 /* we need to allocate a new subhandle table first */
504 PHANDLE_TABLE_ENTRY cure, laste, ntbl, *nmtbl;
505 ULONG i;
506 BOOLEAN AllocatedMtbl;
507
508 ASSERT(HandleTable->NextIndexNeedingPool <= N_MAX_HANDLE);
509
510 /* the index of the next table to be allocated was saved in
511 NextIndexNeedingPool the last time a handle entry was allocated and
512 the subhandle entry list was full. the subhandle entry index of
513 NextIndexNeedingPool should be 0 here! */
514 tli = TLI_FROM_HANDLE(HandleTable->NextIndexNeedingPool);
515 mli = MLI_FROM_HANDLE(HandleTable->NextIndexNeedingPool);
516 DPRINT("HandleTable->NextIndexNeedingPool: 0x%x\n", HandleTable->NextIndexNeedingPool);
517 DPRINT("tli: 0x%x mli: 0x%x eli: 0x%x\n", tli, mli, ELI_FROM_HANDLE(HandleTable->NextIndexNeedingPool));
518
519 ASSERT(ELI_FROM_HANDLE(HandleTable->NextIndexNeedingPool) == 0);
520
521 DPRINT("HandleTable->Table[%d] == 0x%p\n", tli, HandleTable->Table[tli]);
522
523 /* allocate a middle level entry list if required */
524 nmtbl = HandleTable->Table[tli];
525 if(nmtbl == NULL)
526 {
527 if(HandleTable->QuotaProcess != NULL)
528 {
529 /* FIXME - Charge process quota before allocating the handle table! */
530 }
531
532 nmtbl = ExAllocatePoolWithTag(PagedPool,
533 N_MIDDLELEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY),
534 TAG('E', 'x', 'H', 't'));
535 if(nmtbl == NULL)
536 {
537 if(HandleTable->QuotaProcess != NULL)
538 {
539 /* FIXME - return the quota to the process */
540 }
541
542 return NULL;
543 }
544
545 /* clear the middle level entry list */
546 RtlZeroMemory(nmtbl, N_MIDDLELEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY));
547
548 /* make sure the table was zeroed before we set one item */
549 KeMemoryBarrier();
550
551 /* note, don't set the the pointer in the top level list yet because we
552 might screw up lookups if allocating a subhandle entry table failed
553 and this newly allocated table might get freed again */
554 AllocatedMtbl = TRUE;
555 }
556 else
557 {
558 AllocatedMtbl = FALSE;
559
560 /* allocate a subhandle entry table in any case! */
561 ASSERT(nmtbl[mli] == NULL);
562 }
563
564 DPRINT("HandleTable->Table[%d][%d] == 0x%p\n", tli, mli, nmtbl[mli]);
565
566 if(HandleTable->QuotaProcess != NULL)
567 {
568 /* FIXME - Charge process quota before allocating the handle table! */
569 }
570
571 ntbl = ExAllocatePoolWithTag(PagedPool,
572 N_SUBHANDLE_ENTRIES * sizeof(HANDLE_TABLE_ENTRY),
573 TAG('E', 'x', 'H', 't'));
574 if(ntbl == NULL)
575 {
576 if(HandleTable->QuotaProcess != NULL)
577 {
578 /* FIXME - Return process quota charged */
579 }
580
581 /* free the middle level entry list, if allocated, because it's empty and
582 unused */
583 if(AllocatedMtbl)
584 {
585 ExFreePool(nmtbl);
586
587 if(HandleTable->QuotaProcess != NULL)
588 {
589 /* FIXME - Return process quota charged */
590 }
591 }
592
593 return NULL;
594 }
595
596 /* let's just use the very first entry */
597 Entry = ntbl;
598 Entry->u1.ObAttributes = EX_HANDLE_ENTRY_LOCKED;
599 Entry->u2.NextFreeTableEntry = 0;
600
601 *Handle = EX_HANDLE_TO_HANDLE(HandleTable->NextIndexNeedingPool);
602
603 HandleTable->HandleCount++;
604
605 /* set the FirstFreeTableEntry member to the second entry and chain the
606 free entries */
607 HandleTable->FirstFreeTableEntry = HandleTable->NextIndexNeedingPool + 1;
608 for(cure = Entry + 1, laste = Entry + N_SUBHANDLE_ENTRIES, i = HandleTable->FirstFreeTableEntry + 1;
609 cure != laste;
610 cure++, i++)
611 {
612 cure->u1.Object = NULL;
613 cure->u2.NextFreeTableEntry = i;
614 }
615 /* truncate the free entry list */
616 (cure - 1)->u2.NextFreeTableEntry = -1;
617
618 /* save the pointers to the allocated list(s) */
619 InterlockedExchangePointer(&nmtbl[mli], ntbl);
620 if(AllocatedMtbl)
621 {
622 InterlockedExchangePointer(&HandleTable->Table[tli], nmtbl);
623 }
624
625 /* increment the NextIndexNeedingPool to the next index where we need to
626 allocate new memory */
627 HandleTable->NextIndexNeedingPool += N_SUBHANDLE_ENTRIES;
628 }
629 }
630 else
631 {
632 DPRINT1("Can't allocate any more handles in handle table 0x%p!\n", HandleTable);
633 }
634
635 return Entry;
636 }
637
638 static VOID
639 ExpFreeHandleTableEntry(IN PHANDLE_TABLE HandleTable,
640 IN PHANDLE_TABLE_ENTRY Entry,
641 IN LONG Handle)
642 {
643 PAGED_CODE();
644
645 ASSERT(HandleTable);
646 ASSERT(Entry);
647 ASSERT(IS_VALID_EX_HANDLE(Handle));
648
649 DPRINT("ExpFreeHandleTableEntry HT:0x%p Entry:0x%p\n", HandleTable, Entry);
650
651 /* automatically unlock the entry if currently locked. We however don't notify
652 anyone who waited on the handle because we're holding an exclusive lock after
653 all and these locks will fail then */
654 InterlockedExchangePointer(&Entry->u1.Object, NULL);
655 Entry->u2.NextFreeTableEntry = HandleTable->FirstFreeTableEntry;
656 HandleTable->FirstFreeTableEntry = Handle;
657
658 HandleTable->HandleCount--;
659 }
660
661 static PHANDLE_TABLE_ENTRY
662 ExpLookupHandleTableEntry(IN PHANDLE_TABLE HandleTable,
663 IN LONG Handle)
664 {
665 PHANDLE_TABLE_ENTRY Entry = NULL;
666
667 PAGED_CODE();
668
669 ASSERT(HandleTable);
670
671 if(IS_VALID_EX_HANDLE(Handle))
672 {
673 ULONG tli, mli, eli;
674 PHANDLE_TABLE_ENTRY *mlp;
675
676 tli = TLI_FROM_HANDLE(Handle);
677 mli = MLI_FROM_HANDLE(Handle);
678 eli = ELI_FROM_HANDLE(Handle);
679
680 mlp = HandleTable->Table[tli];
681 if(Handle < HandleTable->NextIndexNeedingPool &&
682 mlp != NULL && mlp[mli] != NULL && mlp[mli][eli].u1.Object != NULL)
683 {
684 Entry = &mlp[mli][eli];
685 DPRINT("handle lookup 0x%x -> entry 0x%p [HT:0x%p] ptr: 0x%p\n", Handle, Entry, HandleTable, mlp[mli][eli].u1.Object);
686 }
687 }
688 else
689 {
690 DPRINT("Looking up invalid handle 0x%x\n", Handle);
691 }
692
693 return Entry;
694 }
695
696 BOOLEAN
697 ExLockHandleTableEntry(IN PHANDLE_TABLE HandleTable,
698 IN PHANDLE_TABLE_ENTRY Entry)
699 {
700 ULONG_PTR Current, New;
701
702 PAGED_CODE();
703
704 DPRINT("Entering handle table entry 0x%p lock...\n", Entry);
705
706 ASSERT(HandleTable);
707 ASSERT(Entry);
708
709 for(;;)
710 {
711 Current = (volatile ULONG_PTR)Entry->u1.Object;
712
713 if(!Current || (HandleTable->Flags & EX_HANDLE_TABLE_CLOSING))
714 {
715 DPRINT("Attempted to lock empty handle table entry 0x%p or handle table shut down\n", Entry);
716 break;
717 }
718
719 if(!(Current & EX_HANDLE_ENTRY_LOCKED))
720 {
721 New = Current | EX_HANDLE_ENTRY_LOCKED;
722 if(InterlockedCompareExchangePointer(&Entry->u1.Object,
723 (PVOID)New,
724 (PVOID)Current) == (PVOID)Current)
725 {
726 DPRINT("SUCCESS handle table 0x%p entry 0x%p lock\n", HandleTable, Entry);
727 /* we acquired the lock */
728 return TRUE;
729 }
730 }
731
732 /* wait about 5ms at maximum so we don't wait forever in unfortunate
733 co-incidences where releasing the lock in another thread happens right
734 before we're waiting on the contention event to get pulsed, which might
735 never happen again... */
736 KeWaitForSingleObject(&HandleTable->HandleContentionEvent,
737 Executive,
738 KernelMode,
739 FALSE,
740 &ExpHandleShortWait);
741 }
742
743 return FALSE;
744 }
745
746 VOID
747 ExUnlockHandleTableEntry(IN PHANDLE_TABLE HandleTable,
748 IN PHANDLE_TABLE_ENTRY Entry)
749 {
750 ULONG_PTR Current, New;
751
752 PAGED_CODE();
753
754 ASSERT(HandleTable);
755 ASSERT(Entry);
756
757 DPRINT("ExUnlockHandleTableEntry HT:0x%p Entry:0x%p\n", HandleTable, Entry);
758
759 Current = (volatile ULONG_PTR)Entry->u1.Object;
760
761 ASSERT(Current & EX_HANDLE_ENTRY_LOCKED);
762
763 New = Current & ~EX_HANDLE_ENTRY_LOCKED;
764
765 InterlockedExchangePointer(&Entry->u1.Object,
766 (PVOID)New);
767
768 /* we unlocked the entry, pulse the contention event so threads who're waiting
769 on the release can continue */
770 KePulseEvent(&HandleTable->HandleContentionEvent,
771 EVENT_INCREMENT,
772 FALSE);
773 }
774
775 HANDLE
776 ExCreateHandle(IN PHANDLE_TABLE HandleTable,
777 IN PHANDLE_TABLE_ENTRY Entry)
778 {
779 PHANDLE_TABLE_ENTRY NewHandleTableEntry;
780 HANDLE Handle = NULL;
781
782 PAGED_CODE();
783
784 ASSERT(HandleTable);
785 ASSERT(Entry);
786
787 /* The highest bit in Entry->u1.Object has to be 1 so we make sure it's a
788 pointer to kmode memory. It will cleared though because it also indicates
789 the lock */
790 ASSERT((ULONG_PTR)Entry->u1.Object & EX_HANDLE_ENTRY_LOCKED);
791
792 KeEnterCriticalRegion();
793 ExAcquireHandleTableLockExclusive(HandleTable);
794
795 NewHandleTableEntry = ExpAllocateHandleTableEntry(HandleTable,
796 &Handle);
797 if(NewHandleTableEntry != NULL)
798 {
799 *NewHandleTableEntry = *Entry;
800
801 ExUnlockHandleTableEntry(HandleTable,
802 NewHandleTableEntry);
803 }
804
805 ExReleaseHandleTableLock(HandleTable);
806 KeLeaveCriticalRegion();
807
808 return Handle;
809 }
810
811 BOOLEAN
812 ExDestroyHandle(IN PHANDLE_TABLE HandleTable,
813 IN HANDLE Handle)
814 {
815 PHANDLE_TABLE_ENTRY HandleTableEntry;
816 LONG ExHandle;
817 BOOLEAN Ret = FALSE;
818
819 PAGED_CODE();
820
821 ASSERT(HandleTable);
822
823 ExHandle = HANDLE_TO_EX_HANDLE(Handle);
824
825 KeEnterCriticalRegion();
826 ExAcquireHandleTableLockExclusive(HandleTable);
827
828 HandleTableEntry = ExpLookupHandleTableEntry(HandleTable,
829 ExHandle);
830
831 if(HandleTableEntry != NULL && ExLockHandleTableEntry(HandleTable, HandleTableEntry))
832 {
833 /* free and automatically unlock the handle. However we don't need to pulse
834 the contention event since other locks on this entry will fail */
835 ExpFreeHandleTableEntry(HandleTable,
836 HandleTableEntry,
837 ExHandle);
838 Ret = TRUE;
839 }
840
841 ExReleaseHandleTableLock(HandleTable);
842 KeLeaveCriticalRegion();
843
844 return Ret;
845 }
846
847 VOID
848 ExDestroyHandleByEntry(IN PHANDLE_TABLE HandleTable,
849 IN PHANDLE_TABLE_ENTRY Entry,
850 IN HANDLE Handle)
851 {
852 PAGED_CODE();
853
854 ASSERT(HandleTable);
855 ASSERT(Entry);
856
857 /* This routine requires the entry to be locked */
858 ASSERT((ULONG_PTR)Entry->u1.Object & EX_HANDLE_ENTRY_LOCKED);
859
860 DPRINT("DestroyHandleByEntry HT:0x%p Entry:0x%p\n", HandleTable, Entry);
861
862 KeEnterCriticalRegion();
863 ExAcquireHandleTableLockExclusive(HandleTable);
864
865 /* free and automatically unlock the handle. However we don't need to pulse
866 the contention event since other locks on this entry will fail */
867 ExpFreeHandleTableEntry(HandleTable,
868 Entry,
869 HANDLE_TO_EX_HANDLE(Handle));
870
871 ExReleaseHandleTableLock(HandleTable);
872 KeLeaveCriticalRegion();
873 }
874
875 PHANDLE_TABLE_ENTRY
876 ExMapHandleToPointer(IN PHANDLE_TABLE HandleTable,
877 IN HANDLE Handle)
878 {
879 PHANDLE_TABLE_ENTRY HandleTableEntry;
880
881 PAGED_CODE();
882
883 ASSERT(HandleTable);
884
885 HandleTableEntry = ExpLookupHandleTableEntry(HandleTable,
886 HANDLE_TO_EX_HANDLE(Handle));
887 if (HandleTableEntry != NULL && ExLockHandleTableEntry(HandleTable, HandleTableEntry))
888 {
889 DPRINT("ExMapHandleToPointer HT:0x%p Entry:0x%p locked\n", HandleTable, HandleTableEntry);
890 return HandleTableEntry;
891 }
892
893 return NULL;
894 }
895
896 BOOLEAN
897 ExChangeHandle(IN PHANDLE_TABLE HandleTable,
898 IN HANDLE Handle,
899 IN PEX_CHANGE_HANDLE_CALLBACK ChangeHandleCallback,
900 IN PVOID Context)
901 {
902 PHANDLE_TABLE_ENTRY HandleTableEntry;
903 BOOLEAN Ret = FALSE;
904
905 PAGED_CODE();
906
907 ASSERT(HandleTable);
908 ASSERT(ChangeHandleCallback);
909
910 KeEnterCriticalRegion();
911
912 HandleTableEntry = ExpLookupHandleTableEntry(HandleTable,
913 HANDLE_TO_EX_HANDLE(Handle));
914
915 if(HandleTableEntry != NULL && ExLockHandleTableEntry(HandleTable, HandleTableEntry))
916 {
917 Ret = ChangeHandleCallback(HandleTable,
918 HandleTableEntry,
919 NULL);
920
921 ExUnlockHandleTableEntry(HandleTable,
922 HandleTableEntry);
923 }
924
925 KeLeaveCriticalRegion();
926
927 return Ret;
928 }
929
930 /* EOF */