/** * PROJECT: ExectOS * COPYRIGHT: See COPYING.md in the top level directory * FILE: xtoskrnl/ob/hndltbl.cc * DESCRIPTION: Object Manager Handle Table Management Interface * DEVELOPERS: Aiken Harris */ #include /** * Allocates and initializes a new handle table structure. * * @param Process * Supplies the optional process to charge quota against. * * @param Initialize * Indicates whether to build the initial free list. * * @return This routine returns a pointer to the handle table, or NULL pointer on failure. * * @since XT 1.0 */ PHANDLE_TABLE OB::HandleTable::AllocateHandleTable(IN PEPROCESS Process, IN BOOLEAN Initialize) { PHANDLE_TABLE_ENTRY HandleEntry, LowLevelTable; PHANDLE_TABLE HandleTable; ULONG Index; /* Allocate a memory block for the handle table structure */ HandleTable = (PHANDLE_TABLE)AllocateTableMemory(Process, sizeof(HANDLE_TABLE), TRUE); if(!HandleTable) { /* Memory allocation failed, return a NULL pointer */ return NULLPTR; } /* Allocate a memory block for the initial low-level handle array */ LowLevelTable = (PHANDLE_TABLE_ENTRY)AllocateTableMemory(Process, MM_PAGE_SIZE, FALSE); if(!LowLevelTable) { /* Allocation failed, free the handle table structure and return a NULL pointer */ FreeTableMemory(Process, HandleTable, sizeof(HANDLE_TABLE)); return NULLPTR; } /* Publish the low-level table into the handle table structure */ HandleTable->TableCode = (ULONG_PTR)LowLevelTable; /* Initialize the first entry of the root table */ HandleEntry = &LowLevelTable[0]; HandleEntry->NextFreeTableEntry = OBJECT_HANDLE_ADDITIONAL_INFO_SIGNATURE; HandleEntry->Value = 0; /* Check if the initialization is required */ if(Initialize) { /* Advance to the first handle entry */ HandleEntry++; /* Iterate through the low-level table block */ for(Index = 1; Index < OBJECT_HANDLE_LOWLEVEL_COUNT - 1; Index++) { /* Link the current entry to the next available handle index */ HandleEntry->Value = 0; HandleEntry->NextFreeTableEntry = (Index + 1) * OBJECT_HANDLE_VALUE_INCREMENT; /* Advance the pointer */ HandleEntry++; } /* Terminate the free list chain */ HandleEntry->Value = 0; HandleEntry->NextFreeTableEntry = 0; /* Initialize the free list head */ HandleTable->FirstFreeHandle = (PHANDLE_TABLE_ENTRY)(ULONG_PTR)OBJECT_HANDLE_VALUE_INCREMENT; } /* Set the next handle needing pool */ HandleTable->NextHandleNeedingPool = OBJECT_HANDLE_LOWLEVEL_COUNT * OBJECT_HANDLE_VALUE_INCREMENT; /* Associate the handle table with the target process */ HandleTable->QuotaProcess = Process; /* Check if a target quota process was provided */ if(Process) { /* Inherit the process identifier */ HandleTable->UniqueProcessId = Process->UniqueProcessId; } else { /* Inherit the process identifier from the current process */ HandleTable->UniqueProcessId = PS::Process::GetCurrentProcess()->UniqueProcessId; } /* Clear all flags */ HandleTable->Flags = 0; /* Iterate through the handle table pushlocks */ for(Index = 0; Index < OBJECT_HANDLE_VALUE_INCREMENT; Index++) { /* Initialize the pushlock */ KE::PushLock::InitializePushLock(&HandleTable->HandleTableLock[Index]); } /* Initialize the pushlock used for handle contention synchronization */ KE::PushLock::InitializePushLock(&HandleTable->HandleContentionEvent); /* Return the pointer to the handle table */ return HandleTable; } /** * Allocates a new handle table entry. * * @param HandleTable * Supplies the handle table to allocate from. * * @param Initialize * Indicates whether to initialize the free list for new entries. * * @return This routine returns TRUE on success, or FALSE otherwise. * * @since XT 1.0 */ BOOLEAN OB::HandleTable::AllocateHandleTableEntry(IN PHANDLE_TABLE HandleTable, IN BOOLEAN Initialize) { PHANDLE_TABLE_ENTRY **HighLevelTable, **HighLevelTableEntry, LowLevelTable, *MediumLevelTable, *MediumLevelTableEntry; ULONG FirstFree, FirstFreeIndex, HighLevelIndex, MediumLevelIndex, OldIndex, RemainingIndex, TableLevel; ULONG_PTR CapturedTable; /* Capture the current table structure */ CapturedTable = HandleTable->TableCode; /* Extract the depth level of the handle table */ TableLevel = (ULONG)(CapturedTable & OBJECT_HANDLE_LEVEL_CODE_MASK); /* Mask out the level bits */ CapturedTable = CapturedTable & ~OBJECT_HANDLE_LEVEL_CODE_MASK; /* Initialize the low-level table pointer */ LowLevelTable = NULLPTR; /* Check if the handle table is currently operating as a single-level structure */ if(!TableLevel) { /* Allocate a medium-level table directory */ MediumLevelTable = AllocateMediumLevelTable(HandleTable, Initialize, &LowLevelTable); if(!MediumLevelTable) { /* Table directory allocation failed, return FALSE */ return FALSE; } /* Link the low-level table into the medium-level directory */ MediumLevelTable[1] = MediumLevelTable[0]; /* Set the original flat table as the first entry in the medium-level directory */ MediumLevelTable[0] = (PHANDLE_TABLE_ENTRY)CapturedTable; /* Encode the table pointer */ CapturedTable = ((ULONG_PTR)MediumLevelTable) | 1; /* Publish the multi-level table structure */ RTL::Atomic::ExchangePointer((PVOID *)&HandleTable->TableCode, (PVOID)CapturedTable); } else if(TableLevel == 1) { /* Cast the captured table pointer to a medium-level directory array */ MediumLevelTableEntry = (PHANDLE_TABLE_ENTRY *)CapturedTable; /* Calculate the directory index */ MediumLevelIndex = HandleTable->NextHandleNeedingPool / (OBJECT_HANDLE_LOWLEVEL_COUNT * OBJECT_HANDLE_VALUE_INCREMENT); /* Check if the current medium-level directory has remaining capacity */ if(MediumLevelIndex < OBJECT_HANDLE_MEDIUMLEVEL_COUNT) { /* Allocate a low-level table leaf */ LowLevelTable = AllocateLowLevelTable(HandleTable, Initialize); if(!LowLevelTable) { /* Table allocation failed, return FALSE */ return FALSE; } /* Publish the low-level table */ RTL::Atomic::ExchangePointer((PVOID *)(&MediumLevelTableEntry[MediumLevelIndex]), LowLevelTable); } else { /* The medium-level directory is exhausted, allocate a high-level table directory */ HighLevelTable = (PHANDLE_TABLE_ENTRY**)AllocateTableMemory(HandleTable->QuotaProcess, OBJECT_HANDLE_HIGHLEVEL_SIZE, TRUE); if(!HighLevelTable) { /* Table allocation failed, return FALSE */ return FALSE; } /* Allocate a medium-level directory */ MediumLevelTable = AllocateMediumLevelTable(HandleTable, Initialize, &LowLevelTable); if(!MediumLevelTable) { /* Free the high-level table and return FALSE */ FreeTableMemory(HandleTable->QuotaProcess, HighLevelTable, OBJECT_HANDLE_HIGHLEVEL_SIZE); return FALSE; } /* Link the medium-level structure as the first branch */ HighLevelTable[0] = (PHANDLE_TABLE_ENTRY *)CapturedTable; /* Link the medium-level structure as the second branch */ HighLevelTable[1] = MediumLevelTable; /* Encode the table pointer */ CapturedTable = ((ULONG_PTR)HighLevelTable) | 2; /* Publish the high-level table structure */ RTL::Atomic::ExchangePointer((PVOID *)&HandleTable->TableCode, (PVOID)CapturedTable); } } else if(TableLevel == 2) { /* Cast the captured table pointer to a high-level directory array */ HighLevelTableEntry = (PHANDLE_TABLE_ENTRY **)CapturedTable; /* Calculate the root index into the high-level table */ HighLevelIndex = HandleTable->NextHandleNeedingPool / (OBJECT_HANDLE_MEDIUMLEVEL_THRESHOLD * OBJECT_HANDLE_VALUE_INCREMENT); /* Ensure there is room in the high-level table for the entry */ if(HighLevelIndex >= OBJECT_HANDLE_HIGHLEVEL_COUNT) { /* Table is full, return FALSE */ return FALSE; } /* Check if the required medium-level directory branch needs to be created */ if(!HighLevelTableEntry[HighLevelIndex]) { /* Allocate a medium-level table directory */ MediumLevelTable = AllocateMediumLevelTable(HandleTable, Initialize, &LowLevelTable); if(!MediumLevelTable) { /* Table allocation failed, return FALSE */ return FALSE; } /* Publish the medium-level directory */ RTL::Atomic::ExchangePointer((PVOID *)&HighLevelTableEntry[HighLevelIndex], MediumLevelTable); } else { /* Calculate the localized index within the medium-level branch */ RemainingIndex = (HandleTable->NextHandleNeedingPool / OBJECT_HANDLE_VALUE_INCREMENT) - HighLevelIndex * OBJECT_HANDLE_MEDIUMLEVEL_THRESHOLD; MediumLevelIndex = RemainingIndex / OBJECT_HANDLE_LOWLEVEL_COUNT; /* Allocate a low-level table leaf */ LowLevelTable = AllocateLowLevelTable(HandleTable, Initialize); if(!LowLevelTable) { /* Table allocation failed, return FALSE */ return FALSE; } /* Publish the low-level table */ RTL::Atomic::ExchangePointer((PVOID *)(&HighLevelTableEntry[HighLevelIndex][MediumLevelIndex]), LowLevelTable); } } /* Reserve the allocated index block and capture the base handle value */ OldIndex = RTL::Atomic::ExchangeAdd32((PLONG)&HandleTable->NextHandleNeedingPool, OBJECT_HANDLE_LOWLEVEL_COUNT * OBJECT_HANDLE_VALUE_INCREMENT); /* Check if the initialization flag is set */ if(Initialize) { /* Calculate the starting index */ FirstFreeIndex = OldIndex + OBJECT_HANDLE_VALUE_INCREMENT; /* Enter exchange loop */ while(TRUE) { /* Capture the state of the primary free list */ FirstFree = *(VOLATILE ULONG *)&HandleTable->FirstFreeHandle; /* Chain the tail of allocated page to the current list head */ LowLevelTable[OBJECT_HANDLE_LOWLEVEL_COUNT - 1].NextFreeTableEntry = FirstFree; /* Swap the new chain head */ if (FirstFree == (ULONG)RTL::Atomic::CompareExchange32((PLONG)&HandleTable->FirstFreeHandle, FirstFreeIndex, FirstFree)) { /* Exchange succeeded, break the loop */ break; } } } /* Handle allocation succeeded, return TRUE */ return TRUE; } /** * Allocates a new handle table entry. * * @param HandleTable * Supplies the handle table to allocate from. * * @param Handle * Receives the allocated handle value. * * @return This routine returns a pointer to the allocated entry, or NULL pointer on failure. * * @since XT 1.0 */ XTAPI PHANDLE_TABLE_ENTRY OB::HandleTable::AllocateHandleTableEntry(IN PHANDLE_TABLE HandleTable, OUT POBJECT_HANDLE Handle) { ULONG FreeHandle, LockIndex, NextFreeHandle, OldFreeHandle; PHANDLE_TABLE_ENTRY TableEntry; BOOLEAN ExpansionSucceeded; OBJECT_HANDLE LocalHandle; PKTHREAD CurrentThread; /* Retrieve the currently executing thread */ CurrentThread = KE::Processor::GetCurrentThread(); /* Enter the retry loop */ while(TRUE) { /* Get the current head of the primary free list */ OldFreeHandle = (ULONG)(ULONG_PTR)HandleTable->FirstFreeHandle; /* Enter the replenishment loop */ while(!OldFreeHandle) { /* Start a guarded code block */ { /* Acquire a critical region guard */ KE::CriticalRegionGuard CriticalRegion(CurrentThread); /* Acquire the handle table lock */ KE::PushLockExclusiveGuard PushLock(&HandleTable->HandleTableLock[0]); /* Check if another thread replenished the free list */ OldFreeHandle = (ULONG)(ULONG_PTR)HandleTable->FirstFreeHandle; if(OldFreeHandle) { /* The free list was replenished, break the loop execution */ break; } /* Pull available handles */ OldFreeHandle = MoveFreeHandles(HandleTable); if(OldFreeHandle) { /* Break the loop execution */ break; } /* Allocate a handle table entry */ ExpansionSucceeded = AllocateHandleTableEntry(HandleTable, TRUE); } /* Get the free list head */ OldFreeHandle = (ULONG)(ULONG_PTR)HandleTable->FirstFreeHandle; /* Check if the block allocation failed */ if(!ExpansionSucceeded) { /* Verify if the free list remains depleted */ if(!OldFreeHandle) { /* Fail the allocation request and return NULL pointer */ Handle->GenericHandleOverlay = NULLPTR; return NULLPTR; } } } /* Extract the handle value and retrieve its underlying table entry */ LocalHandle.Value = (OldFreeHandle & OBJECT_FREE_HANDLE_MASK); TableEntry = LookupHandleTableEntry(HandleTable, LocalHandle); /* Calculate the specific lock index */ LockIndex = ((OldFreeHandle & OBJECT_FREE_HANDLE_MASK) >> 2) % OBJECT_HANDLE_VALUE_INCREMENT; /* Start a guarded code block */ { /* Acquire a critical region guard */ KE::CriticalRegionGuard CriticalRegion(CurrentThread); /* Acquire the handle table lock */ KE::PushLockSharedGuard PushLock(&HandleTable->HandleTableLock[LockIndex]); /* Verify that the free list head has not been modified */ if(OldFreeHandle != *(volatile ULONG *)&HandleTable->FirstFreeHandle) { /* The list changed, restart the allocation */ continue; } /* Memory barrier */ AR::CpuFunctions::MemoryBarrier(); /* Retrieve the index of the next free entry in the chain */ NextFreeHandle = *(volatile ULONG *)&TableEntry->NextFreeTableEntry; /* Replace the free list head with the next index */ FreeHandle = RTL::Atomic::CompareExchange32((PLONG)&HandleTable->FirstFreeHandle, NextFreeHandle, OldFreeHandle); } /* Check if the replace succeeded */ if(FreeHandle == OldFreeHandle) { /* Exit the retry loop */ break; } } /* Increment the handle allocation count */ RTL::Atomic::Increment32(&HandleTable->HandleCount); /* Store the handle and return a pointer to the allocated entry */ *Handle = LocalHandle; return TableEntry; } /** * Allocates a low-level handle table page. * * @param HandleTable * Supplies the parent handle table. * * @param Initialize * Indicates whether to initialize the free list within the page. * * @return This routine returns a pointer to the allocated table, or NULL pointer on failure. * * @since XT 1.0 */ XTAPI PHANDLE_TABLE_ENTRY OB::HandleTable::AllocateLowLevelTable(IN PHANDLE_TABLE HandleTable, IN BOOLEAN Initialize) { PHANDLE_TABLE_ENTRY HandleEntry, LowLevelEntry; ULONG BaseHandle, Index; /* Allocate a memory block for the handle table */ LowLevelEntry = (PHANDLE_TABLE_ENTRY)AllocateTableMemory(HandleTable->QuotaProcess, MM_PAGE_SIZE, FALSE); if(!LowLevelEntry) { /* Memory allocation failed, return a NULL pointer */ return NULLPTR; } /* Initialize the first entry of the page as the extended information header */ HandleEntry = &LowLevelEntry[0]; HandleEntry->NextFreeTableEntry = OBJECT_HANDLE_ADDITIONAL_INFO_SIGNATURE; HandleEntry->Value = 0; /* Check if initialization requested */ if(Initialize) { /* Advance to the first usable handle entry */ HandleEntry++; /* Calculate the base handle index */ BaseHandle = HandleTable->NextHandleNeedingPool + 2 * OBJECT_HANDLE_VALUE_INCREMENT; /* Iterate through the table block */ for(Index = BaseHandle; Index < BaseHandle + (OBJECT_HANDLE_LOWLEVEL_COUNT - 2) * OBJECT_HANDLE_VALUE_INCREMENT; Index += OBJECT_HANDLE_VALUE_INCREMENT) { /* Link the current entry to the next available handle index */ HandleEntry->NextFreeTableEntry = Index; HandleEntry->Value = 0; /* Advance to the next handle table entry */ HandleEntry++; } /* Terminate the free list chain */ HandleEntry->NextFreeTableEntry = 0; HandleEntry->Value = 0; } /* Return the pointer to the handle table */ return LowLevelEntry; } /** * Allocates a mid-level table with an initial low-level table. * * @param HandleTable * Supplies the parent handle table. * * @param Initialize * Indicates whether to initialize the free list. * * @param LowLevelEntry * Receives the pointer to the allocated low-level table. * * @return This routine returns a pointer to the mid-level table, or NULL on failure. * * @since XT 1.0 */ PHANDLE_TABLE_ENTRY* OB::HandleTable::AllocateMediumLevelTable(IN PHANDLE_TABLE HandleTable, IN BOOLEAN Initialize, OUT PHANDLE_TABLE_ENTRY *LowLevelEntry) { PHANDLE_TABLE_ENTRY *MediumLevelEntry; PHANDLE_TABLE_ENTRY LowLevel; /* Allocate a memory block for the medium-level handle table */ MediumLevelEntry = (PHANDLE_TABLE_ENTRY*)AllocateTableMemory(HandleTable->QuotaProcess, MM_PAGE_SIZE, TRUE); if(!MediumLevelEntry) { /* Memory allocation failed, return a NULL pointer */ return NULLPTR; } /* Allocate the initial low-level handle table page */ LowLevel = AllocateLowLevelTable(HandleTable, Initialize); if(!LowLevel) { /* Allocation failed, free the medium-level table and return a NULL pointer */ FreeTableMemory(HandleTable->QuotaProcess, MediumLevelEntry, MM_PAGE_SIZE); return NULLPTR; } /* Link the initial table into the first slot */ MediumLevelEntry[0] = LowLevel; *LowLevelEntry = LowLevel; /* Return the medium-level handle table */ return MediumLevelEntry; } /** * Allocates a memory block for handle table structures. * * @param QuotaProcess * Optionally supplies a pointer to the process object that will be charged for the memory quota. * * @param Size * Supplies the size, in bytes, of the memory block to allocate. * * @param ZeroMemory * Supplies a boolean value indicating whether the allocated memory block should be zero-initialized. * * @return This routine returns a pointer to the newly allocated memory block, or NULLPTR if the allocation fails. * * @since XT 1.0 */ XTAPI PVOID OB::HandleTable::AllocateTableMemory(IN PEPROCESS QuotaProcess, IN SIZE_T Size, IN BOOLEAN ZeroMemory) { PVOID MemoryBuffer; XTSTATUS Status; /* Allocate a paged pool memory block with the object table tag */ MM::Allocator::AllocatePool(PagedPool, Size, &MemoryBuffer, TAG_OB_OBJECT_TABLE); if(!MemoryBuffer) { /* Memory allocation failed, return a NULL pointer */ return NULLPTR; } /* Check if the caller requested zero-initialized memory */ if(ZeroMemory) { /* Clear the contents of the allocated memory block */ RTL::Memory::ZeroMemory(MemoryBuffer, Size); } /* Check if a quota process was specified */ if(QuotaProcess) { /* Charge the paged pool memory allocation against the target process quota */ Status = PS::Quota::ChargeProcessQuota(QuotaProcess->QuotaBlock, QuotaProcess, PsPagedPool, Size); if(Status != STATUS_SUCCESS) { /* Quota charge failed, free the allocated memory and return a NULL pointer */ MM::Allocator::FreePool(MemoryBuffer, TAG_OB_OBJECT_TABLE); return NULLPTR; } } /* Return a pointer to the allocated memory block */ return MemoryBuffer; } /** * Changes the contents of a handle entry via a callback. * * @param HandleTable * Supplies the handle table containing the entry. * * @param Handle * Supplies the handle value to modify. * * @param Callback * Supplies the callback that performs the modification. * * @param Parameter * Supplies an opaque parameter passed to the callback. * * @return This routine returns TRUE if the change succeeded, or FALSE otherwise. * * @since XT 1.0 */ XTAPI BOOLEAN OB::HandleTable::ChangeHandle(IN PHANDLE_TABLE HandleTable, IN HANDLE Handle, IN POB_CHANGE_HANDLE_ROUTINE Callback, IN ULONG_PTR Parameter ) { PHANDLE_TABLE_ENTRY HandleTableEntry; OBJECT_HANDLE LocalHandle; PKTHREAD CurrentThread; BOOLEAN ReturnValue; /* Initialize the default return value */ ReturnValue = FALSE; /* Overlay the generic handle value */ LocalHandle.GenericHandleOverlay = Handle; /* Retrieve the currently executing thread */ CurrentThread = KE::Processor::GetCurrentThread(); /* Retrieve the corresponding handle table entry */ HandleTableEntry = LookupHandleTableEntry(HandleTable, LocalHandle); /* Ensure the entry exists and points to an active object */ if(HandleTableEntry && ValidateObjectEntry(HandleTableEntry)) { /* Acquire a critical region guard */ KE::CriticalRegionGuard CriticalRegion(CurrentThread); /* Acquire the exclusive lock for this entry */ if(LockHandleTableEntry(HandleTable, HandleTableEntry)) { /* Execute the provided callback */ ReturnValue = (*Callback)(HandleTableEntry, Parameter); /* Release the lock */ UnlockHandleTableEntry(HandleTable, HandleTableEntry); } } /* Return the callback result */ return ReturnValue; } /** * Creates a new handle entry in a handle table. * * @param HandleTable * Supplies the handle table to create the entry in. * * @param HandleTableEntry * Supplies the template entry to copy. * * @return This routine returns the created handle value, or NULL on failure. * * @since XT 1.0 */ XTAPI HANDLE OB::HandleTable::CreateHandle(IN PHANDLE_TABLE HandleTable, IN PHANDLE_TABLE_ENTRY HandleTableEntry) { PHANDLE_TABLE_ENTRY NewHandleTableEntry; PETHREAD CurrentThread; OBJECT_HANDLE Handle; /* Initialize the generic handle overlay */ Handle.GenericHandleOverlay = NULLPTR; /* Allocate a new handle table entry */ NewHandleTableEntry = AllocateHandleTableEntry(HandleTable, &Handle); if(NewHandleTableEntry) { /* Retrieve the current thread object */ CurrentThread = PS::Thread::GetCurrentThread(); /* Acquire a critical region guard */ KE::CriticalRegionGuard CriticalRegion(&CurrentThread->ThreadControlBlock); /* Copy the contents of the template entry */ *NewHandleTableEntry = *HandleTableEntry; /* Unlock the handle table entry */ UnlockHandleTableEntry(HandleTable, NewHandleTableEntry); } /* Return the resulting handle value */ return Handle.GenericHandleOverlay; } /** * Creates and initializes a new handle table. * * @param Process * Supplies the optional process to charge quota against. * * @return This routine returns a pointer to the handle table, or NULL pointer on failure. * * @since XT 1.0 */ XTAPI PHANDLE_TABLE OB::HandleTable::CreateHandleTable(IN PEPROCESS Process) { PKTHREAD CurrentThread; PHANDLE_TABLE HandleTable; /* Get the current thread */ CurrentThread = KE::Processor::GetCurrentThread(); /* Allocate a new handle table */ HandleTable = AllocateHandleTable(Process, TRUE); if(!HandleTable) { /* Handle table allocation failure, return NULL pointer */ return NULLPTR; } /* Enter a critical region and acquire the handle table lock */ KE::CriticalRegionGuard CriticalRegion(CurrentThread); KE::PushLockExclusiveGuard PushLock(&HandleTableListLock); /* Insert the new handle table into the global list */ RTL::LinkedList::InsertTailList(&HandleTableListHead, &HandleTable->HandleTableList); /* Return the new handle table */ return HandleTable; } /** * Removes a handle from a handle table. * * @param HandleTable * Supplies the handle table containing the handle. * * @param Handle * Supplies the handle value to remove. * * @param HandleTableEntry * Optionally supplies a pre-locked entry pointer. * * @return This routine returns TRUE if the handle was removed, or FALSE otherwise. * * @since XT 1.0 */ XTAPI BOOLEAN OB::HandleTable::DestroyHandle(IN PHANDLE_TABLE HandleTable, IN HANDLE Handle, IN PHANDLE_TABLE_ENTRY HandleTableEntry) { OBJECT_HANDLE LocalHandle; PETHREAD CurrentThread; /* Capture the provided handle value */ LocalHandle.GenericHandleOverlay = Handle; /* Retrieve the current thread object */ CurrentThread = PS::Thread::GetCurrentThread(); /* Acquire a critical region guard */ KE::CriticalRegionGuard CriticalRegion(&CurrentThread->ThreadControlBlock); /* Check if a handle table entry was provided by the caller */ if(!HandleTableEntry) { /* Resolve the local handle to its corresponding handle table entry */ HandleTableEntry = LookupHandleTableEntry(HandleTable, LocalHandle); /* Verify that the resolved entry points to a valid object */ if(!ValidateObjectEntry(HandleTableEntry)) { /* Invalid object entry, return FALSE */ return FALSE; } /* Acquire the lock */ if(!LockHandleTableEntry(HandleTable, HandleTableEntry)) { /* Entry locking failed, return FALSE */ return FALSE; } } /* Clear the object pointer */ RTL::Atomic::ExchangePointer(&HandleTableEntry->Object, NULLPTR); /* Unblock any threads waiting on the handle table */ KE::PushLock::UnblockPushLock(&HandleTable->HandleContentionEvent, NULLPTR); /* Release the handle table entry */ FreeHandleTableEntry(HandleTable, LocalHandle, HandleTableEntry); /* Return TRUE */ return TRUE; } /** * Destroys a handle table and all its entries. * * @param HandleTable * Supplies the handle table to destroy. * * @param Callback * Supplies an optional callback invoked for each valid handle. * * @return This routine does not return any value. * * @since XT 1.0 */ VOID OB::HandleTable::DestroyHandleTable(IN PHANDLE_TABLE HandleTable, IN POB_DESTROY_HANDLE_ROUTINE Callback) { PHANDLE_TABLE_ENTRY HandleTableEntry; OBJECT_HANDLE Handle; /* Remove the handle table from the tracking list */ RemoveHandleTable(HandleTable); /* Check if a handle destruction callback was provided */ if(Callback) { /* Initialize the handle */ Handle.Value = 0; /* Iterate through all handles within the table */ while(TRUE) { /* Retrieve the underlying handle table entry */ HandleTableEntry = LookupHandleTableEntry(HandleTable, Handle); if(!HandleTableEntry) { /* Handle is out of bounds, abort the enumeration */ break; } /* Validate that the entry points to a registered object */ if(ValidateObjectEntry(HandleTableEntry)) { /* Invoke the destruction callback */ (*Callback)(Handle.GenericHandleOverlay); } /* Advance to the next handle value */ Handle.Value += OBJECT_HANDLE_VALUE_INCREMENT; } } /* Release all memory */ FreeHandleTable(HandleTable); } /** * Duplicates copy of a handle table. * * @param Process * Supplies the optional process to charge quota against. * * @param OldHandleTable * Supplies the source handle table to duplicate. * * @param Callback * Supplies the callback invoked for each duplicated handle. * * @param Mask * Supplies a mask applied to object pointers during duplication. * * @return This routine returns a pointer to the new handle table, or NULL pointer on failure. * * @since XT 1.0 */ XTAPI PHANDLE_TABLE OB::HandleTable::DuplicateHandleTable(IN PEPROCESS Process, IN PHANDLE_TABLE OldHandleTable, IN POB_DUPLICATE_HANDLE_ROUTINE Callback, IN ULONG_PTR Mask) { PHANDLE_TABLE_ENTRY NewHandleTableEntry, OldHandleTableEntry; PHANDLE_TABLE_ENTRY_INFO EntryInfo; PHANDLE_TABLE DuplicateHandleTable; ULONG Index, RemainingEntries; BOOLEAN FreeEntry, Ready; PKTHREAD CurrentThread; OBJECT_HANDLE Handle; /* Retrieve the currently executing thread */ CurrentThread = KE::Processor::GetCurrentThread(); /* Allocate a handle table */ DuplicateHandleTable = AllocateHandleTable(Process, FALSE); if(!DuplicateHandleTable) { /* Allocation failed, return NULL pointer */ return NULLPTR; } /* Expand the handle table to match the capacity */ while(DuplicateHandleTable->NextHandleNeedingPool < OldHandleTable->NextHandleNeedingPool) { /* Allocate the handle table entries */ if(!AllocateHandleTableEntry(DuplicateHandleTable, FALSE)) { /* Allocation failed, free handle table and return NULL pointer */ FreeHandleTable(DuplicateHandleTable); return NULLPTR; } } /* Memory barrier */ AR::CpuFunctions::MemoryBarrier(); /* Initialize the base state of the cloned table */ DuplicateHandleTable->ExtraInfoPages = 0; DuplicateHandleTable->FirstFreeHandle = 0; DuplicateHandleTable->HandleCount = 0; /* Initialize the handle value */ Handle.Value = OBJECT_HANDLE_VALUE_INCREMENT; /* Acquire a critical region guard */ KE::CriticalRegionGuard CriticalRegion(CurrentThread); /* Enumerate all allocated handle table entry layers */ while(TRUE) { /* Retrieve the physical pointer to the handle table entry */ NewHandleTableEntry = LookupHandleTableEntry(DuplicateHandleTable, Handle); if(!NewHandleTableEntry) { /* Allocated table layers exhausted, break the enumeration loop */ break; } /* Retrieve the pointer from the source table */ OldHandleTableEntry = LookupHandleTableEntry(OldHandleTable, Handle); /* Calculate how many entries remain to be processed */ RemainingEntries = OBJECT_HANDLE_LOWLEVEL_COUNT - ((Handle.Value / OBJECT_HANDLE_VALUE_INCREMENT) % OBJECT_HANDLE_LOWLEVEL_COUNT); /* Process all entries */ for(Index = 0; Index < RemainingEntries; Index++) { /* Check if the entry matches the required mask and lock it */ if(!(OldHandleTableEntry->Value & Mask) || !LockHandleTableEntry(OldHandleTable, OldHandleTableEntry)) { /* Invalid or locked entry, push to free list */ FreeEntry = TRUE; } else { /* Perform a raw memory copy */ *NewHandleTableEntry = *OldHandleTableEntry; /* Retrieve extended information attached to the handle */ EntryInfo = GetHandleInformation(OldHandleTable, Handle.GenericHandleOverlay, TRUE); if(EntryInfo) { /* Check the readiness of the entry */ Ready = (SetHandleInformation(DuplicateHandleTable, Handle.GenericHandleOverlay, EntryInfo, TRUE) == STATUS_SUCCESS); } else { /* No extended info exists, ready to proceed */ Ready = TRUE; } /* Verify the readiness */ if(Ready) { /* Invoke the duplication callback */ if((*Callback)(Process, OldHandleTable, OldHandleTableEntry, NewHandleTableEntry)) { /* The callback accepted the duplication */ FreeEntry = FALSE; /* Increment the handle count and ensure the new entry remains locked */ DuplicateHandleTable->HandleCount += 1; NewHandleTableEntry->Value |= OBJECT_HANDLE_TABLE_ENTRY_LOCK_BIT; } else { /* Check if extended information was attached */ if(EntryInfo) { /* Clear the audit mask */ EntryInfo->AuditMask = 0; } /* Discarded entry, push to free list */ FreeEntry = TRUE; } } else { /* Setup failed, release the source entry lock */ UnlockHandleTableEntry(OldHandleTable, OldHandleTableEntry); /* Unusable entry, push to free list */ FreeEntry = TRUE; } } /* Process entries designated for the free list */ if(FreeEntry) { /* Link the unused entry into the free list chain */ NewHandleTableEntry->NextFreeTableEntry = (ULONG)(ULONG_PTR)DuplicateHandleTable->FirstFreeHandle; NewHandleTableEntry->Object = NULLPTR; /* Update the head of the free list */ DuplicateHandleTable->FirstFreeHandle = (PHANDLE_TABLE_ENTRY)(ULONG_PTR)Handle.Value; } /* Advance the logical handle value */ Handle.Value += OBJECT_HANDLE_VALUE_INCREMENT; /* Advance the table entry pointers */ NewHandleTableEntry++; OldHandleTableEntry++; } } /* Acquire the handle table lock */ KE::PushLockExclusiveGuard PushLock(&HandleTableListLock); /* Insert the handle table into the global tracking list */ RTL::LinkedList::InsertTailList(&HandleTableListHead, &DuplicateHandleTable->HandleTableList); /* Return the duplicated handle table */ return DuplicateHandleTable; } /** * Enumerates all valid handles in a handle table. * * @param HandleTable * Supplies the handle table to enumerate. * * @param Callback * Supplies the callback invoked for each valid handle. * * @param Parameter * Supplies an opaque parameter passed to the callback. * * @param Handle * Optionally receives the handle where enumeration stopped. * * @return This routine returns TRUE if enumeration stopped early, FALSE otherwise. * * @since XT 1.0 */ XTAPI BOOLEAN OB::HandleTable::EnumerateHandleTable(IN PHANDLE_TABLE HandleTable, IN POB_ENUMERATE_HANDLE_ROUTINE Callback, IN PVOID Parameter, OUT PHANDLE Handle) { PHANDLE_TABLE_ENTRY HandleTableEntry; OBJECT_HANDLE LocalHandle; PKTHREAD CurrentThread; BOOLEAN ResultValue; /* Initialize the default return value */ ResultValue = FALSE; /* Retrieve the currently executing thread */ CurrentThread = KE::Processor::GetCurrentThread(); /* Acquire a critical region guard */ KE::CriticalRegionGuard CriticalRegion(CurrentThread); /* Initialize the handle value */ LocalHandle.Value = 0; /* Iterate through all handles within the table */ while(TRUE) { /* Retrieve the handle table entry for the current value */ HandleTableEntry = LookupHandleTableEntry(HandleTable, LocalHandle); if(!HandleTableEntry) { /* Handle is out of bounds, abort enumeration */ break; } /* Validate that the entry points to a registered object */ if(ValidateObjectEntry(HandleTableEntry)) { /* Acquire the lock */ if(LockHandleTableEntry(HandleTable, HandleTableEntry)) { /* Invoke the enumeration callback */ ResultValue = (*Callback)(HandleTableEntry, LocalHandle.GenericHandleOverlay, Parameter); /* Release the lock */ UnlockHandleTableEntry(HandleTable, HandleTableEntry); /* Check if the callback requested termination */ if(ResultValue) { /* Check if the caller provided an output parameter */ if(Handle) { /* Store the handle */ *Handle = LocalHandle.GenericHandleOverlay; } /* Exit the enumeration loop */ break; } } } /* Advance to the next handle value */ LocalHandle.Value += OBJECT_HANDLE_VALUE_INCREMENT; } /* Return enumeration result */ return ResultValue; } /** * Frees and tears down a handle table structure. * * @param HandleTable * Supplies the handle table to free. * * @return This routine does not return any value. * * @since XT 1.0 */ VOID OB::HandleTable::FreeHandleTable(IN PHANDLE_TABLE HandleTable) { PHANDLE_TABLE_ENTRY **HighLevelTable, LowLevelTable, *MediumLevelTable; ULONG HighLevelIndex, MediumLevelIndex, TableLevel; ULONG_PTR CapturedTable; PEPROCESS Process; /* Capture the current table */ CapturedTable = HandleTable->TableCode; /* Extract the handle table depth level */ TableLevel = (ULONG)(CapturedTable & OBJECT_HANDLE_LEVEL_CODE_MASK); /* Mask out the level bits */ CapturedTable = CapturedTable & ~OBJECT_HANDLE_LEVEL_CODE_MASK; /* Retrieve the quota process associated with handle table */ Process = HandleTable->QuotaProcess; /* Check if the handle table is currently operating as a single-level structure */ if(!TableLevel) { /* Extract the low-level table */ LowLevelTable = (PHANDLE_TABLE_ENTRY)CapturedTable; /* Free the low-level directly */ FreeLowLevelTable(Process, LowLevelTable); } else if(TableLevel == 1) { /* Extract the medium-level directory */ MediumLevelTable = (PHANDLE_TABLE_ENTRY *)CapturedTable; /* Initialize the medium-level directory index */ MediumLevelIndex = 0; /* Enumerate and free all allocated low-level leaves */ while(TRUE) { /* Check if the maximum count reached */ if(MediumLevelIndex >= OBJECT_HANDLE_MEDIUMLEVEL_COUNT || !MediumLevelTable[MediumLevelIndex]) { /* Break the enumeration loop */ break; } /* Free the low-level table leaf */ FreeLowLevelTable(Process, MediumLevelTable[MediumLevelIndex]); /* Advance to the next medium-level directory slot */ MediumLevelIndex++; } /* Free the medium-level directory itself */ FreeTableMemory(Process, MediumLevelTable, MM_PAGE_SIZE); } else { /* Extract the high-level directory */ HighLevelTable = (PHANDLE_TABLE_ENTRY **)CapturedTable; /* Initialize the high-level root index */ HighLevelIndex = 0; /* Enumerate and free all allocated medium-level directories */ while(TRUE) { /* Check if the maximum count reached */ if(HighLevelIndex >= OBJECT_HANDLE_HIGHLEVEL_COUNT || !HighLevelTable[HighLevelIndex]) { /* Break the enumeration loop */ break; } /* Initialize the medium-level directory index */ MediumLevelIndex = 0; /* Enumerate and free all allocated low-level leaves */ while(TRUE) { /* Check if the maximum count reached */ if(MediumLevelIndex >= OBJECT_HANDLE_MEDIUMLEVEL_COUNT || !HighLevelTable[HighLevelIndex][MediumLevelIndex]) { /* Break the inner enumeration loop */ break; } /* Free the low-level table leaf */ FreeLowLevelTable(Process, HighLevelTable[HighLevelIndex][MediumLevelIndex]); /* Advance to the next mediun-level directory slot */ MediumLevelIndex++; } /* Free the current medium-level directory page */ FreeTableMemory(Process, HighLevelTable[HighLevelIndex], MM_PAGE_SIZE); /* Advance to the next high-level directory slot */ HighLevelIndex++; } /* Free the high-level root directory itself */ FreeTableMemory(Process, HighLevelTable, OBJECT_HANDLE_HIGHLEVEL_SIZE); } /* Free the base handle table */ FreeTableMemory(Process, HandleTable, sizeof(HANDLE_TABLE)); } /** * Frees a handle table entry back to the free list. * * @param HandleTable * Supplies the parent handle table. * * @param Handle * Supplies the handle being freed. * * @param HandleTableEntry * Supplies the entry being freed. * * @return This routine does not return any value. * * @since XT 1.0 */ VOID OB::HandleTable::FreeHandleTableEntry(IN PHANDLE_TABLE HandleTable, IN OBJECT_HANDLE Handle, IN PHANDLE_TABLE_ENTRY HandleTableEntry) { ULONG HandleValue, ListHead, LockIndex, SequenceTag; PHANDLE_TABLE_ENTRY_INFO EntryInfo; PULONG TargetListPointer; /* Retrieve the extended information attached to the handle */ EntryInfo = GetHandleInformation(HandleTable, Handle.GenericHandleOverlay, TRUE); if(EntryInfo) { /* Clear the audit mask */ EntryInfo->AuditMask = 0; } /* Decrement the handle allocation count */ RTL::Atomic::Decrement32(&HandleTable->HandleCount); /* Mask out the lower control bits */ HandleValue = (ULONG)Handle.Value & ~(OBJECT_HANDLE_VALUE_INCREMENT - 1); /* Check if strict FIFO mode is enabled */ if(!HandleTable->StrictFIFO) { /* Calculate the lock index */ LockIndex = (HandleValue >> 2) % OBJECT_HANDLE_VALUE_INCREMENT; /* Acquire the segment lock */ if(KE::PushLock::TryAcquireExclusivePushLock(&HandleTable->HandleTableLock[LockIndex])) { /* Lock acquired, use the free list */ SequenceTag = 0; TargetListPointer = (PULONG)&HandleTable->FirstFreeHandle; } else { /* Lock contended, use the delayed list */ SequenceTag = 0; TargetListPointer = (PULONG)&HandleTable->LastFreeHandle; } } else { /* Send freed handle to the delayed list */ SequenceTag = 0; TargetListPointer = (PULONG)&HandleTable->LastFreeHandle; } /* Enter a loop to publish the freed handle */ while(TRUE) { /* Capture the state of the targeted list head */ ListHead = *(VOLATILE ULONG *)TargetListPointer; /* Chain the freed table entry to the list head */ HandleTableEntry->NextFreeTableEntry = ListHead; /* Publish the freed handle */ if((ULONG)RTL::Atomic::CompareExchange32((PLONG)TargetListPointer, HandleValue + SequenceTag, ListHead) == ListHead) { /* Publish succeeded, break the loop */ break; } } } /** * Frees a low-level handle table page. * * @param QuotaProcess * Supplies the process to return quota to. * * @param TableEntry * Supplies the low-level table to free. * * @return This routine does not return any value. * * @since XT 1.0 */ VOID OB::HandleTable::FreeLowLevelTable(IN PEPROCESS QuotaProcess, IN PHANDLE_TABLE_ENTRY TableEntry) { /* Check if an extended information table is attached */ if(TableEntry[0].Object) { /* Free the handle information table */ FreeTableMemory(QuotaProcess, TableEntry[0].Object, OBJECT_HANDLE_LOWLEVEL_COUNT * sizeof(HANDLE_TABLE_ENTRY_INFO)); } /* Release the handle table */ FreeTableMemory(QuotaProcess, TableEntry, MM_PAGE_SIZE); } /** * Frees a handle table memory block and returns the associated quota. * * @param QuotaProcess * Optionally supplies a pointer to the process object that will receive the memory quota reimbursement. * * @param MemoryBuffer * Supplies a pointer to the allocated pool memory block to be freed. * * @param Size * Supplies the size, in bytes, of the memory block being freed. * * @return This routine does not return any value. * * @since XT 1.0 */ XTAPI VOID OB::HandleTable::FreeTableMemory(IN PEPROCESS QuotaProcess, IN PVOID MemoryBuffer, IN SIZE_T Size) { /* Release the allocated pool memory block associated with the object table tag */ MM::Allocator::FreePool(MemoryBuffer, TAG_OB_OBJECT_TABLE); /* Check if a valid quota process object was specified */ if(QuotaProcess) { /* Reimburse the paged pool memory quota to the specified process */ PS::Quota::ReturnProcessQuota(QuotaProcess->QuotaBlock, QuotaProcess, PsPagedPool, Size); } } /** * Retrieves the extended information for a specific handle table entry. * * @param HandleTable * Supplies a pointer to the handle table containing the handle. * * @param Handle * Supplies the handle for which the extended information is requested. * * @param Locked * Specifies whether the handle table entry is currently locked by the caller. * * @return This routine returns a pointer to the handle table entry information, or NULL pointer if the table * does not support extra info pages or lookup fails. * * @since XT 1.0 */ XTAPI PHANDLE_TABLE_ENTRY_INFO OB::HandleTable::GetHandleInformation(IN PHANDLE_TABLE HandleTable, IN HANDLE Handle, IN BOOLEAN Locked) { PHANDLE_TABLE_ENTRY InfoStructure; PHANDLE_TABLE_ENTRY TableEntry; PHANDLE_TABLE_ENTRY_INFO ResultInfo; OBJECT_HANDLE BaseHandle, ObHandle; /* Initialize local state */ ResultInfo = NULLPTR; TableEntry = NULLPTR; /* Check if the handle table maintains extra information pages */ if(HandleTable->ExtraInfoPages) { /* Check if the caller has not locked the entry */ if(!Locked) { /* Acquire the lock */ TableEntry = MapHandleToPointer(HandleTable, Handle); if(!TableEntry) { /* The handle is invalid or mapping failed, return NULL pointer */ return NULLPTR; } } /* Isolate the base index of the handle table page */ BaseHandle.GenericHandleOverlay = Handle; BaseHandle.Index &= ~(OBJECT_HANDLE_LOWLEVEL_COUNT - 1); /* Fetch the base information structure */ InfoStructure = LookupHandleTableEntry(HandleTable, BaseHandle); /* Validate the retrieved structure */ if(InfoStructure && InfoStructure->NextFreeTableEntry == OBJECT_HANDLE_ADDITIONAL_INFO_SIGNATURE && InfoStructure->InfoTable) { /* Decode the target index */ ObHandle.GenericHandleOverlay = Handle; /* Resolve the information entry pointer */ ResultInfo = &InfoStructure->InfoTable[ObHandle.Index % OBJECT_HANDLE_LOWLEVEL_COUNT]; } else { /* Check if locked */ if(TableEntry) { /* Release the lock */ UnlockHandleTableEntry(HandleTable, TableEntry); } } } /* Return the information pointer */ return ResultInfo; } /** * Initializes the handle table. * * @return This routine does not return any value. * * @since XT 1.0 */ XTAPI VOID OB::HandleTable::InitializeHandleTable(VOID) { /* Initialize the handle table list and its synchronization pushlock */ RTL::LinkedList::InitializeListHead(&HandleTableListHead); KE::PushLock::InitializePushLock(&HandleTableListLock); } /** * Acquires the lock on a handle table entry, blocking the current thread if contention occurs. * * @param HandleTable * Supplies a pointer to the handle table containing the entry. * * @param HandleTableEntry * Supplies a pointer to the handle table entry to lock. * * @return This routine returns TRUE if the lock was successfully acquired, or FALSE otherwise. * * @since XT 1.0 */ XTAPI BOOLEAN OB::HandleTable::LockHandleTableEntry(IN PHANDLE_TABLE HandleTable, IN PHANDLE_TABLE_ENTRY HandleTableEntry) { LONG_PTR CurrentValue, NewValue, OldValue; KPUSH_LOCK_WAIT_BLOCK WaitBlock; /* Enter a retry loop */ while(TRUE) { /* Get the state of the entry */ OldValue = *(VOLATILE LONG_PTR *)&HandleTableEntry->Value; /* Check if the entry is currently unlocked */ if(OldValue & OBJECT_HANDLE_TABLE_ENTRY_LOCK_BIT) { /* Clear the lock bit */ NewValue = OldValue & ~OBJECT_HANDLE_TABLE_ENTRY_LOCK_BIT; /* Attempt to apply the lock */ if(RTL::Atomic::CompareExchangePointer((PVOID *)&HandleTableEntry->Value, (PVOID)NewValue, (PVOID)OldValue) == (PVOID)OldValue) { /* Lock acquired, return TRUE */ return TRUE; } } else { /* Check if the entry is completely empty */ if(!OldValue) { /* Cannot proceed, return FALSE */ return FALSE; } } /* The entry is locked by another thread, block thread */ KE::PushLock::BlockPushLock(&HandleTable->HandleContentionEvent, &WaitBlock); /* Check if the entry was freed or unlocked */ CurrentValue = *(VOLATILE LONG_PTR *)&HandleTableEntry->Value; if(!CurrentValue || (CurrentValue & OBJECT_HANDLE_TABLE_ENTRY_LOCK_BIT)) { /* The state changed, abort the wait */ KE::PushLock::UnblockPushLock(&HandleTable->HandleContentionEvent, &WaitBlock); } else { /* The entry is locked, commit to the wait state */ KE::Dispatcher::WaitForSingleObject(&WaitBlock.WakeEvent, Executive, KernelMode, FALSE, NULLPTR); } } } /** * Translates a generic handle into a pointer to its underlying handle table entry. * * @param HandleTable * Supplies a pointer to the handle table performing the lookup. * * @param Handle * Supplies the structured handle to be translated. * * @return This routine returns a pointer to the corresponding handle table entry, or NULL pointer if the handle * is out of bounds or invalid. * * @since XT 1.0 */ XTAPI PHANDLE_TABLE_ENTRY OB::HandleTable::LookupHandleTableEntry(IN PHANDLE_TABLE HandleTable, IN OBJECT_HANDLE Handle) { PHANDLE_TABLE_ENTRY EntryTable; ULONG MaxHandle, TableLevel; ULONG_PTR Index, TableCode; PVOID *DirectoryTable; /* Strip the tag bits */ Handle.Value &= ~(ULONG_PTR)(OBJECT_HANDLE_VALUE_INCREMENT - 1); /* Read the maximum handle value */ MaxHandle = *(VOLATILE ULONG *)&HandleTable->NextHandleNeedingPool; if(Handle.Value >= MaxHandle) { /* Handle is out of bounds, return NULL pointer */ return NULLPTR; } /* Convert the raw handle value to an array index */ Index = Handle.Value / OBJECT_HANDLE_VALUE_INCREMENT; /* Read the table routing state */ TableCode = *(VOLATILE ULONG_PTR *)&HandleTable->TableCode; /* Extract the table depth level */ TableLevel = (ULONG)(TableCode & OBJECT_HANDLE_LEVEL_CODE_MASK); /* Validate the structural integrity of the table */ if(TableLevel > 2) { /* Invalid table level, return NULL pointer */ return NULLPTR; } /* Isolate the base address of the table */ DirectoryTable = (PVOID *)(TableCode & ~(ULONG_PTR)OBJECT_HANDLE_LEVEL_CODE_MASK); /* Cascade through the directory levels */ if(TableLevel == 2) { /* Resolve the highest level directory routing */ DirectoryTable = (PVOID *)DirectoryTable[Index / (OBJECT_HANDLE_LOWLEVEL_COUNT * OBJECT_HANDLE_MEDIUMLEVEL_COUNT)]; } if(TableLevel >= 1) { /* Resolve the intermediate directory routing */ DirectoryTable = (PVOID *)DirectoryTable[(Index / OBJECT_HANDLE_LOWLEVEL_COUNT) % OBJECT_HANDLE_MEDIUMLEVEL_COUNT]; } /* Resolve the final entry page base */ EntryTable = (PHANDLE_TABLE_ENTRY)DirectoryTable; /* Return the memory location of the requested handle table entry */ return &EntryTable[Index % OBJECT_HANDLE_LOWLEVEL_COUNT]; } /** * Resolves a handle to its corresponding handle table entry and locks it. * * @param HandleTable * Supplies a pointer to the handle table where the lookup will be performed. * * @param Handle * Supplies the generic handle to map. * * @return This routine returns a pointer to the locked handle table entry on success, or NULL pointer on failure. * * @since XT 1.0 */ XTAPI PHANDLE_TABLE_ENTRY OB::HandleTable::MapHandleToPointer(IN PHANDLE_TABLE HandleTable, IN HANDLE Handle) { PHANDLE_TABLE_ENTRY TableEntry; OBJECT_HANDLE LocalHandle; /* Assume failure initially */ TableEntry = NULLPTR; /* Overlay the generic handle */ LocalHandle.GenericHandleOverlay = Handle; /* Validate the handle index */ if(LocalHandle.Index & (OBJECT_HANDLE_LOWLEVEL_COUNT - 1)) { /* Lookup the handle table entry */ TableEntry = LookupHandleTableEntry(HandleTable, LocalHandle); if(TableEntry) { /* Acquire the entry lock */ if(!LockHandleTableEntry(HandleTable, TableEntry)) { /* Lock acquisition failed, reset the pointer */ TableEntry = NULLPTR; } } } /* Return the table entry pointer */ return TableEntry; } /** * Maps a handle to a locked pointer with mode-specific error handling. * * @param HandleTable * Supplies the handle table to query. * * @param Handle * Supplies the handle value to map. * * @param ProcessorMode * Supplies the previous processor mode of the caller. * * @return This routine returns a pointer to the locked entry, or NULL pointer on failure. * * @since XT 1.0 */ XTAPI PHANDLE_TABLE_ENTRY OB::HandleTable::MapHandleToPointer(IN PHANDLE_TABLE HandleTable, IN HANDLE Handle, IN KPROCESSOR_MODE ProcessorMode) { PHANDLE_TABLE_ENTRY TableEntry; OBJECT_HANDLE LocalHandle; /* Assume failure initially */ TableEntry = NULLPTR; /* Overlay the generic handle */ LocalHandle.GenericHandleOverlay = Handle; /* Validate the handle index */ if(!(LocalHandle.Index & (OBJECT_HANDLE_LOWLEVEL_COUNT - 1))) { /* Invalid handle index alignment, return a NULL pointer */ return NULLPTR; } /* Retrieve the handle table entry corresponding to the specified handle value */ TableEntry = LookupHandleTableEntry(HandleTable, LocalHandle); if(!TableEntry) { /* Entry lookup failed, return a NULL pointer */ return NULLPTR; } /* Acquire the entry lock */ if(!LockHandleTableEntry(HandleTable, TableEntry)) { /* Lock acquisition failed, reset the pointer */ TableEntry = NULLPTR; } /* Return a pointer to the handle table entry */ return TableEntry; } /** * Moves free handles from the alternate free list to the primary free list. * * @param HandleTable * Supplies the handle table to process. * * @return This routine returns the old value of the last free handle. * * @since XT 1.0 */ XTAPI ULONG OB::HandleTable::MoveFreeHandles(IN PHANDLE_TABLE HandleTable) { ULONG CurrentFirstFree, FreeSize, Index, LockId, NewFirstFreeIndex, NewIndex, OldIndex, OldValue; PHANDLE_TABLE_ENTRY Entry, FirstEntry; BOOLEAN StrictFIFO; OBJECT_HANDLE Handle; /* Extract the current free list and zero it out */ OldValue = RTL::Atomic::Exchange32((PLONG)&HandleTable->LastFreeHandle, 0); /* Assign the extracted old value and check if there are any free handles */ Index = OldValue; if(!Index) { /* No free handles available, return old value */ return OldValue; } /* Acquire all secondary handle table locks for structural integrity */ for(LockId = 1; LockId < OBJECT_HANDLE_VALUE_INCREMENT; LockId++) { /* Block on the designated segment lock */ KE::PushLock::AcquireExclusivePushLock(&HandleTable->HandleTableLock[LockId]); } /* Capture the FIFO requirement flag */ StrictFIFO = HandleTable->StrictFIFO; /* Check if the handle table operates without strict FIFO ordering */ if(!StrictFIFO) { /* Append the sequence */ if(!RTL::Atomic::CompareExchange32((PLONG)&HandleTable->FirstFreeHandle, OldValue, 0)) { /* Release all secondary segment locks */ for(LockId = 1; LockId < OBJECT_HANDLE_VALUE_INCREMENT; LockId++) { /* Unblock the designated segment lock */ KE::PushLock::ReleaseExclusivePushLock(&HandleTable->HandleTableLock[LockId]); } /* Return the old value */ return OldValue; } } /* Initialize tracking variables */ FirstEntry = NULLPTR; FreeSize = 0; OldIndex = 0; /* Traverse the alternate free list chain */ while(Index) { /* Increment the free handle counter */ FreeSize++; /* Map the raw index to its handle table entry */ Handle.Value = Index; Entry = LookupHandleTableEntry(HandleTable, Handle); /* Link the current entry to point backwards */ NewIndex = Entry->NextFreeTableEntry; Entry->NextFreeTableEntry = OldIndex; /* Check if the old index is zero */ if(!OldIndex) { /* Store the pointer to the newly formed chain tail */ FirstEntry = Entry; } /* Advance the traversal pointers */ OldIndex = Index; Index = NewIndex; } /* Calculate the target index */ NewFirstFreeIndex = OldIndex; /* Loop until the exchange succeeds */ while(TRUE) { /* Capture the state of the primary free list */ CurrentFirstFree = *(VOLATILE ULONG *)&HandleTable->FirstFreeHandle; /* Chain tail to the current head of the primary list */ FirstEntry->NextFreeTableEntry = CurrentFirstFree; /* Attempt to swap the new chain head */ if(CurrentFirstFree == (ULONG)RTL::Atomic::CompareExchange32((PLONG)&HandleTable->FirstFreeHandle, NewFirstFreeIndex, CurrentFirstFree)) { /* The exchange succeeded, exit the loop */ break; } } /* Release all handle table locks */ for(LockId = 1; LockId < OBJECT_HANDLE_VALUE_INCREMENT; LockId++) { /* Unblock the designated segment lock */ KE::PushLock::ReleaseExclusivePushLock(&HandleTable->HandleTableLock[LockId]); } /* Check if the batch size is below the threshold */ if(FreeSize < 100 && StrictFIFO) { /* Reset the old value to zero */ OldValue = 0; } /* Return the original first handle value */ return OldValue; } /** * Removes a handle table from the global list. * * @param HandleTable * Supplies the handle table to remove. * * @return This routine does not return any value. * * @since XT 1.0 */ XTAPI VOID OB::HandleTable::RemoveHandleTable(IN PHANDLE_TABLE HandleTable) { PKTHREAD CurrentThread; /* Get the current thread */ CurrentThread = KE::Processor::GetCurrentThread(); /* Enter a critical region and acquire the handle table lock */ KE::CriticalRegionGuard CriticalRegion(CurrentThread); KE::PushLockExclusiveGuard PushLock(&HandleTableListLock); /* Remove the handle table from the global list and initialize the list head */ RTL::LinkedList::RemoveEntryList(&HandleTable->HandleTableList); RTL::LinkedList::InitializeListHead(&HandleTable->HandleTableList); } /** * Sets extended information for a handle table entry. * * @param HandleTable * Supplies a pointer to the handle table containing the entry. * * @param Handle * Supplies the handle for which the information is being updated. * * @param EntryInfo * Supplies a pointer to the entry information structure to be copied. * * @param Locked * Supplies a boolean value indicating whether the handle table is already locked. * * @return This routine returns a status code indicating the success or failure of the operation. * * @since XT 1.0 */ XTAPI XTSTATUS OB::HandleTable::SetHandleInformation(IN PHANDLE_TABLE HandleTable, IN HANDLE Handle, IN PHANDLE_TABLE_ENTRY_INFO EntryInfo, IN BOOLEAN Locked) { PHANDLE_TABLE_ENTRY InfoStructure, TableEntry; PHANDLE_TABLE_ENTRY_INFO InfoTable; OBJECT_HANDLE ExHandle; /* Initialize local state tracking */ ExHandle.GenericHandleOverlay = Handle; ExHandle.Index &= ~(OBJECT_HANDLE_LOWLEVEL_COUNT - 1); TableEntry = NULLPTR; /* Acquire a critical region guard */ KE::CriticalRegionGuard CriticalRegion(NULLPTR, !Locked); /* Check if the caller specified that the handle table is unlocked */ if(!Locked) { /* Map the supplied handle to its corresponding handle table entry */ TableEntry = MapHandleToPointer(HandleTable, Handle); if(!TableEntry) { /* Handle mapping failed, return error code */ return STATUS_UNSUCCESSFUL; } } /* Retrieve the base info structure */ InfoStructure = LookupHandleTableEntry(HandleTable, ExHandle); if(!InfoStructure || InfoStructure->NextFreeTableEntry != OBJECT_HANDLE_ADDITIONAL_INFO_SIGNATURE) { /* Check if the handle table entry was mapped */ if(TableEntry) { /* Unlock the mapped handle table entry */ UnlockHandleTableEntry(HandleTable, TableEntry); } /* Invalid info structure signature, return error code */ return STATUS_UNSUCCESSFUL; } /* Retrieve the pointer to the handle table entry info table */ InfoTable = InfoStructure->InfoTable; if(!InfoTable) { /* Allocate memory for the table */ InfoTable = (PHANDLE_TABLE_ENTRY_INFO)AllocateTableMemory(HandleTable->QuotaProcess, OBJECT_HANDLE_LOWLEVEL_COUNT * sizeof(HANDLE_TABLE_ENTRY_INFO), FALSE); if(InfoTable) { /* Attempt to publish the info table pointer */ if(!RTL::Atomic::CompareExchangePointer((PVOID *)&InfoStructure->InfoTable, InfoTable, NULLPTR)) { /* Increment the extra info pages counter */ RTL::Atomic::Increment32(&HandleTable->ExtraInfoPages); } else { /* Free the redundant info table */ FreeTableMemory(HandleTable->QuotaProcess, InfoTable, OBJECT_HANDLE_LOWLEVEL_COUNT * sizeof(HANDLE_TABLE_ENTRY_INFO)); /* Retrieve the info table pointer */ InfoTable = InfoStructure->InfoTable; } } } /* Verify that a valid info table pointer is available */ if(!InfoTable) { /* Check if the handle table entry was mapped */ if(TableEntry) { /* Unlock the mapped handle table entry */ UnlockHandleTableEntry(HandleTable, TableEntry); } /* Info table allocation failed, return error code */ return STATUS_UNSUCCESSFUL; } /* Restore the original handle */ ExHandle.GenericHandleOverlay = Handle; InfoTable[ExHandle.Index % OBJECT_HANDLE_LOWLEVEL_COUNT] = *EntryInfo; /* Check if the handle table entry is still locked */ if(TableEntry) { /* Unlock the handle table entry */ UnlockHandleTableEntry(HandleTable, TableEntry); } /* Return success */ return STATUS_SUCCESS; } /** * Captures a snapshot of all handle tables in the system. * * @param SnapshotHandleEntry * Supplies the callback invoked for each handle entry. * * @param HandleInformation * Supplies the buffer to fill with handle information. * * @param Length * Supplies the size of the buffer in bytes. * * @param RequiredLength * Receives the required buffer size. * * @return This routine returns the status of the operation. * * @since XT 1.0 */ XTAPI XTSTATUS OB::HandleTable::SnapshotHandleTables(IN POB_SNAPSHOT_HANDLE_ENTRY SnapshotHandleEntry, IN OUT PSYSTEM_HANDLE_INFORMATION HandleInformation, IN ULONG Length, IN OUT PULONG RequiredLength) { PSYSTEM_HANDLE_TABLE_ENTRY_INFO HandleEntryInfo; PHANDLE_TABLE_ENTRY HandleTableEntry; PHANDLE_TABLE HandleTable; PKTHREAD CurrentThread; PLIST_ENTRY NextEntry; OBJECT_HANDLE Handle; XTSTATUS Status; /* Retrieve the currently executing thread */ CurrentThread = KE::Processor::GetCurrentThread(); /* Initialize the handle entry information */ HandleEntryInfo = &HandleInformation->Handles[0]; HandleInformation->NumberOfHandles = 0; /* Acquire a critical region guard */ KE::CriticalRegionGuard CriticalRegion(CurrentThread); /* Acquire the global handle table list lock */ KE::PushLockSharedGuard PushLock(&HandleTableListLock); /* Traverse the global list of handle tables */ for(NextEntry = HandleTableListHead.Flink; NextEntry != &HandleTableListHead; NextEntry = NextEntry->Flink) { /* Resolve the handle table record */ HandleTable = CONTAIN_RECORD(NextEntry, HANDLE_TABLE, HandleTableList); /* Initialize the handle value */ Handle.Value = 0; /* Iterate through all handles within the current table */ while (TRUE) { /* Retrieve the underlying handle table entry */ HandleTableEntry = LookupHandleTableEntry(HandleTable, Handle); if(!HandleTableEntry) { /* Handle is out of bounds, break the loop */ break; } /* Validate that the entry actually points to a registered object */ if(ValidateObjectEntry(HandleTableEntry)) { /* Increment the global handle count */ HandleInformation->NumberOfHandles += 1; /* Acquire the lock */ if(LockHandleTableEntry(HandleTable, HandleTableEntry)) { /* Invoke the snapshot callback */ Status = (*SnapshotHandleEntry)(&HandleEntryInfo, HandleTable->UniqueProcessId, HandleTableEntry, Handle.GenericHandleOverlay, Length, RequiredLength); /* Release the lock */ UnlockHandleTableEntry(HandleTable, HandleTableEntry); /* Check the callback status code */ if(Status != STATUS_SUCCESS) { /* Callback routine failed, return status code */ return Status; } } } /* Advance to the next handle value */ Handle.Value += OBJECT_HANDLE_VALUE_INCREMENT; } } /* Return success */ return STATUS_SUCCESS; } /** * Captures an extended snapshot of all handle tables in the system. * * @param SnapshotHandleEntry * Supplies the callback invoked for each handle entry. * * @param HandleInformation * Supplies the buffer to fill with extended handle information. * * @param Length * Supplies the size of the buffer in bytes. * * @param RequiredLength * Receives the required buffer size. * * @return This routine returns the status of the operation. * * @since XT 1.0 */ XTAPI XTSTATUS OB::HandleTable::SnapshotHandleTables(IN POB_SNAPSHOT_HANDLE_ENTRY_EX SnapshotHandleEntry, IN OUT PSYSTEM_HANDLE_INFORMATION_EX HandleInformation, IN ULONG Length, IN OUT PULONG RequiredLength) { PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX HandleEntryInfo; PHANDLE_TABLE_ENTRY HandleTableEntry; PHANDLE_TABLE HandleTable; PKTHREAD CurrentThread; PLIST_ENTRY NextEntry; OBJECT_HANDLE Handle; XTSTATUS Status; /* Retrieve the currently executing thread */ CurrentThread = KE::Processor::GetCurrentThread(); /* Initialize the extended handle entry information */ HandleEntryInfo = &HandleInformation->Handles[0]; HandleInformation->NumberOfHandles = 0; /* Acquire a critical region guard */ KE::CriticalRegionGuard CriticalRegion(CurrentThread); /* Acquire the global handle table list lock */ KE::PushLockSharedGuard PushLock(&HandleTableListLock); /* Traverse the global list of handle tables */ for(NextEntry = HandleTableListHead.Flink; NextEntry != &HandleTableListHead; NextEntry = NextEntry->Flink) { /* Resolve the handle table record */ HandleTable = CONTAIN_RECORD(NextEntry, HANDLE_TABLE, HandleTableList); /* Initialize the handle value */ Handle.Value = 0; /* Iterate through all handles within the current table */ while (TRUE) { /* Retrieve the underlying handle table entry */ HandleTableEntry = LookupHandleTableEntry(HandleTable, Handle); if(!HandleTableEntry) { /* Handle is out of bounds, break the loop */ break; } /* Validate that the entry actually points to a registered object */ if(ValidateObjectEntry(HandleTableEntry)) { /* Increment the global extended handle count */ HandleInformation->NumberOfHandles += 1; /* Acquire the lock */ if(LockHandleTableEntry(HandleTable, HandleTableEntry)) { /* Invoke the extended snapshot callback */ Status = (*SnapshotHandleEntry)(&HandleEntryInfo, HandleTable->UniqueProcessId, HandleTableEntry, Handle.GenericHandleOverlay, Length, RequiredLength); /* Release the lock */ UnlockHandleTableEntry(HandleTable, HandleTableEntry); /* Check the callback status code */ if(Status != STATUS_SUCCESS) { /* Callback routine failed, return status code */ return Status; } } } /* Advance to the next handle value */ Handle.Value += OBJECT_HANDLE_VALUE_INCREMENT; } } /* Return success */ return STATUS_SUCCESS; } /** * Sweeps a handle table without synchronization. * * @param HandleTable * Supplies the handle table to sweep. * * @param Callback * Supplies the callback invoked for each valid handle. * * @param Parameter * Supplies an opaque parameter passed to the callback. * * @return This routine does not return any value. * * @since XT 1.0 */ XTAPI VOID OB::HandleTable::SweepHandleTable(IN PHANDLE_TABLE HandleTable, IN POB_ENUMERATE_HANDLE_ROUTINE Callback, IN PVOID Parameter) { PHANDLE_TABLE_ENTRY HandleTableEntry; ULONG Index, RemainingEntries; OBJECT_HANDLE Handle; /* Initialize the handle value */ Handle.Value = OBJECT_HANDLE_VALUE_INCREMENT; /* Traverse the handle table */ while(TRUE) { /* Resolve the current handle value */ HandleTableEntry = LookupHandleTableEntry(HandleTable, Handle); if(!HandleTableEntry) { /* Handle entry lookup fails, break loop */ break; } /* Calculate the number of remaining entries */ RemainingEntries = OBJECT_HANDLE_LOWLEVEL_COUNT - ((Handle.Value / OBJECT_HANDLE_VALUE_INCREMENT) % OBJECT_HANDLE_LOWLEVEL_COUNT); /* Iterate through the remaining handle entries */ for(Index = 0; Index < RemainingEntries; Index++) { /* Lock the current handle table entry */ if(LockHandleTableEntry(HandleTable, HandleTableEntry)) { /* Execute the specified callback routine */ (*Callback)(HandleTableEntry, Handle.GenericHandleOverlay, Parameter); } /* Increment the handle value */ Handle.Value += OBJECT_HANDLE_VALUE_INCREMENT; /* Advance to the next handle slot */ HandleTableEntry++; } } } /** * Unlocks a previously locked handle table entry and wakes up any threads waiting on the table's contention event. * * @param HandleTable * Supplies a pointer to the handle table containing the entry. * * @param HandleTableEntry * Supplies a pointer to the handle table entry to be unlocked. * * @return This routine does not return any value. * * @since XT 1.0 */ XTAPI VOID OB::HandleTable::UnlockHandleTableEntry(IN PHANDLE_TABLE HandleTable, IN PHANDLE_TABLE_ENTRY HandleTableEntry) { /* Unlock entry and signal the handle contention event */ RTL::Atomic::Or64((VOLATILE PLONG_PTR)&HandleTableEntry->Value, (LONG_PTR)OBJECT_HANDLE_TABLE_ENTRY_LOCK_BIT); KE::PushLock::UnblockPushLock(&HandleTable->HandleContentionEvent, NULLPTR); } /** * Checks if a handle table entry is valid and points to an actual object. * * @param Entry * Supplies a pointer to the handle table entry to validate. * * @return This routine returns TRUE if the entry is valid, FALSE otherwise. * * @since XT 1.0 */ XTINLINE BOOLEAN OB::HandleTable::ValidateObjectEntry(IN PHANDLE_TABLE_ENTRY Entry) { /* Return TRUE if the entry and its underlying object pointer exist */ return (Entry && Entry->Object && Entry->NextFreeTableEntry != OBJECT_HANDLE_ADDITIONAL_INFO_SIGNATURE); }