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