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