[CMAKE]
[reactos.git] / ntoskrnl / ex / handle.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ex/init.c
5 * PURPOSE: Generic Executive Handle Tables
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Thomas Weidenmueller <w3seek@reactos.com>
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS *******************************************************************/
17
18 LIST_ENTRY HandleTableListHead;
19 EX_PUSH_LOCK HandleTableListLock;
20 #define SizeOfHandle(x) (sizeof(HANDLE) * (x))
21
22 /* PRIVATE FUNCTIONS *********************************************************/
23
24 VOID
25 NTAPI
26 INIT_FUNCTION
27 ExpInitializeHandleTables(VOID)
28 {
29 /* Initialize the list of handle tables and the lock */
30 InitializeListHead(&HandleTableListHead);
31 ExInitializePushLock((PULONG_PTR)&HandleTableListLock);
32 }
33
34 PHANDLE_TABLE_ENTRY
35 NTAPI
36 ExpLookupHandleTableEntry(IN PHANDLE_TABLE HandleTable,
37 IN EXHANDLE LookupHandle)
38 {
39 ULONG i, j, k, TableLevel, NextHandle;
40 ULONG_PTR TableBase;
41 PHANDLE_TABLE_ENTRY Entry = NULL;
42 EXHANDLE Handle = LookupHandle;
43 PUCHAR Level1, Level2, Level3;
44
45 /* Clear the tag bits and check what the next handle is */
46 Handle.TagBits = 0;
47 NextHandle = *(volatile ULONG*)&HandleTable->NextHandleNeedingPool;
48 if (Handle.Value >= NextHandle) return NULL;
49
50 /* Get the table code */
51 TableBase = *(volatile ULONG_PTR*)&HandleTable->TableCode;
52
53 /* Extract the table level and actual table base */
54 TableLevel = (ULONG)(TableBase & 3);
55 TableBase = TableBase - TableLevel;
56
57 /* Check what level we're running at */
58 switch (TableLevel)
59 {
60 /* Direct index */
61 case 0:
62
63 /* Use level 1 and just get the entry directly */
64 Level1 = (PUCHAR)TableBase;
65 Entry = (PVOID)&Level1[Handle.Value *
66 (sizeof(HANDLE_TABLE_ENTRY) /
67 SizeOfHandle(1))];
68 break;
69
70 /* Nested index into mid level */
71 case 1:
72
73 /* Get the second table and index into it */
74 Level2 = (PUCHAR)TableBase;
75 i = Handle.Value % SizeOfHandle(LOW_LEVEL_ENTRIES);
76
77 /* Substract this index, and get the next one */
78 Handle.Value -= i;
79 j = Handle.Value /
80 (SizeOfHandle(LOW_LEVEL_ENTRIES) / sizeof(PHANDLE_TABLE_ENTRY));
81
82 /* Now get the next table and get the entry from it */
83 Level1 = (PUCHAR)*(PHANDLE_TABLE_ENTRY*)&Level2[j];
84 Entry = (PVOID)&Level1[i *
85 (sizeof(HANDLE_TABLE_ENTRY) /
86 SizeOfHandle(1))];
87 break;
88
89 /* Nested index into high level */
90 case 2:
91
92 /* Start with the 3rd level table */
93 Level3 = (PUCHAR)TableBase;
94 i = Handle.Value % SizeOfHandle(LOW_LEVEL_ENTRIES);
95
96 /* Subtract this index and get the index for the next lower table */
97 Handle.Value -= i;
98 k = Handle.Value /
99 (SizeOfHandle(LOW_LEVEL_ENTRIES) / sizeof(PHANDLE_TABLE_ENTRY));
100
101 /* Get the remaining index in the 2nd level table */
102 j = k % (MID_LEVEL_ENTRIES * sizeof(PHANDLE_TABLE_ENTRY));
103
104 /* Get the remaining index, which is in the third table */
105 k -= j;
106 k /= MID_LEVEL_ENTRIES;
107
108 /* Extract the table level for the handle in each table */
109 Level2 = (PUCHAR)*(PHANDLE_TABLE_ENTRY*)&Level3[k];
110 Level1 = (PUCHAR)*(PHANDLE_TABLE_ENTRY*)&Level2[j];
111
112 /* Get the handle table entry */
113 Entry = (PVOID)&Level1[i *
114 (sizeof(HANDLE_TABLE_ENTRY) /
115 SizeOfHandle(1))];
116
117 default:
118
119 /* All done */
120 break;
121 }
122
123 /* Return the handle entry */
124 return Entry;
125 }
126
127 PVOID
128 NTAPI
129 ExpAllocateTablePagedPool(IN PEPROCESS Process OPTIONAL,
130 IN SIZE_T Size)
131 {
132 PVOID Buffer;
133
134 /* Do the allocation */
135 Buffer = ExAllocatePoolWithTag(PagedPool, Size, 'btbO');
136 if (Buffer)
137 {
138 /* Clear the memory */
139 RtlZeroMemory(Buffer, Size);
140
141 /* Check if we have a process to charge quota */
142 if (Process)
143 {
144 /* FIXME: Charge quota */
145 }
146 }
147
148 /* Return the allocated memory */
149 return Buffer;
150 }
151
152 PVOID
153 NTAPI
154 ExpAllocateTablePagedPoolNoZero(IN PEPROCESS Process OPTIONAL,
155 IN SIZE_T Size)
156 {
157 PVOID Buffer;
158
159 /* Do the allocation */
160 Buffer = ExAllocatePoolWithTag(PagedPool, Size, 'btbO');
161 if (Buffer)
162 {
163 /* Check if we have a process to charge quota */
164 if (Process)
165 {
166 /* FIXME: Charge quota */
167 }
168 }
169
170 /* Return the allocated memory */
171 return Buffer;
172 }
173
174 VOID
175 NTAPI
176 ExpFreeTablePagedPool(IN PEPROCESS Process OPTIONAL,
177 IN PVOID Buffer,
178 IN SIZE_T Size)
179 {
180 /* Free the buffer */
181 ExFreePool(Buffer);
182 if (Process)
183 {
184 /* FIXME: Release quota */
185 }
186 }
187
188 VOID
189 NTAPI
190 ExpFreeLowLevelTable(IN PEPROCESS Process,
191 IN PHANDLE_TABLE_ENTRY TableEntry)
192 {
193 /* Check if we have an entry */
194 if (TableEntry[0].Object)
195 {
196 /* Free the entry */
197 ExpFreeTablePagedPool(Process,
198 TableEntry[0].Object,
199 LOW_LEVEL_ENTRIES *
200 sizeof(HANDLE_TABLE_ENTRY_INFO));
201 }
202
203 /* Free the table */
204 ExpFreeTablePagedPool(Process, TableEntry, PAGE_SIZE);
205 }
206
207 VOID
208 NTAPI
209 ExpFreeHandleTable(IN PHANDLE_TABLE HandleTable)
210 {
211 PEPROCESS Process = HandleTable->QuotaProcess;
212 ULONG i, j;
213 ULONG_PTR TableCode = HandleTable->TableCode;
214 ULONG_PTR TableBase = TableCode & ~3;
215 ULONG TableLevel = (ULONG)(TableCode & 3);
216 PHANDLE_TABLE_ENTRY Level1, *Level2, **Level3;
217 PAGED_CODE();
218
219 /* Check which level we're at */
220 if (!TableLevel)
221 {
222 /* Select the first level table base and just free it */
223 Level1 = (PVOID)TableBase;
224 ExpFreeLowLevelTable(Process, Level1);
225 }
226 else if (TableLevel == 1)
227 {
228 /* Select the second level table base */
229 Level2 = (PVOID)TableBase;
230
231 /* Loop each mid level entry */
232 for (i = 0; i < MID_LEVEL_ENTRIES; i++)
233 {
234 /* Leave if we've reached the last entry */
235 if (!Level2[i]) break;
236
237 /* Free the second level table */
238 ExpFreeLowLevelTable(Process, Level2[i]);
239 }
240
241 /* Free the second level table */
242 ExpFreeTablePagedPool(Process, Level2, PAGE_SIZE);
243 }
244 else
245 {
246 /* Select the third level table base */
247 Level3 = (PVOID)TableBase;
248
249 /* Loop each high level entry */
250 for (i = 0; i < HIGH_LEVEL_ENTRIES; i++)
251 {
252 /* Leave if we've reached the last entry */
253 if (!Level3[i]) break;
254
255 /* Loop each mid level entry */
256 for (j = 0; j < MID_LEVEL_ENTRIES; j++)
257 {
258 /* Leave if we've reached the last entry */
259 if (!Level3[i][j]) break;
260
261 /* Free the second level table */
262 ExpFreeLowLevelTable(Process, Level3[i][j]);
263 }
264
265 /* Free the third level table entry */
266 ExpFreeTablePagedPool(Process, Level3[i], PAGE_SIZE);
267 }
268
269 /* Free the third level table */
270 ExpFreeTablePagedPool(Process,
271 Level3,
272 SizeOfHandle(HIGH_LEVEL_ENTRIES));
273 }
274
275 /* Free the actual table and check if we need to release quota */
276 ExFreePool(HandleTable);
277 if (Process)
278 {
279 /* FIXME: TODO */
280 }
281 }
282
283 VOID
284 NTAPI
285 ExpFreeHandleTableEntry(IN PHANDLE_TABLE HandleTable,
286 IN EXHANDLE Handle,
287 IN PHANDLE_TABLE_ENTRY HandleTableEntry)
288 {
289 ULONG OldValue, NewValue, *Free;
290 ULONG i;
291 PAGED_CODE();
292
293 /* Sanity checks */
294 ASSERT(HandleTableEntry->Object == NULL);
295 ASSERT(HandleTableEntry == ExpLookupHandleTableEntry(HandleTable, Handle));
296
297 /* Decrement the handle count */
298 InterlockedDecrement(&HandleTable->HandleCount);
299
300 /* Mark the handle as free */
301 NewValue = (ULONG)Handle.Value & ~(SizeOfHandle(1) - 1);
302
303 /* Check if we're FIFO */
304 if (!HandleTable->StrictFIFO)
305 {
306 /* Select a lock index */
307 i = (NewValue >> 2) % 4;
308
309 /* Select which entry to use */
310 Free = (HandleTable->HandleTableLock[i].Locked) ?
311 &HandleTable->FirstFree : &HandleTable->LastFree;
312 }
313 else
314 {
315 /* No need to worry about locking, take the last entry */
316 Free = &HandleTable->LastFree;
317 }
318
319 /* Start value change loop */
320 for (;;)
321 {
322 /* Get the current value and write */
323 OldValue = *Free;
324 HandleTableEntry->NextFreeTableEntry = (ULONG)OldValue;
325 if (InterlockedCompareExchange((PLONG) Free, NewValue, OldValue) == OldValue)
326 {
327 /* Break out, we're done. Make sure the handle value makes sense */
328 ASSERT((OldValue & FREE_HANDLE_MASK) <
329 HandleTable->NextHandleNeedingPool);
330 break;
331 }
332 }
333 }
334
335 PHANDLE_TABLE
336 NTAPI
337 ExpAllocateHandleTable(IN PEPROCESS Process OPTIONAL,
338 IN BOOLEAN NewTable)
339 {
340 PHANDLE_TABLE HandleTable;
341 PHANDLE_TABLE_ENTRY HandleTableTable, HandleEntry;
342 ULONG i;
343 PAGED_CODE();
344
345 /* Allocate the table */
346 HandleTable = ExAllocatePoolWithTag(PagedPool,
347 sizeof(HANDLE_TABLE),
348 'btbO');
349 if (!HandleTable) return NULL;
350
351 /* Check if we have a process */
352 if (Process)
353 {
354 /* FIXME: Charge quota */
355 }
356
357 /* Clear the table */
358 RtlZeroMemory(HandleTable, sizeof(HANDLE_TABLE));
359
360 /* Now allocate the first level structures */
361 HandleTableTable = ExpAllocateTablePagedPoolNoZero(Process, PAGE_SIZE);
362 if (!HandleTableTable)
363 {
364 /* Failed, free the table */
365 ExFreePool(HandleTable);
366 return NULL;
367 }
368
369 /* Write the pointer to our first level structures */
370 HandleTable->TableCode = (ULONG_PTR)HandleTableTable;
371
372 /* Initialize the first entry */
373 HandleEntry = &HandleTableTable[0];
374 HandleEntry->NextFreeTableEntry = -2;
375 HandleEntry->Value = 0;
376
377 /* Check if this is a new table */
378 if (NewTable)
379 {
380 /* Go past the root entry */
381 HandleEntry++;
382
383 /* Loop every low level entry */
384 for (i = 1; i < (LOW_LEVEL_ENTRIES - 1); i++)
385 {
386 /* Set up the free data */
387 HandleEntry->Value = 0;
388 HandleEntry->NextFreeTableEntry = (i + 1) * SizeOfHandle(1);
389
390 /* Move to the next entry */
391 HandleEntry++;
392 }
393
394 /* Terminate the last entry */
395 HandleEntry->Value = 0;
396 HandleEntry->NextFreeTableEntry = 0;
397 HandleTable->FirstFree = SizeOfHandle(1);
398 }
399
400 /* Set the next handle needing pool after our allocated page from above */
401 HandleTable->NextHandleNeedingPool = LOW_LEVEL_ENTRIES * SizeOfHandle(1);
402
403 /* Setup the rest of the handle table data */
404 HandleTable->QuotaProcess = Process;
405 HandleTable->UniqueProcessId = PsGetCurrentProcess()->UniqueProcessId;
406 HandleTable->Flags = 0;
407
408 /* Loop all the handle table locks */
409 for (i = 0; i < 4; i++)
410 {
411 /* Initialize the handle table lock */
412 ExInitializePushLock((PULONG_PTR)&HandleTable->HandleTableLock[i]);
413 }
414
415 /* Initialize the contention event lock and return the lock */
416 ExInitializePushLock((PULONG_PTR)&HandleTable->HandleContentionEvent);
417 return HandleTable;
418 }
419
420 PHANDLE_TABLE_ENTRY
421 NTAPI
422 ExpAllocateLowLevelTable(IN PHANDLE_TABLE HandleTable,
423 IN BOOLEAN DoInit)
424 {
425 ULONG i, Base;
426 PHANDLE_TABLE_ENTRY Low, HandleEntry;
427
428 /* Allocate the low level table */
429 Low = ExpAllocateTablePagedPoolNoZero(HandleTable->QuotaProcess,
430 PAGE_SIZE);
431 if (!Low) return NULL;
432
433 /* Setup the initial entry */
434 HandleEntry = &Low[0];
435 HandleEntry->NextFreeTableEntry = -2;
436 HandleEntry->Value = 0;
437
438 /* Check if we're initializing */
439 if (DoInit)
440 {
441 /* Go to the next entry and the base entry */
442 HandleEntry++;
443 Base = HandleTable->NextHandleNeedingPool + SizeOfHandle(2);
444
445 /* Loop each entry */
446 for (i = Base;
447 i < Base + SizeOfHandle(LOW_LEVEL_ENTRIES - 2);
448 i += SizeOfHandle(1))
449 {
450 /* Free this entry and move on to the next one */
451 HandleEntry->NextFreeTableEntry = i;
452 HandleEntry->Value = 0;
453 HandleEntry++;
454 }
455
456 /* Terminate the last entry */
457 HandleEntry->NextFreeTableEntry = 0;
458 HandleEntry->Value = 0;
459 }
460
461 /* Return the low level table */
462 return Low;
463 }
464
465 PHANDLE_TABLE_ENTRY*
466 NTAPI
467 ExpAllocateMidLevelTable(IN PHANDLE_TABLE HandleTable,
468 IN BOOLEAN DoInit,
469 OUT PHANDLE_TABLE_ENTRY *LowTableEntry)
470 {
471 PHANDLE_TABLE_ENTRY *Mid, Low;
472
473 /* Allocate the mid level table */
474 Mid = ExpAllocateTablePagedPool(HandleTable->QuotaProcess, PAGE_SIZE);
475 if (!Mid) return NULL;
476
477 /* Allocate a new low level for it */
478 Low = ExpAllocateLowLevelTable(HandleTable, DoInit);
479 if (!Low)
480 {
481 /* We failed, free the mid table */
482 ExpFreeTablePagedPool(HandleTable->QuotaProcess, Mid, PAGE_SIZE);
483 return NULL;
484 }
485
486 /* Link the tables and return the pointer */
487 Mid[0] = Low;
488 *LowTableEntry = Low;
489 return Mid;
490 }
491
492 BOOLEAN
493 NTAPI
494 ExpAllocateHandleTableEntrySlow(IN PHANDLE_TABLE HandleTable,
495 IN BOOLEAN DoInit)
496 {
497 ULONG i, j, Index;
498 PHANDLE_TABLE_ENTRY Low = NULL, *Mid, **High, *SecondLevel, **ThirdLevel;
499 ULONG NewFree, FirstFree;
500 PVOID Value;
501 ULONG_PTR TableCode = HandleTable->TableCode;
502 ULONG_PTR TableBase = TableCode & ~3;
503 ULONG TableLevel = (ULONG)(TableCode & 3);
504 PAGED_CODE();
505
506 /* Check how many levels we already have */
507 if (!TableLevel)
508 {
509 /* Allocate a mid level, since we only have a low level */
510 Mid = ExpAllocateMidLevelTable(HandleTable, DoInit, &Low);
511 if (!Mid) return FALSE;
512
513 /* Link up the tables */
514 Mid[1] = Mid[0];
515 Mid[0] = (PVOID)TableBase;
516
517 /* Write the new level and attempt to change the table code */
518 TableBase = ((ULONG_PTR)Mid) | 1;
519 Value = InterlockedExchangePointer((PVOID*)&HandleTable->TableCode, (PVOID)TableBase);
520 }
521 else if (TableLevel == 1)
522 {
523 /* Setup the 2nd level table */
524 SecondLevel = (PVOID)TableBase;
525
526 /* Get if the next index can fit in the table */
527 i = HandleTable->NextHandleNeedingPool /
528 SizeOfHandle(LOW_LEVEL_ENTRIES);
529 if (i < MID_LEVEL_ENTRIES)
530 {
531 /* We need to allocate a new table */
532 Low = ExpAllocateLowLevelTable(HandleTable, DoInit);
533 if (!Low) return FALSE;
534
535 /* Update the table */
536 Value = InterlockedExchangePointer((PVOID*)&SecondLevel[i], Low);
537 ASSERT(Value == NULL);
538 }
539 else
540 {
541 /* We need a new high level table */
542 High = ExpAllocateTablePagedPool(HandleTable->QuotaProcess,
543 SizeOfHandle(HIGH_LEVEL_ENTRIES));
544 if (!High) return FALSE;
545
546 /* Allocate a new mid level table as well */
547 Mid = ExpAllocateMidLevelTable(HandleTable, DoInit, &Low);
548 if (!Mid)
549 {
550 /* We failed, free the high level table as welll */
551 ExpFreeTablePagedPool(HandleTable->QuotaProcess,
552 High,
553 SizeOfHandle(HIGH_LEVEL_ENTRIES));
554 return FALSE;
555 }
556
557 /* Link up the tables */
558 High[0] = (PVOID)TableBase;
559 High[1] = Mid;
560
561 /* Write the new table and change the table code */
562 TableBase = ((ULONG_PTR)High) | 2;
563 Value = InterlockedExchangePointer((PVOID*)&HandleTable->TableCode,
564 (PVOID)TableBase);
565 }
566 }
567 else if (TableLevel == 2)
568 {
569 /* Setup the 3rd level table */
570 ThirdLevel = (PVOID)TableBase;
571
572 /* Get the index and check if it can fit */
573 i = HandleTable->NextHandleNeedingPool / SizeOfHandle(MAX_MID_INDEX);
574 if (i >= HIGH_LEVEL_ENTRIES) return FALSE;
575
576 /* Check if there's no mid-level table */
577 if (!ThirdLevel[i])
578 {
579 /* Allocate a new mid level table */
580 Mid = ExpAllocateMidLevelTable(HandleTable, DoInit, &Low);
581 if (!Mid) return FALSE;
582
583 /* Update the table pointer */
584 Value = InterlockedExchangePointer((PVOID*)&ThirdLevel[i], Mid);
585 ASSERT(Value == NULL);
586 }
587 else
588 {
589 /* We have one, check at which index we should insert our entry */
590 Index = (HandleTable->NextHandleNeedingPool / SizeOfHandle(1)) -
591 i * MAX_MID_INDEX;
592 j = Index / LOW_LEVEL_ENTRIES;
593
594 /* Allocate a new low level */
595 Low = ExpAllocateLowLevelTable(HandleTable, DoInit);
596 if (!Low) return FALSE;
597
598 /* Update the table pointer */
599 Value = InterlockedExchangePointer((PVOID*)&ThirdLevel[i][j], Low);
600 ASSERT(Value == NULL);
601 }
602 }
603
604 /* Update the index of the next handle */
605 Index = InterlockedExchangeAdd((PLONG) &HandleTable->NextHandleNeedingPool,
606 SizeOfHandle(LOW_LEVEL_ENTRIES));
607
608 /* Check if need to initialize the table */
609 if (DoInit)
610 {
611 /* Create a new index number */
612 Index += SizeOfHandle(1);
613
614 /* Start free index change loop */
615 for (;;)
616 {
617 /* Setup the first free index */
618 FirstFree = HandleTable->FirstFree;
619 Low[LOW_LEVEL_ENTRIES - 1].NextFreeTableEntry = FirstFree;
620
621 /* Change the index */
622 NewFree = InterlockedCompareExchange((PLONG) &HandleTable->FirstFree,
623 Index,
624 FirstFree);
625 if (NewFree == FirstFree) break;
626 }
627 }
628
629 /* All done */
630 return TRUE;
631 }
632
633 ULONG
634 NTAPI
635 ExpMoveFreeHandles(IN PHANDLE_TABLE HandleTable)
636 {
637 ULONG LastFree, i;
638
639 /* Clear the last free index */
640 LastFree = InterlockedExchange((PLONG) &HandleTable->LastFree, 0);
641
642 /* Check if we had no index */
643 if (!LastFree) return LastFree;
644
645 /* Acquire the locks we need */
646 for (i = 1; i < 4; i++)
647 {
648 /* Acquire this lock exclusively */
649 ExWaitOnPushLock(&HandleTable->HandleTableLock[i]);
650 }
651
652 /* Check if we're not strict FIFO */
653 if (!HandleTable->StrictFIFO)
654 {
655 /* Update the first free index */
656 if (!InterlockedCompareExchange((PLONG) &HandleTable->FirstFree, LastFree, 0))
657 {
658 /* We're done, exit */
659 return LastFree;
660 }
661 }
662
663 /* We are strict FIFO, we need to reverse the entries */
664 ASSERT(FALSE);
665 return LastFree;
666 }
667
668 PHANDLE_TABLE_ENTRY
669 NTAPI
670 ExpAllocateHandleTableEntry(IN PHANDLE_TABLE HandleTable,
671 OUT PEXHANDLE NewHandle)
672 {
673 ULONG OldValue, NewValue, NewValue1;
674 PHANDLE_TABLE_ENTRY Entry;
675 EXHANDLE Handle;
676 BOOLEAN Result;
677 ULONG i;
678
679 /* Start allocation loop */
680 for (;;)
681 {
682 /* Get the current link */
683 OldValue = HandleTable->FirstFree;
684 while (!OldValue)
685 {
686 /* No free entries remain, lock the handle table */
687 KeEnterCriticalRegion();
688 ExAcquirePushLockExclusive(&HandleTable->HandleTableLock[0]);
689
690 /* Check the value again */
691 OldValue = HandleTable->FirstFree;
692 if (OldValue)
693 {
694 /* Another thread has already created a new level, bail out */
695 ExReleasePushLockExclusive(&HandleTable->HandleTableLock[0]);
696 KeLeaveCriticalRegion();
697 break;
698 }
699
700 /* Now move any free handles */
701 OldValue = ExpMoveFreeHandles(HandleTable);
702 if (OldValue)
703 {
704 /* Another thread has already moved them, bail out */
705 ExReleasePushLockExclusive(&HandleTable->HandleTableLock[0]);
706 KeLeaveCriticalRegion();
707 break;
708 }
709
710 /* We're the first one through, so do the actual allocation */
711 Result = ExpAllocateHandleTableEntrySlow(HandleTable, TRUE);
712
713 /* Unlock the table and get the value now */
714 ExReleasePushLockExclusive(&HandleTable->HandleTableLock[0]);
715 KeLeaveCriticalRegion();
716 OldValue = HandleTable->FirstFree;
717
718 /* Check if allocation failed */
719 if (!Result)
720 {
721 /* Check if nobody else went through here */
722 if (!OldValue)
723 {
724 /* We're still the only thread around, so fail */
725 NewHandle->GenericHandleOverlay = NULL;
726 return NULL;
727 }
728 }
729 }
730
731 /* We made it, write the current value */
732 Handle.Value = (OldValue & FREE_HANDLE_MASK);
733
734 /* Lookup the entry for this handle */
735 Entry = ExpLookupHandleTableEntry(HandleTable, Handle);
736
737 /* Get an available lock and acquire it */
738 i = ((OldValue & FREE_HANDLE_MASK) >> 2) % 4;
739 KeEnterCriticalRegion();
740 ExAcquirePushLockShared(&HandleTable->HandleTableLock[i]);
741
742 /* Check if the value changed after acquiring the lock */
743 if (OldValue != *(volatile ULONG*)&HandleTable->FirstFree)
744 {
745 /* It did, so try again */
746 ExReleasePushLockShared(&HandleTable->HandleTableLock[i]);
747 KeLeaveCriticalRegion();
748 continue;
749 }
750
751 /* Now get the next value and do the compare */
752 NewValue = *(volatile ULONG*)&Entry->NextFreeTableEntry;
753 NewValue1 = InterlockedCompareExchange((PLONG) &HandleTable->FirstFree,
754 NewValue,
755 OldValue);
756
757 /* The change was done, so release the lock */
758 ExReleasePushLockShared(&HandleTable->HandleTableLock[i]);
759 KeLeaveCriticalRegion();
760
761 /* Check if the compare was successful */
762 if (NewValue1 == OldValue)
763 {
764 /* Make sure that the new handle is in range, and break out */
765 ASSERT((NewValue & FREE_HANDLE_MASK) <
766 HandleTable->NextHandleNeedingPool);
767 break;
768 }
769 else
770 {
771 /* The compare failed, make sure we expected it */
772 ASSERT((NewValue1 & FREE_HANDLE_MASK) !=
773 (OldValue & FREE_HANDLE_MASK));
774 }
775 }
776
777 /* Increase the number of handles */
778 InterlockedIncrement(&HandleTable->HandleCount);
779
780 /* Return the handle and the entry */
781 *NewHandle = Handle;
782 return Entry;
783 }
784
785 PHANDLE_TABLE
786 NTAPI
787 ExCreateHandleTable(IN PEPROCESS Process OPTIONAL)
788 {
789 PHANDLE_TABLE HandleTable;
790 PAGED_CODE();
791
792 /* Allocate the handle table */
793 HandleTable = ExpAllocateHandleTable(Process, TRUE);
794 if (!HandleTable) return NULL;
795
796 /* Acquire the handle table lock */
797 KeEnterCriticalRegion();
798 ExAcquirePushLockExclusive(&HandleTableListLock);
799
800 /* Insert it into the list */
801 InsertTailList(&HandleTableListHead, &HandleTable->HandleTableList);
802
803 /* Release the lock */
804 ExReleasePushLockExclusive(&HandleTableListLock);
805 KeLeaveCriticalRegion();
806
807 /* Return the handle table */
808 return HandleTable;
809 }
810
811 HANDLE
812 NTAPI
813 ExCreateHandle(IN PHANDLE_TABLE HandleTable,
814 IN PHANDLE_TABLE_ENTRY HandleTableEntry)
815 {
816 EXHANDLE Handle;
817 PHANDLE_TABLE_ENTRY NewEntry;
818 PAGED_CODE();
819
820 /* Start with a clean handle */
821 Handle.GenericHandleOverlay = NULL;
822
823 /* Allocate a new entry */
824 NewEntry = ExpAllocateHandleTableEntry(HandleTable, &Handle);
825 if (NewEntry)
826 {
827 /* Enter a critical region */
828 KeEnterCriticalRegion();
829
830 /* Write the entry */
831 *NewEntry = *HandleTableEntry;
832
833 /* Unlock it and leave the critical region */
834 ExUnlockHandleTableEntry(HandleTable, NewEntry);
835 KeLeaveCriticalRegion();
836 }
837
838 /* Return the handle value */
839 return Handle.GenericHandleOverlay;
840 }
841
842 VOID
843 NTAPI
844 ExpBlockOnLockedHandleEntry(IN PHANDLE_TABLE HandleTable,
845 IN PHANDLE_TABLE_ENTRY HandleTableEntry)
846 {
847 LONG_PTR OldValue;
848 DEFINE_WAIT_BLOCK(WaitBlock);
849
850 /* Block on the pushlock */
851 ExBlockPushLock(&HandleTable->HandleContentionEvent, WaitBlock);
852
853 /* Get the current value and check if it's been unlocked */
854 OldValue = HandleTableEntry->Value;
855 if (!(OldValue) || (OldValue & EXHANDLE_TABLE_ENTRY_LOCK_BIT))
856 {
857 /* Unblock the pushlock and return */
858 ExfUnblockPushLock(&HandleTable->HandleContentionEvent, WaitBlock);
859 }
860 else
861 {
862 /* Wait for it to be unblocked */
863 ExWaitForUnblockPushLock(&HandleTable->HandleContentionEvent,
864 WaitBlock);
865 }
866 }
867
868 BOOLEAN
869 NTAPI
870 ExpLockHandleTableEntry(IN PHANDLE_TABLE HandleTable,
871 IN PHANDLE_TABLE_ENTRY HandleTableEntry)
872 {
873 LONG_PTR NewValue, OldValue;
874
875 /* Sanity check */
876 ASSERT((KeGetCurrentThread()->CombinedApcDisable != 0) ||
877 (KeGetCurrentIrql() == APC_LEVEL));
878
879 /* Start lock loop */
880 for (;;)
881 {
882 /* Get the current value and check if it's locked */
883 OldValue = *(volatile LONG_PTR *)&HandleTableEntry->Object;
884 if (OldValue & EXHANDLE_TABLE_ENTRY_LOCK_BIT)
885 {
886 /* It's not locked, remove the lock bit to lock it */
887 NewValue = OldValue & ~EXHANDLE_TABLE_ENTRY_LOCK_BIT;
888 if (InterlockedCompareExchangePointer(&HandleTableEntry->Object,
889 (PVOID)NewValue,
890 (PVOID)OldValue) == (PVOID)OldValue)
891 {
892 /* We locked it, get out */
893 return TRUE;
894 }
895 }
896 else
897 {
898 /* We couldn't lock it, bail out if it's been freed */
899 if (!OldValue) return FALSE;
900 }
901
902 /* It's locked, wait for it to be unlocked */
903 ExpBlockOnLockedHandleEntry(HandleTable, HandleTableEntry);
904 }
905 }
906
907 VOID
908 NTAPI
909 ExUnlockHandleTableEntry(IN PHANDLE_TABLE HandleTable,
910 IN PHANDLE_TABLE_ENTRY HandleTableEntry)
911 {
912 LONG_PTR OldValue;
913 PAGED_CODE();
914
915 /* Sanity check */
916 ASSERT((KeGetCurrentThread()->CombinedApcDisable != 0) ||
917 (KeGetCurrentIrql() == APC_LEVEL));
918
919 /* Set the lock bit and make sure it wasn't earlier */
920 OldValue = InterlockedOr((PLONG) &HandleTableEntry->Value,
921 EXHANDLE_TABLE_ENTRY_LOCK_BIT);
922 ASSERT((OldValue & EXHANDLE_TABLE_ENTRY_LOCK_BIT) == 0);
923
924 /* Unblock any waiters */
925 ExfUnblockPushLock(&HandleTable->HandleContentionEvent, NULL);
926 }
927
928 VOID
929 NTAPI
930 ExRemoveHandleTable(IN PHANDLE_TABLE HandleTable)
931 {
932 PAGED_CODE();
933
934 /* Acquire the table lock */
935 KeEnterCriticalRegion();
936 ExAcquirePushLockExclusive(&HandleTableListLock);
937
938 /* Remove the table and reset the list */
939 RemoveEntryList(&HandleTable->HandleTableList);
940 InitializeListHead(&HandleTable->HandleTableList);
941
942 /* Release the lock */
943 ExReleasePushLockExclusive(&HandleTableListLock);
944 KeLeaveCriticalRegion();
945 }
946
947 VOID
948 NTAPI
949 ExDestroyHandleTable(IN PHANDLE_TABLE HandleTable,
950 IN PVOID DestroyHandleProcedure OPTIONAL)
951 {
952 PAGED_CODE();
953
954 /* Remove the handle from the list */
955 ExRemoveHandleTable(HandleTable);
956
957 /* Check if we have a desotry callback */
958 if (DestroyHandleProcedure)
959 {
960 /* FIXME: */
961 ASSERT(FALSE);
962 }
963
964 /* Free the handle table */
965 ExpFreeHandleTable(HandleTable);
966 }
967
968 BOOLEAN
969 NTAPI
970 ExDestroyHandle(IN PHANDLE_TABLE HandleTable,
971 IN HANDLE Handle,
972 IN PHANDLE_TABLE_ENTRY HandleTableEntry OPTIONAL)
973 {
974 EXHANDLE ExHandle;
975 PVOID Object;
976 PAGED_CODE();
977
978 /* Setup the actual handle value */
979 ExHandle.GenericHandleOverlay = Handle;
980
981 /* Enter a critical region and check if we have to lookup the handle */
982 KeEnterCriticalRegion();
983 if (!HandleTableEntry)
984 {
985 /* Lookup the entry */
986 HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, ExHandle);
987
988 /* Make sure that we found an entry, and that it's valid */
989 if (!(HandleTableEntry) ||
990 !(HandleTableEntry->Object) ||
991 (HandleTableEntry->NextFreeTableEntry == -2))
992 {
993 /* Invalid handle, fail */
994 KeLeaveCriticalRegion();
995 return FALSE;
996 }
997
998 /* Lock the entry */
999 if (!ExpLockHandleTableEntry(HandleTable, HandleTableEntry))
1000 {
1001 /* Couldn't lock, fail */
1002 KeLeaveCriticalRegion();
1003 return FALSE;
1004 }
1005 }
1006 else
1007 {
1008 /* Make sure the handle is locked */
1009 ASSERT((HandleTableEntry->Value & EXHANDLE_TABLE_ENTRY_LOCK_BIT) == 0);
1010 }
1011
1012 /* Clear the handle */
1013 Object = InterlockedExchangePointer((PVOID*)&HandleTableEntry->Object, NULL);
1014
1015 /* Sanity checks */
1016 ASSERT(Object != NULL);
1017 ASSERT((((ULONG_PTR)Object) & EXHANDLE_TABLE_ENTRY_LOCK_BIT) == 0);
1018
1019 /* Unblock the pushlock */
1020 ExfUnblockPushLock(&HandleTable->HandleContentionEvent, NULL);
1021
1022 /* Free the actual entry */
1023 ExpFreeHandleTableEntry(HandleTable, ExHandle, HandleTableEntry);
1024
1025 /* If we got here, return success */
1026 KeLeaveCriticalRegion();
1027 return TRUE;
1028 }
1029
1030 PHANDLE_TABLE_ENTRY
1031 NTAPI
1032 ExMapHandleToPointer(IN PHANDLE_TABLE HandleTable,
1033 IN HANDLE Handle)
1034 {
1035 EXHANDLE ExHandle;
1036 PHANDLE_TABLE_ENTRY HandleTableEntry;
1037 PAGED_CODE();
1038
1039 /* Set the handle value */
1040 ExHandle.GenericHandleOverlay = Handle;
1041
1042 /* Fail if we got an invalid index */
1043 if (!(ExHandle.Index & (LOW_LEVEL_ENTRIES - 1))) return NULL;
1044
1045 /* Do the lookup */
1046 HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, ExHandle);
1047 if (!HandleTableEntry) return NULL;
1048
1049 /* Lock it */
1050 if (!ExpLockHandleTableEntry(HandleTable, HandleTableEntry)) return NULL;
1051
1052 /* Return the entry */
1053 return HandleTableEntry;
1054 }
1055
1056 PHANDLE_TABLE
1057 NTAPI
1058 ExDupHandleTable(IN PEPROCESS Process,
1059 IN PHANDLE_TABLE HandleTable,
1060 IN PEX_DUPLICATE_HANDLE_CALLBACK DupHandleProcedure,
1061 IN ULONG_PTR Mask)
1062 {
1063 PHANDLE_TABLE NewTable;
1064 EXHANDLE Handle;
1065 PHANDLE_TABLE_ENTRY HandleTableEntry, NewEntry;
1066 BOOLEAN Failed = FALSE;
1067 PAGED_CODE();
1068
1069 /* Allocate the duplicated copy */
1070 NewTable = ExpAllocateHandleTable(Process, FALSE);
1071 if (!NewTable) return NULL;
1072
1073 /* Loop each entry */
1074 while (NewTable->NextHandleNeedingPool <
1075 HandleTable->NextHandleNeedingPool)
1076 {
1077 /* Insert it into the duplicated copy */
1078 if (!ExpAllocateHandleTableEntrySlow(NewTable, FALSE))
1079 {
1080 /* Insert failed, free the new copy and return */
1081 ExpFreeHandleTable(NewTable);
1082 return NULL;
1083 }
1084 }
1085
1086 /* Setup the initial handle table data */
1087 NewTable->HandleCount = 0;
1088 NewTable->ExtraInfoPages = 0;
1089 NewTable->FirstFree = 0;
1090
1091 /* Setup the first handle value */
1092 Handle.Value = SizeOfHandle(1);
1093
1094 /* Enter a critical region and lookup the new entry */
1095 KeEnterCriticalRegion();
1096 while ((NewEntry = ExpLookupHandleTableEntry(NewTable, Handle)))
1097 {
1098 /* Lookup the old entry */
1099 HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, Handle);
1100
1101 /* Loop each entry */
1102 do
1103 {
1104 /* Check if it doesn't match the audit mask */
1105 if (!(HandleTableEntry->Value & Mask))
1106 {
1107 /* Free it since we won't use it */
1108 Failed = TRUE;
1109 }
1110 else
1111 {
1112 /* Lock the entry */
1113 if (!ExpLockHandleTableEntry(HandleTable, HandleTableEntry))
1114 {
1115 /* Free it since we can't lock it, so we won't use it */
1116 Failed = TRUE;
1117 }
1118 else
1119 {
1120 /* Copy the handle value */
1121 *NewEntry = *HandleTableEntry;
1122
1123 /* Call the duplicate callback */
1124 if (DupHandleProcedure(Process,
1125 HandleTable,
1126 HandleTableEntry,
1127 NewEntry))
1128 {
1129 /* Clear failure flag */
1130 Failed = FALSE;
1131
1132 /* Lock the entry, increase the handle count */
1133 NewEntry->Value |= EXHANDLE_TABLE_ENTRY_LOCK_BIT;
1134 NewTable->HandleCount++;
1135 }
1136 else
1137 {
1138 /* Duplication callback refused, fail */
1139 Failed = TRUE;
1140 }
1141 }
1142 }
1143
1144 /* Check if we failed earlier and need to free */
1145 if (Failed)
1146 {
1147 /* Free this entry */
1148 NewEntry->Object = NULL;
1149 NewEntry->NextFreeTableEntry = NewTable->FirstFree;
1150 NewTable->FirstFree = Handle.Value;
1151 }
1152
1153 /* Increase the handle value and move to the next entry */
1154 Handle.Value += SizeOfHandle(1);
1155 NewEntry++;
1156 HandleTableEntry++;
1157 } while (Handle.Value % SizeOfHandle(LOW_LEVEL_ENTRIES));
1158
1159 /* We're done, skip the last entry */
1160 Handle.Value += SizeOfHandle(1);
1161 }
1162
1163 /* Acquire the table lock and insert this new table into the list */
1164 ExAcquirePushLockExclusive(&HandleTableListLock);
1165 InsertTailList(&HandleTableListHead, &NewTable->HandleTableList);
1166 ExReleasePushLockExclusive(&HandleTableListLock);
1167
1168 /* Leave the critical region we entered previously and return the table */
1169 KeLeaveCriticalRegion();
1170 return NewTable;
1171 }
1172
1173 BOOLEAN
1174 NTAPI
1175 ExChangeHandle(IN PHANDLE_TABLE HandleTable,
1176 IN HANDLE Handle,
1177 IN PEX_CHANGE_HANDLE_CALLBACK ChangeRoutine,
1178 IN ULONG_PTR Context)
1179 {
1180 EXHANDLE ExHandle;
1181 PHANDLE_TABLE_ENTRY HandleTableEntry;
1182 BOOLEAN Result = FALSE;
1183 PAGED_CODE();
1184
1185 /* Set the handle value */
1186 ExHandle.GenericHandleOverlay = Handle;
1187
1188 /* Find the entry for this handle */
1189 HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, ExHandle);
1190
1191 /* Make sure that we found an entry, and that it's valid */
1192 if (!(HandleTableEntry) ||
1193 !(HandleTableEntry->Object) ||
1194 (HandleTableEntry->NextFreeTableEntry == -2))
1195 {
1196 /* It isn't, fail */
1197 return FALSE;
1198 }
1199
1200 /* Enter a critical region */
1201 KeEnterCriticalRegion();
1202
1203 /* Try locking the handle entry */
1204 if (ExpLockHandleTableEntry(HandleTable, HandleTableEntry))
1205 {
1206 /* Call the change routine and unlock the entry */
1207 Result = ChangeRoutine(HandleTableEntry, Context);
1208 ExUnlockHandleTableEntry(HandleTable, HandleTableEntry);
1209 }
1210
1211 /* Leave the critical region and return the callback result */
1212 KeLeaveCriticalRegion();
1213 return Result;
1214 }
1215
1216 VOID
1217 NTAPI
1218 ExSweepHandleTable(IN PHANDLE_TABLE HandleTable,
1219 IN PEX_SWEEP_HANDLE_CALLBACK EnumHandleProcedure,
1220 IN PVOID Context)
1221 {
1222 EXHANDLE Handle;
1223 PHANDLE_TABLE_ENTRY HandleTableEntry;
1224 PAGED_CODE();
1225
1226 /* Set the initial value and loop the entries */
1227 Handle.Value = SizeOfHandle(1);
1228 while ((HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, Handle)))
1229 {
1230 /* Loop each handle */
1231 do
1232 {
1233 /* Lock the entry */
1234 if (ExpLockHandleTableEntry(HandleTable, HandleTableEntry))
1235 {
1236 /* Notify the callback routine */
1237 EnumHandleProcedure(HandleTableEntry,
1238 Handle.GenericHandleOverlay,
1239 Context);
1240 }
1241
1242 /* Go to the next handle and entry */
1243 Handle.Value += SizeOfHandle(1);
1244 HandleTableEntry++;
1245 } while (Handle.Value % SizeOfHandle(LOW_LEVEL_ENTRIES));
1246
1247 /* Skip past the last entry */
1248 Handle.Value += SizeOfHandle(1);
1249 }
1250 }
1251
1252 /*
1253 * @implemented
1254 */
1255 BOOLEAN
1256 NTAPI
1257 ExEnumHandleTable(IN PHANDLE_TABLE HandleTable,
1258 IN PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure,
1259 IN OUT PVOID Context,
1260 OUT PHANDLE EnumHandle OPTIONAL)
1261 {
1262 EXHANDLE Handle;
1263 PHANDLE_TABLE_ENTRY HandleTableEntry;
1264 BOOLEAN Result = FALSE;
1265 PAGED_CODE();
1266
1267 /* Enter a critical region */
1268 KeEnterCriticalRegion();
1269
1270 /* Set the initial value and loop the entries */
1271 Handle.Value = 0;
1272 while ((HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, Handle)))
1273 {
1274 /* Validate the entry */
1275 if ((HandleTableEntry) &&
1276 (HandleTableEntry->Object) &&
1277 (HandleTableEntry->NextFreeTableEntry != -2))
1278 {
1279 /* Lock the entry */
1280 if (ExpLockHandleTableEntry(HandleTable, HandleTableEntry))
1281 {
1282 /* Notify the callback routine */
1283 Result = EnumHandleProcedure(HandleTableEntry,
1284 Handle.GenericHandleOverlay,
1285 Context);
1286
1287 /* Unlock it */
1288 ExUnlockHandleTableEntry(HandleTable, HandleTableEntry);
1289
1290 /* Was this the one looked for? */
1291 if (Result)
1292 {
1293 /* If so, return it if requested */
1294 if (EnumHandle) *EnumHandle = Handle.GenericHandleOverlay;
1295 break;
1296 }
1297 }
1298 }
1299
1300 /* Go to the next entry */
1301 Handle.Value += SizeOfHandle(1);
1302 }
1303
1304 /* Leave the critical region and return callback result */
1305 KeLeaveCriticalRegion();
1306 return Result;
1307 }