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