diff --git a/xtoskrnl/CMakeLists.txt b/xtoskrnl/CMakeLists.txt index 9efe19f94..a9dfd59be 100644 --- a/xtoskrnl/CMakeLists.txt +++ b/xtoskrnl/CMakeLists.txt @@ -16,7 +16,6 @@ list(APPEND XTOSKRNL_SOURCE ${XTOSKRNL_SOURCE_DIR}/ar/${ARCH}/traps.cc ${XTOSKRNL_SOURCE_DIR}/ex/data.cc ${XTOSKRNL_SOURCE_DIR}/ex/exports.cc - ${XTOSKRNL_SOURCE_DIR}/ex/handle.cc ${XTOSKRNL_SOURCE_DIR}/ex/laslist.cc ${XTOSKRNL_SOURCE_DIR}/ex/resource.cc ${XTOSKRNL_SOURCE_DIR}/ex/rundown.cc @@ -93,6 +92,7 @@ list(APPEND XTOSKRNL_SOURCE ${XTOSKRNL_SOURCE_DIR}/ob/data.cc ${XTOSKRNL_SOURCE_DIR}/ob/devmap.cc ${XTOSKRNL_SOURCE_DIR}/ob/handle.cc + ${XTOSKRNL_SOURCE_DIR}/ob/hndltlb.cc ${XTOSKRNL_SOURCE_DIR}/ob/lifecycl.cc ${XTOSKRNL_SOURCE_DIR}/ob/obdir.cc ${XTOSKRNL_SOURCE_DIR}/ob/obmgr.cc diff --git a/xtoskrnl/ex/handle.cc b/xtoskrnl/ex/handle.cc deleted file mode 100644 index 3c1b7644c..000000000 --- a/xtoskrnl/ex/handle.cc +++ /dev/null @@ -1,302 +0,0 @@ -/** - * PROJECT: ExectOS - * COPYRIGHT: See COPYING.md in the top level directory - * FILE: xtoskrnl/ex/handle.cc - * DESCRIPTION: Handle Table management interface - * DEVELOPERS: Aiken Harris - */ - -#include - - -/** - * 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 - */ -XTFASTCALL -PHANDLE_TABLE_ENTRY_INFO -EX::Handle::GetHandleInformation(IN PHANDLE_TABLE HandleTable, - IN HANDLE Handle, - IN BOOLEAN Locked) -{ - PHANDLE_TABLE_ENTRY InfoStructure, TableEntry; - PHANDLE_TABLE_ENTRY_INFO ResultInfo; - EXHANDLE BaseHandle, ExHandle; - - /* 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 &= ~(HANDLE_LOWLEVEL_COUNT - 1); - - /* Fetch the base information structure */ - InfoStructure = LookupHandleTableEntry(HandleTable, BaseHandle); - - /* Validate the retrieved structure */ - if(InfoStructure && - InfoStructure->NextFreeTableEntry == HANDLE_ADDITIONAL_INFO_SIGNATURE && - InfoStructure->InfoTable) - { - /* Decode the target index */ - ExHandle.GenericHandleOverlay = Handle; - - /* Resolve the information entry pointer */ - ResultInfo = &InfoStructure->InfoTable[ExHandle.Index % HANDLE_LOWLEVEL_COUNT]; - } - else - { - /* Check if locked */ - if(TableEntry) - { - /* Release the lock */ - UnlockHandleTableEntry(HandleTable, TableEntry); - } - } - } - - /* Return the information pointer */ - return ResultInfo; -} - -/** - * 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 -EX::Handle::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 & EXHANDLE_TABLE_ENTRY_LOCK_BIT) - { - /* Clear the lock bit */ - NewValue = OldValue & ~EXHANDLE_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 & EXHANDLE_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 -EX::Handle::LookupHandleTableEntry(IN PHANDLE_TABLE HandleTable, - IN EXHANDLE Handle) -{ - PHANDLE_TABLE_ENTRY EntryTable; - ULONG MaxHandle, TableLevel; - ULONG_PTR Index, TableCode; - PVOID *DirectoryTable; - - /* Strip the tag bits */ - Handle.Value &= ~(ULONG_PTR)(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 / HANDLE_VALUE_INCREMENT; - - /* Read the table routing state */ - TableCode = *(VOLATILE ULONG_PTR *)&HandleTable->TableCode; - - /* Extract the table depth level */ - TableLevel = (ULONG)(TableCode & 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)HANDLE_LEVEL_CODE_MASK); - - /* Cascade through the directory levels */ - if(TableLevel == 2) - { - /* Resolve the highest level directory routing */ - DirectoryTable = (PVOID *)DirectoryTable[Index / (HANDLE_LOWLEVEL_COUNT * HANDLE_MIDLEVEL_COUNT)]; - } - if(TableLevel >= 1) - { - /* Resolve the intermediate directory routing */ - DirectoryTable = (PVOID *)DirectoryTable[(Index / HANDLE_LOWLEVEL_COUNT) % HANDLE_MIDLEVEL_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 % 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 -EX::Handle::MapHandleToPointer(IN PHANDLE_TABLE HandleTable, - IN HANDLE Handle) -{ - PHANDLE_TABLE_ENTRY TableEntry; - EXHANDLE LocalHandle; - - /* Assume failure initially */ - TableEntry = NULLPTR; - - /* Overlay the generic handle */ - LocalHandle.GenericHandleOverlay = Handle; - - /* Validate the handle index */ - if(LocalHandle.Index & (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; -} - -/** - * 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 -EX::Handle::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)EXHANDLE_TABLE_ENTRY_LOCK_BIT); - KE::PushLock::UnblockPushLock(&HandleTable->HandleContentionEvent, NULLPTR); -} diff --git a/xtoskrnl/includes/ex.hh b/xtoskrnl/includes/ex.hh index 59f9a778a..a4c31ecc5 100644 --- a/xtoskrnl/includes/ex.hh +++ b/xtoskrnl/includes/ex.hh @@ -11,7 +11,6 @@ #include -#include #include #include #include diff --git a/xtoskrnl/includes/ex/handle.hh b/xtoskrnl/includes/ex/handle.hh deleted file mode 100644 index b1e2ba655..000000000 --- a/xtoskrnl/includes/ex/handle.hh +++ /dev/null @@ -1,37 +0,0 @@ -/** - * PROJECT: ExectOS - * COPYRIGHT: See COPYING.md in the top level directory - * FILE: xtoskrnl/includes/ex/handle.hh - * DESCRIPTION: Handle Table management interface - * DEVELOPERS: Aiken Harris - */ - -#ifndef __XTOSKRNL_EX_HANDLE_HH -#define __XTOSKRNL_EX_HANDLE_HH - -#include - - -/* Kernel Executive */ -namespace EX -{ - class Handle - { - public: - STATIC XTFASTCALL PHANDLE_TABLE_ENTRY_INFO GetHandleInformation(IN PHANDLE_TABLE HandleTable, - IN HANDLE Handle, - IN BOOLEAN Locked); - STATIC XTAPI BOOLEAN LockHandleTableEntry(IN PHANDLE_TABLE HandleTable, - IN PHANDLE_TABLE_ENTRY HandleTableEntry); - STATIC XTAPI PHANDLE_TABLE_ENTRY MapHandleToPointer(IN PHANDLE_TABLE HandleTable, - IN HANDLE Handle); - STATIC XTAPI VOID UnlockHandleTableEntry(IN PHANDLE_TABLE HandleTable, - IN PHANDLE_TABLE_ENTRY HandleTableEntry); - - private: - STATIC XTAPI PHANDLE_TABLE_ENTRY LookupHandleTableEntry(IN PHANDLE_TABLE HandleTable, - IN EXHANDLE Handle); - }; -} - -#endif /* __XTOSKRNL_EX_HANDLE_HH */ diff --git a/xtoskrnl/includes/ob.hh b/xtoskrnl/includes/ob.hh index cdd3d321b..3e1366064 100644 --- a/xtoskrnl/includes/ob.hh +++ b/xtoskrnl/includes/ob.hh @@ -13,11 +13,11 @@ #include #include +#include #include #include #include #include #include - #endif /* __XTOSKRNL_OB_HH */ diff --git a/xtoskrnl/includes/ob/hndltlb.hh b/xtoskrnl/includes/ob/hndltlb.hh new file mode 100644 index 000000000..1cfd078c4 --- /dev/null +++ b/xtoskrnl/includes/ob/hndltlb.hh @@ -0,0 +1,106 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/includes/ob/hndltlb.hh + * DESCRIPTION: Object Manager Handle Table Management Interface + * DEVELOPERS: Aiken Harris + */ + +#ifndef __XTOSKRNL_OB_HNDLTLB_HH +#define __XTOSKRNL_OB_HNDLTLB_HH + +#include + + +/* Object Manager */ +namespace OB +{ + class HandleTable + { + private: + STATIC KPUSH_LOCK HandleTableListLock; + STATIC LIST_ENTRY HandleTableListHead; + + public: + STATIC XTAPI BOOLEAN ChangeHandle(IN PHANDLE_TABLE HandleTable, + IN HANDLE Handle, + IN POB_CHANGE_HANDLE_ROUTINE ChangeRoutine, + IN ULONG_PTR Parameter); + STATIC XTAPI HANDLE CreateHandle(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY HandleTableEntry); + STATIC XTAPI PHANDLE_TABLE CreateHandleTable(IN PEPROCESS Process); + STATIC XTAPI BOOLEAN DestroyHandle(IN PHANDLE_TABLE HandleTable, + IN HANDLE Handle, + IN PHANDLE_TABLE_ENTRY HandleTableEntry); + STATIC XTAPI VOID DestroyHandleTable(IN PHANDLE_TABLE HandleTable, + IN POB_DESTROY_HANDLE_ROUTINE DestroyHandleProcedure); + STATIC XTAPI PHANDLE_TABLE DuplicateHandleTable(IN PEPROCESS Process, + IN PHANDLE_TABLE OldHandleTable, + IN POB_DUPLICATE_HANDLE_ROUTINE DupHandleProcedure, + IN ULONG_PTR Mask); + STATIC XTAPI BOOLEAN EnumerateHandleTable(IN PHANDLE_TABLE HandleTable, + IN POB_ENUMERATE_HANDLE_ROUTINE EnumHandleProcedure, + IN PVOID EnumParameter, + OUT PHANDLE Handle); + STATIC XTAPI PHANDLE_TABLE_ENTRY_INFO GetHandleInformation(IN PHANDLE_TABLE HandleTable, + IN HANDLE Handle, + IN BOOLEAN Locked); + STATIC XTAPI VOID InitializeHandleTable(VOID); + STATIC XTAPI BOOLEAN LockHandleTableEntry(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY HandleTableEntry); + STATIC XTAPI PHANDLE_TABLE_ENTRY MapHandleToPointer(IN PHANDLE_TABLE HandleTable, + IN HANDLE Handle); + STATIC XTAPI PHANDLE_TABLE_ENTRY MapHandleToPointer(IN PHANDLE_TABLE HandleTable, + IN HANDLE Handle, + IN KPROCESSOR_MODE PreviousMode); + STATIC XTAPI VOID RemoveHandleTable(IN PHANDLE_TABLE HandleTable); + STATIC XTAPI XTSTATUS SetHandleInformation(IN PHANDLE_TABLE HandleTable, + IN HANDLE Handle, + IN PHANDLE_TABLE_ENTRY_INFO EntryInfo, + IN BOOLEAN EntryLocked); + STATIC XTAPI XTSTATUS SnapshotHandleTables(IN POB_SNAPSHOT_HANDLE_ENTRY SnapshotHandleEntry, + IN OUT PSYSTEM_HANDLE_INFORMATION HandleInformation, + IN ULONG Length, + IN OUT PULONG RequiredLength); + STATIC XTAPI XTSTATUS SnapshotHandleTables(IN POB_SNAPSHOT_HANDLE_ENTRY_EX SnapshotHandleEntry, + IN OUT PSYSTEM_HANDLE_INFORMATION_EX HandleInformation, + IN ULONG Length, + IN OUT PULONG RequiredLength); + STATIC XTAPI VOID SweepHandleTable(IN PHANDLE_TABLE HandleTable, + IN POB_ENUMERATE_HANDLE_ROUTINE EnumHandleProcedure, + IN PVOID EnumParameter); + STATIC XTAPI VOID UnlockHandleTableEntry(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY HandleTableEntry); + + private: + STATIC XTAPI PHANDLE_TABLE AllocateHandleTable(IN PEPROCESS Process, + IN BOOLEAN DoInit); + STATIC XTAPI PHANDLE_TABLE_ENTRY AllocateHandleTableEntry(IN PHANDLE_TABLE HandleTable, + OUT POBJECT_HANDLE Handle); + STATIC XTAPI BOOLEAN AllocateHandleTableEntry(IN PHANDLE_TABLE HandleTable, + IN BOOLEAN DoInit); + STATIC XTAPI PHANDLE_TABLE_ENTRY AllocateLowLevelTable(IN PHANDLE_TABLE HandleTable, + IN BOOLEAN DoInit); + STATIC XTAPI PHANDLE_TABLE_ENTRY *AllocateMediumLevelTable(IN PHANDLE_TABLE HandleTable, + IN BOOLEAN DoInit, + OUT PHANDLE_TABLE_ENTRY *NewLowLevel); + STATIC XTAPI PVOID AllocateTableMemory(IN PEPROCESS QuotaProcess, + IN SIZE_T NumberOfBytes, + IN BOOLEAN ZeroMemory); + STATIC XTAPI VOID FreeHandleTable(IN PHANDLE_TABLE HandleTable); + STATIC XTAPI VOID FreeHandleTableEntry(IN PHANDLE_TABLE HandleTable, + IN OBJECT_HANDLE Handle, + IN PHANDLE_TABLE_ENTRY HandleTableEntry); + STATIC XTAPI VOID FreeLowLevelTable(IN PEPROCESS QuotaProcess, + IN PHANDLE_TABLE_ENTRY TableLevel1); + STATIC XTAPI VOID FreeTableMemory(IN PEPROCESS QuotaProcess, + IN PVOID PoolMemory, + IN SIZE_T NumberOfBytes); + STATIC XTAPI PHANDLE_TABLE_ENTRY LookupHandleTableEntry(IN PHANDLE_TABLE HandleTable, + IN OBJECT_HANDLE Handle); + STATIC XTAPI ULONG MoveFreeHandles(IN PHANDLE_TABLE HandleTable); + STATIC XTINLINE BOOLEAN ValidateObjectEntry(IN PHANDLE_TABLE_ENTRY Entry); + }; +} + +#endif /* __XTOSKRNL_OB_HNDLTLB_HH */ diff --git a/xtoskrnl/ob/data.cc b/xtoskrnl/ob/data.cc index d2355e45b..01e9a2606 100644 --- a/xtoskrnl/ob/data.cc +++ b/xtoskrnl/ob/data.cc @@ -15,6 +15,12 @@ KPUSH_LOCK OB::DeviceMap::DeviceMapLock; /* Indicates whether the system employs unique device maps */ BOOLEAN OB::DeviceMap::UniqueDeviceMaps; +/* The list head for all active handle tables in the system */ +LIST_ENTRY OB::HandleTable::HandleTableListHead; + +/* Pushlock used to synchronize access to the handle table list */ +KPUSH_LOCK OB::HandleTable::HandleTableListLock; + /* Pointer to the system-wide kernel handle table */ PHANDLE_TABLE OB::LifeCycle::KernelHandleTable; diff --git a/xtoskrnl/ob/hndltlb.cc b/xtoskrnl/ob/hndltlb.cc new file mode 100644 index 000000000..ec799bd29 --- /dev/null +++ b/xtoskrnl/ob/hndltlb.cc @@ -0,0 +1,2358 @@ +/** + * 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); +}