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