diff --git a/xtoskrnl/CMakeLists.txt b/xtoskrnl/CMakeLists.txt index 43efe81..5ed4303 100644 --- a/xtoskrnl/CMakeLists.txt +++ b/xtoskrnl/CMakeLists.txt @@ -89,6 +89,13 @@ list(APPEND XTOSKRNL_SOURCE ${XTOSKRNL_SOURCE_DIR}/mm/probe.cc ${XTOSKRNL_SOURCE_DIR}/mm/pte.cc ${XTOSKRNL_SOURCE_DIR}/mm/quota.cc + ${XTOSKRNL_SOURCE_DIR}/ob/data.cc + ${XTOSKRNL_SOURCE_DIR}/ob/devmap.cc + ${XTOSKRNL_SOURCE_DIR}/ob/lifecycl.cc + ${XTOSKRNL_SOURCE_DIR}/ob/obdir.cc + ${XTOSKRNL_SOURCE_DIR}/ob/obmgr.cc + ${XTOSKRNL_SOURCE_DIR}/ob/security.cc + ${XTOSKRNL_SOURCE_DIR}/ob/typereg.cc ${XTOSKRNL_SOURCE_DIR}/po/idle.cc ${XTOSKRNL_SOURCE_DIR}/ps/data.cc ${XTOSKRNL_SOURCE_DIR}/ps/process.cc diff --git a/xtoskrnl/includes/mm/tags.hh b/xtoskrnl/includes/mm/tags.hh index 225ed96..e88e6ab 100644 --- a/xtoskrnl/includes/mm/tags.hh +++ b/xtoskrnl/includes/mm/tags.hh @@ -19,6 +19,11 @@ #define TAG_MM_OVERFLOW SIGNATURE32('O', 'V', 'F', 'L') #define TAG_MM_MEMORY_POOL SIGNATURE32('P', 'O', 'O', 'L') #define TAG_MM_NONE SIGNATURE32('N', 'O', 'N', 'E') +#define TAG_OB_CREATE_INFO SIGNATURE32('O', 'B', 'C', 'I') +#define TAG_OB_DIRECTORY SIGNATURE32('O', 'B', 'D', 'I') +#define TAG_OB_MEMORY_POOL SIGNATURE32('O', 'B', 'J', 'M') +#define TAG_OB_NAME SIGNATURE32('O', 'B', 'N', 'M') +#define TAG_OB_OBJECT_TYPE SIGNATURE32('O', 'B', 'J', 'T') #define TAG_PS_QUOTA_BLOCK SIGNATURE32('P', 'S', 'Q', 'B') #define TAG_SE_DESCRIPTOR SIGNATURE32('S', 'E', 'S', 'D') diff --git a/xtoskrnl/includes/ob.hh b/xtoskrnl/includes/ob.hh new file mode 100644 index 0000000..1b3e199 --- /dev/null +++ b/xtoskrnl/includes/ob.hh @@ -0,0 +1,22 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/includes/ob.hh + * DESCRIPTION: Object Manager + * DEVELOPERS: Aiken Harris + */ + +#ifndef __XTOSKRNL_OB_HH +#define __XTOSKRNL_OB_HH + +#include + +#include +#include +#include +#include +#include +#include + + +#endif /* __XTOSKRNL_OB_HH */ diff --git a/xtoskrnl/includes/ob/devmap.hh b/xtoskrnl/includes/ob/devmap.hh new file mode 100644 index 0000000..a03a6d5 --- /dev/null +++ b/xtoskrnl/includes/ob/devmap.hh @@ -0,0 +1,31 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/includes/ob/devmap.hh + * DESCRIPTION: Object Manager Device Map + * DEVELOPERS: Aiken Harris + */ + +#ifndef __XTOSKRNL_OB_DEVMAP_HH +#define __XTOSKRNL_OB_DEVMAP_HH + +#include + + +/* Object Manager */ +namespace OB +{ + class DeviceMap + { + private: + STATIC KPUSH_LOCK DeviceMapLock; + STATIC BOOLEAN UniqueDeviceMaps; + + public: + STATIC XTAPI POBJECT_DIRECTORY GetGlobalDevicesDirectory(IN POBJECT_DIRECTORY Directory); + STATIC XTAPI BOOLEAN GetUniqueDeviceMaps(VOID); + STATIC XTAPI XTSTATUS InitializeDeviceMap(VOID); + }; +} + +#endif /* __XTOSKRNL_OB_DEVMAP_HH */ diff --git a/xtoskrnl/includes/ob/lifecycl.hh b/xtoskrnl/includes/ob/lifecycl.hh new file mode 100644 index 0000000..f92a4c5 --- /dev/null +++ b/xtoskrnl/includes/ob/lifecycl.hh @@ -0,0 +1,86 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/includes/ob/lifecycl.hh + * DESCRIPTION: Object Manager Lifecycle Management + * DEVELOPERS: Aiken Harris + */ + +#ifndef __XTOSKRNL_OB_LIFECYCL_HH +#define __XTOSKRNL_OB_LIFECYCL_HH + +#include + + +/* Object Manager */ +namespace OB +{ + class LifeCycle + { + private: + STATIC PVOID RemoveObjectList; + STATIC WORK_QUEUE_ITEM RemoveObjectWorkItem; + + public: + STATIC XTAPI XTSTATUS AllocateObject(IN POBJECT_CREATE_INFORMATION CreateInfo, + IN KPROCESSOR_MODE OwnerProcessorMode, + IN POBJECT_TYPE ObjectType, + IN PUNICODE_STRING ObjectName, + IN ULONG ObjectBodySize, + OUT POBJECT_HEADER *ReturnedObjectHeader); + STATIC XTAPI VOID DeferObjectDeletion(IN POBJECT_HEADER ObjectHeader); + STATIC XTFASTCALL LONG_PTR DereferenceObject(IN PVOID Object); + STATIC XTFASTCALL LONG_PTR DereferenceObject(IN PVOID Object, + IN ULONG Count); + STATIC XTFASTCALL LONG_PTR DereferenceObjectDeferDelete(IN PVOID Object); + STATIC XTFASTCALL VOID DereferenceObjectNameInformation(IN POBJECT_HEADER_NAME_INFO NameInfo); + STATIC XTFASTCALL POBJECT_HEADER_CREATOR_INFO GetObjectCreatorInformation(IN POBJECT_HEADER Header); + STATIC XTFASTCALL POBJECT_HEADER_NAME_INFO GetObjectNameInformation(IN POBJECT_HEADER Header); + STATIC XTAPI VOID InitializeObjectLifeCycle(VOID); + STATIC XTFASTCALL LONG_PTR ReferenceObject(IN PVOID Object); + STATIC XTFASTCALL LONG_PTR ReferenceObject(IN PVOID Object, + IN ULONG Count); + STATIC XTFASTCALL POBJECT_HEADER_NAME_INFO ReferenceObjectNameInformation(IN POBJECT_HEADER ObjectHeader); + + private: + STATIC XTFASTCALL PWCH AllocateObjectName(IN ULONG Length, + IN BOOLEAN UseLookaside, + IN OUT PUNICODE_STRING ObjectName); + STATIC XTAPI VOID CalculateOptionalHeaderSize(IN POBJECT_CREATE_INFORMATION CreateInfo, + IN POBJECT_TYPE ObjectType, + IN PUNICODE_STRING ObjectName, + OUT POBJECT_OPTIONAL_HEADER_LAYOUT Layout); + STATIC XTAPI XTSTATUS CaptureObjectCreateInformation(IN POBJECT_TYPE ObjectType, + IN KPROCESSOR_MODE ProcessorMode, + IN KPROCESSOR_MODE OwnerProcessorMode, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN OUT PUNICODE_STRING CapturedObjectName, + IN POBJECT_CREATE_INFORMATION ObjectCreateInfo, + IN BOOLEAN UseLookaside); + STATIC XTAPI XTSTATUS CaptureObjectName(IN KPROCESSOR_MODE ProcessorMode, + IN PUNICODE_STRING ObjectName, + IN OUT PUNICODE_STRING CapturedObjectName, + IN BOOLEAN UseLookaside); + STATIC XTAPI XTSTATUS CreateObject(IN KPROCESSOR_MODE ProcessorMode, + IN POBJECT_TYPE ObjectType, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN KPROCESSOR_MODE OwnerProcessorMode, + IN OUT PVOID ParseContext, + IN ULONG ObjectBodySize, + IN ULONG PagedPoolCharge, + IN ULONG NonPagedPoolCharge, + OUT PVOID *Object); + STATIC XTAPI VOID DeleteObject(IN PVOID Object, + IN BOOLEAN CalledOnWorkerThread); + STATIC XTAPI VOID FreeObject(IN PVOID Object); + STATIC XTFASTCALL VOID FreeObjectCreateInformation(IN POBJECT_CREATE_INFORMATION CreateInfo); + STATIC XTFASTCALL VOID FreeObjectName(IN OUT PUNICODE_STRING ObjectName); + STATIC XTAPI PVOID GetObjectAllocationBase(IN POBJECT_HEADER ObjectHeader); + STATIC XTAPI VOID ProcessDeferredDeletionQueue(IN PVOID Parameter); + STATIC XTFASTCALL VOID ReleaseObjectCreateInformation(IN POBJECT_CREATE_INFORMATION CreateInfo); + STATIC XTAPI VOID ReturnObjectQuota(IN POBJECT_HEADER ObjectHeader, + IN POBJECT_TYPE ObjectType); + }; +} + +#endif /* __XTOSKRNL_OB_LIFECYCL_HH */ diff --git a/xtoskrnl/includes/ob/obdir.hh b/xtoskrnl/includes/ob/obdir.hh new file mode 100644 index 0000000..f864b85 --- /dev/null +++ b/xtoskrnl/includes/ob/obdir.hh @@ -0,0 +1,53 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/includes/ob/obdir.hh + * DESCRIPTION: Object Manager Security API + * DEVELOPERS: Aiken Harris + */ + +#ifndef __XTOSKRNL_OB_OBDIR_HH +#define __XTOSKRNL_OB_OBDIR_HH + +#include + + +/* Object Manager */ +namespace OB +{ + class Directory + { + public: + STATIC XTAPI BOOLEAN DeleteDirectoryEntry(IN POBJECT_LOOKUP_CONTEXT LookupContext); + STATIC XTFASTCALL VOID InitializeLookupContext(OUT POBJECT_LOOKUP_CONTEXT LookupContext); + STATIC XTAPI BOOLEAN InsertDirectoryEntry(IN OUT POBJECT_DIRECTORY Directory, + IN POBJECT_LOOKUP_CONTEXT LookupContext, + IN OUT POBJECT_HEADER ObjectHeader); + STATIC XTFASTCALL VOID LockLookupContext(IN POBJECT_DIRECTORY Directory, + IN POBJECT_LOOKUP_CONTEXT LookupContext); + + STATIC XTAPI PVOID LookupDirectoryEntry(IN POBJECT_DIRECTORY Directory, + IN PUNICODE_STRING Name, + IN ULONG Attributes, + IN BOOLEAN SearchGlobalDirectory, + OUT POBJECT_LOOKUP_CONTEXT LookupContext); + STATIC XTFASTCALL VOID ReleaseLookupContext(IN OUT POBJECT_LOOKUP_CONTEXT LookupContext); + + private: + STATIC XTFASTCALL VOID AcquireExclusiveDirectoryLock(IN OUT POBJECT_DIRECTORY Directory, + IN OUT POBJECT_LOOKUP_CONTEXT LookupContext); + STATIC XTFASTCALL VOID AcquireSharedDirectoryLock(IN POBJECT_DIRECTORY Directory, + IN POBJECT_LOOKUP_CONTEXT LookupContext); + STATIC XTFASTCALL ULONG ComputeObjectNameHash(IN PCUNICODE_STRING Name); + STATIC XTFASTCALL VOID ReleaseDirectoryLock(IN OUT POBJECT_DIRECTORY Directory, + IN OUT POBJECT_LOOKUP_CONTEXT LookupContext); + STATIC XTFASTCALL VOID ReleaseLookupContextObject(IN OUT POBJECT_LOOKUP_CONTEXT LookupContext); + STATIC XTFASTCALL PVOID SearchDirectory(IN POBJECT_DIRECTORY Directory, + IN PCUNICODE_STRING Name, + IN BOOLEAN CaseInsensitive, + IN ULONG HashIndex, + IN POBJECT_LOOKUP_CONTEXT LookupContext); + }; +} + +#endif /* __XTOSKRNL_OB_OBDIR_HH */ diff --git a/xtoskrnl/includes/ob/obmgr.hh b/xtoskrnl/includes/ob/obmgr.hh new file mode 100644 index 0000000..9c2e1f8 --- /dev/null +++ b/xtoskrnl/includes/ob/obmgr.hh @@ -0,0 +1,30 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/includes/ob/obmgr.hh + * DESCRIPTION: Object Manager + * DEVELOPERS: Aiken Harris + */ + +#ifndef __XTOSKRNL_OB_OBMGR_HH +#define __XTOSKRNL_OB_OBMGR_HH + +#include + + +/* Object Manager */ +namespace OB +{ + class Manager + { + private: + STATIC GENERAL_LOOKASIDE CreateInfoList; + STATIC GENERAL_LOOKASIDE NameBufferList; + + public: + STATIC XTAPI XTSTATUS InitializeObjectManager(VOID); + STATIC XTAPI VOID InitializeSystemLookasideList(VOID); + }; +} + +#endif /* __XTOSKRNL_OB_OBMGR_HH */ diff --git a/xtoskrnl/includes/ob/security.hh b/xtoskrnl/includes/ob/security.hh new file mode 100644 index 0000000..8398b82 --- /dev/null +++ b/xtoskrnl/includes/ob/security.hh @@ -0,0 +1,32 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/includes/ob/security.hh + * DESCRIPTION: Object Manager Security API + * DEVELOPERS: Aiken Harris + */ + +#ifndef __XTOSKRNL_OB_SECURITY_HH +#define __XTOSKRNL_OB_SECURITY_HH + +#include + + +/* Object Manager */ +namespace OB +{ + class Security + { + public: + STATIC XTAPI XTSTATUS ProcessObjectSecurityDescriptor(IN PVOID Object, + IN SECURITY_OPERATION_CODE OperationCode, + IN PSECURITY_INFORMATION SecurityInformation, + IN OUT PSECURITY_DESCRIPTOR SecurityDescriptor, + IN OUT PULONG Length, + IN OUT PSECURITY_DESCRIPTOR *OldSecurityDescriptor, + IN MMPOOL_TYPE PoolType, + IN PGENERIC_MAPPING GenericMapping); + }; +} + +#endif /* __XTOSKRNL_OB_SECURITY_HH */ diff --git a/xtoskrnl/includes/ob/typereg.hh b/xtoskrnl/includes/ob/typereg.hh new file mode 100644 index 0000000..5e4d2d4 --- /dev/null +++ b/xtoskrnl/includes/ob/typereg.hh @@ -0,0 +1,44 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/includes/ob/typereg.hh + * DESCRIPTION: Object Manager Type Registry + * DEVELOPERS: Aiken Harris + */ + +#ifndef __XTOSKRNL_OB_TYPEREG_HH +#define __XTOSKRNL_OB_TYPEREG_HH + +#include + + +/* Object Manager */ +namespace OB +{ + class TypeRegistry + { + private: + STATIC KEVENT DefaultEvent; + STATIC POBJECT_TYPE MasterObjectType; + STATIC POBJECT_DIRECTORY ObjectTypeDirectory; + STATIC POBJECT_TYPE ObjectTypesTable[OBJECT_MAX_DEFINED_OBJECT_TYPES]; + + public: + STATIC XTAPI XTSTATUS CreateObjectType(IN PUNICODE_STRING TypeName, + IN POBJECT_TYPE_INITIALIZER ObjectTypeInitializer, + IN PSECURITY_DESCRIPTOR SecurityDescriptor, + OUT POBJECT_TYPE *ObjectType); + STATIC XTAPI VOID DeleteObjectType(IN PVOID Object); + STATIC XTAPI XTSTATUS InitializeObjectTypeRegistry(VOID); + + private: + STATIC XTAPI ULONG GenerateObjectPoolTag(IN PUNICODE_STRING TypeName); + STATIC XTAPI VOID InitializeObjectType(IN OUT POBJECT_TYPE ObjectType, + IN POBJECT_TYPE_INITIALIZER Initializer, + IN PUNICODE_STRING ObjectName); + STATIC XTAPI XTSTATUS ValidateObjectTypeParameters(IN PUNICODE_STRING TypeName, + IN POBJECT_TYPE_INITIALIZER Initializer); + }; +} + +#endif /* __XTOSKRNL_OB_TYPEREG_HH */ diff --git a/xtoskrnl/ob/data.cc b/xtoskrnl/ob/data.cc new file mode 100644 index 0000000..870c5ac --- /dev/null +++ b/xtoskrnl/ob/data.cc @@ -0,0 +1,40 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/ob/data.cc + * DESCRIPTION: Object Manager global and static data + * DEVELOPERS: Aiken Harris + */ + +#include + + +/* Synchronizes shared read and exclusive write access to system device map structures */ +KPUSH_LOCK OB::DeviceMap::DeviceMapLock; + +/* Indicates whether the system employs unique device maps */ +BOOLEAN OB::DeviceMap::UniqueDeviceMaps; + +/* System work queue item responsible for scheduling the deferred object deletion */ +WORK_QUEUE_ITEM OB::LifeCycle::RemoveObjectWorkItem; + +/* Default kernel event utilized by the Object Manager */ +KEVENT OB::TypeRegistry::DefaultEvent; + +/* Fundamental Type object type */ +POBJECT_TYPE OB::TypeRegistry::MasterObjectType; + +/* Points to the Object Types directory */ +POBJECT_DIRECTORY OB::TypeRegistry::ObjectTypeDirectory; + +/* Index-based lookup for all registered object types */ +POBJECT_TYPE OB::TypeRegistry::ObjectTypesTable[OBJECT_MAX_DEFINED_OBJECT_TYPES]; + +/* General lookaside list used for allocation and deallocation of OBJECT_CREATE_INFORMATION structures */ +GENERAL_LOOKASIDE OB::Manager::CreateInfoList; + +/* General lookaside list used for allocation and deallocation buffers for Unicode object names */ +GENERAL_LOOKASIDE OB::Manager::NameBufferList; + +/* The list head for tracking objects that have been marked for deferred deletion */ +PVOID OB::LifeCycle::RemoveObjectList; diff --git a/xtoskrnl/ob/devmap.cc b/xtoskrnl/ob/devmap.cc new file mode 100644 index 0000000..143036b --- /dev/null +++ b/xtoskrnl/ob/devmap.cc @@ -0,0 +1,82 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/ob/devmap.cc + * DESCRIPTION: Object Manager Device Map + * DEVELOPERS: Aiken Harris + */ + +#include + + +/** + * Retrieves the Global Devices Directory associated with a given directory's device map. + * + * @param Directory + * Supplies a pointer to the object directory to query. + * + * @return Returns a pointer to the global devices directory, or NULLPTR if none exists. + * + * @since XT 1.0 + */ +XTAPI +POBJECT_DIRECTORY +OB::DeviceMap::GetGlobalDevicesDirectory(IN POBJECT_DIRECTORY Directory) +{ + POBJECT_DIRECTORY GlobalDevicesDirectory; + + /* Assume no mapping exists */ + GlobalDevicesDirectory = NULLPTR; + + /* Acquire the device map lock */ + KE::PushLock::AcquireExclusivePushLock(&DeviceMapLock); + + /* Check if the directory has an associated device map */ + if(Directory->DeviceMap) + { + /* Extract the global devices directory */ + GlobalDevicesDirectory = Directory->DeviceMap->GlobalDevicesDirectory; + } + + /* Release the device map lock */ + KE::PushLock::ReleaseExclusivePushLock(&DeviceMapLock); + + /* Return the resolved directory */ + return GlobalDevicesDirectory; +} + +/** + * Retrieves the current state of the unique device maps configuration. + * + * @return This routine returns TRUE if unique device maps are enabled, or FALSE otherwise. + * + * @since XT 1.0 + */ +XTAPI +BOOLEAN +OB::DeviceMap::GetUniqueDeviceMaps(VOID) +{ + /* Return the unique device maps flag */ + return UniqueDeviceMaps; +} + +/** + * Initializes the Object Manager's device map subsystem. + * + * @return This routine returns a status code indicating the success or failure of the operation. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +OB::DeviceMap::InitializeDeviceMap(VOID) +{ + /* Initialize device map lock */ + KE::PushLock::InitializePushLock(&DeviceMapLock); + + /* Temporarily disable unique device maps */ + UniqueDeviceMaps = FALSE; + + /* Return success */ + return STATUS_SUCCESS; +} diff --git a/xtoskrnl/ob/lifecycl.cc b/xtoskrnl/ob/lifecycl.cc new file mode 100644 index 0000000..1352a8d --- /dev/null +++ b/xtoskrnl/ob/lifecycl.cc @@ -0,0 +1,1541 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/ob/lifecycl.cc + * DESCRIPTION: Object Manager Lifecycle Management + * DEVELOPERS: Aiken Harris + */ + +#include + + +/** + * Allocates and initializes the memory block for a new object and its headers. + * + * @param CreateInfo + * Supplies a pointer to the object creation information. + * + * @param OwnerProcessorMode + * Supplies the processor mode that will own the object. + * + * @param ObjectType + * Supplies an optional pointer to the object type definition. + * + * @param ObjectName + * Supplies an optional pointer to the unicode string representing the object's name. + * + * @param ObjectBodySize + * Supplies the size, in bytes, of the object body itself. + * + * @param ReturnedObjectHeader + * Receives a pointer to the newly allocated object header. + * + * @return This routine returns a status code indicating the success or failure of the operation. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +OB::LifeCycle::AllocateObject(IN POBJECT_CREATE_INFORMATION CreateInfo, + IN KPROCESSOR_MODE OwnerProcessorMode, + IN POBJECT_TYPE ObjectType, + IN PUNICODE_STRING ObjectName, + IN ULONG ObjectBodySize, + OUT POBJECT_HEADER *ReturnedObjectHeader) +{ + POBJECT_HEADER_CREATOR_INFO CreatorInfoHeader; + POBJECT_HEADER_HANDLE_INFO HandleInfoHeader; + POBJECT_HEADER_QUOTA_INFO QuotaInfoHeader; + POBJECT_HEADER_NAME_INFO NameInfoHeader; + OBJECT_OPTIONAL_HEADER_LAYOUT Layout; + ULONG Tag, TotalAllocationSize; + POBJECT_HEADER ObjectHeader; + MMPOOL_TYPE PoolType; + PVOID PoolBuffer; + XTSTATUS Status; + PUCHAR Offset; + + /* Calculate the sizes for all optional headers */ + CalculateOptionalHeaderSize(CreateInfo, ObjectType, ObjectName, &Layout); + + /* Determine the pool type */ + if(ObjectType == NULLPTR || ObjectType->TypeInfo.PoolType == NonPagedPool) + { + /* Use non-paged pool */ + PoolType = NonPagedPool; + } + else + { + /* Use paged pool */ + PoolType = PagedPool; + } + + /* Compute the total required block size */ + TotalAllocationSize = Layout.TotalSize + sizeof(OBJECT_HEADER) + ObjectBodySize; + Tag = (ObjectType == NULLPTR) ? TAG_OB_OBJECT_TYPE : ObjectType->Key; + + /* Allocate the memory block from the selected pool */ + Status = MM::Allocator::AllocatePool(PoolType, TotalAllocationSize, &PoolBuffer, Tag); + if(Status != STATUS_SUCCESS || PoolBuffer == NULLPTR) + { + /* Allocation failed, return error code */ + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Set the initial offset */ + Offset = (PUCHAR)PoolBuffer; + + /* Check if there is a Quota Information header */ + if(Layout.QuotaInfoSize != 0) + { + /* Initialize the Quota Information header */ + QuotaInfoHeader = (POBJECT_HEADER_QUOTA_INFO)Offset; + QuotaInfoHeader->ExclusiveProcess = NULLPTR; + QuotaInfoHeader->NonPagedPoolCharge = CreateInfo->NonPagedPoolCharge; + QuotaInfoHeader->PagedPoolCharge = CreateInfo->PagedPoolCharge; + QuotaInfoHeader->SecurityDescriptorCharge = CreateInfo->SecurityDescriptorCharge; + + /* Advance the pointer */ + Offset += Layout.QuotaInfoSize; + } + + /* Check if there is a Handle Information header */ + if(Layout.HandleInfoSize != 0) + { + /* Initialize the Handle Information header */ + HandleInfoHeader = (POBJECT_HEADER_HANDLE_INFO)Offset; + HandleInfoHeader->SingleEntry.HandleCount = 0; + + /* Advance the pointer */ + Offset += Layout.HandleInfoSize; + } + + /* Check if there is a Name Information header */ + if(Layout.NameInfoSize != 0) + { + /* Initialize the Name Information header */ + NameInfoHeader = (POBJECT_HEADER_NAME_INFO)Offset; + NameInfoHeader->Directory = NULLPTR; + NameInfoHeader->Name = *ObjectName; + NameInfoHeader->QueryReferences = 1; + + /* Advance the pointer */ + Offset += Layout.NameInfoSize; + } + + /* Check if there is a Creator Information header */ + if(Layout.CreatorInfoSize != 0) + { + /* Initialize the Creator Information header */ + CreatorInfoHeader = (POBJECT_HEADER_CREATOR_INFO)Offset; + CreatorInfoHeader->CreatorBackTraceIndex = 0; + CreatorInfoHeader->CreatorUniqueProcess = PS::Process::GetCurrentProcess()->UniqueProcessId; + RTL::LinkedList::InitializeListHead(&CreatorInfoHeader->TypeList); + + /* Advance the pointer */ + Offset += Layout.CreatorInfoSize; + } + + /* Retrieve the object header */ + ObjectHeader = (POBJECT_HEADER)Offset; + + /* Initialize the object header */ + ObjectHeader->Flags = OBJECT_FLAG_NEW_OBJECT; + ObjectHeader->HandleInfoOffset = Layout.HandleInfoSize ? (UCHAR)(Layout.TotalSize - Layout.QuotaInfoSize) : 0; + ObjectHeader->NameInfoOffset = Layout.NameInfoSize ? (UCHAR)(Layout.CreatorInfoSize + Layout.NameInfoSize) : 0; + ObjectHeader->QuotaInfoOffset = Layout.QuotaInfoSize ? (UCHAR)Layout.TotalSize : 0; + + /* Check if there is creator information */ + if(Layout.CreatorInfoSize != 0) + { + /* Flag the presence of creator information */ + ObjectHeader->Flags |= OBJECT_FLAG_CREATOR_INFO; + } + + /* Check if there is handle information */ + if(Layout.HandleInfoSize != 0) + { + /* Flag the presence of handle information */ + ObjectHeader->Flags |= OBJECT_FLAG_SINGLE_PROCESS; + } + + /* Check if the object is being created in kernel mode */ + if(OwnerProcessorMode == KernelMode) + { + /* Assign kernel mode ownership */ + ObjectHeader->Flags |= OBJECT_FLAG_KERNEL_MODE; + } + + /* Check if creation information was provided */ + if(CreateInfo) + { + /* Check if the object is permanent */ + if(CreateInfo->Attributes & OBJECT_PERMANENT) + { + /* Flag the object to prevent deletion */ + ObjectHeader->Flags |= OBJECT_FLAG_PERMANENT; + } + + /* Check if the object is exclusive */ + if(CreateInfo->Attributes & OBJECT_EXCLUSIVE) + { + /* Flag the object as exclusive to a single process */ + ObjectHeader->Flags |= OBJECT_FLAG_EXCLUSIVE; + } + } + + /* Initialize reference counters and data associations */ + ObjectHeader->HandleCount = 0; + ObjectHeader->ObjectCreateInfo = CreateInfo; + ObjectHeader->PointerCount = 1; + ObjectHeader->SecurityDescriptor = NULLPTR; + ObjectHeader->Type = ObjectType; + + /* Check if an object type was provided */ + if(ObjectType) + { + /* Increment the total active objects counter */ + RTL::Atomic::Increment32((VOLATILE PLONG)&ObjectType->TotalNumberOfObjects); + + /* Check if the current total exceeds the previous peak */ + if(ObjectType->TotalNumberOfObjects > ObjectType->HighWaterNumberOfObjects) + { + /* Update the high-water mark */ + ObjectType->HighWaterNumberOfObjects = ObjectType->TotalNumberOfObjects; + } + } + + /* Return the pointer to the initialized object */ + *ReturnedObjectHeader = ObjectHeader; + + /* Return success */ + return STATUS_SUCCESS; +} + +/** + * Allocates a buffer for an object name string. + * + * @param Length + * Supplies the exact length of the object name in bytes, excluding the NULL terminator. + * + * @param UseLookaside + * Indicates whether the allocation should attempt to draw from the paged pool lookaside list. + * + * @param ObjectName + * Supplies a pointer to the Unicode string descriptor to be initialized with the new buffer. + * + * @return This routine returns a pointer to the allocated buffer, or NULLPTR if allocation fails. + * + * @since XT 1.0 + */ +XTFASTCALL +PWCH +OB::LifeCycle::AllocateObjectName(IN ULONG Length, + IN BOOLEAN UseLookaside, + IN OUT PUNICODE_STRING ObjectName) +{ + ULONG MaximumLength; + PVOID Buffer; + + /* Calculate the required maximum length and initialize the buffer pointer */ + MaximumLength = Length + sizeof(WCHAR); + Buffer = NULLPTR; + + /* Check if the general pool must be used */ + if(!UseLookaside || MaximumLength > OBJECT_NAME_BUFFER_SIZE) + { + /* Allocate from the Memory Manager */ + MM::Allocator::AllocatePool(PagedPool, MaximumLength, (PVOID *)&Buffer, TAG_OB_NAME); + } + else + { + /* Set maximum length to the fixed lookaside entry size */ + MaximumLength = OBJECT_NAME_BUFFER_SIZE; + + /* Allocate from the lookaside list */ + Buffer = EX::LookasideList::AllocateFromPerProcessorList(LookasideNameBufferList); + } + + /* Initialize the Unicode string descriptor */ + ObjectName->Buffer = (PWCH)Buffer; + ObjectName->Length = (USHORT)Length; + ObjectName->MaximumLength = (USHORT)MaximumLength; + + /* Return the buffer pointer */ + return (PWCH)Buffer; +} + +/** + * Calculates the memory layout and sizes for optional object header components. + * + * @param CreateInfo + * Supplies a pointer to the object creation information. + * + * @param ObjectType + * Supplies an optional pointer to the object type definition. + * + * @param ObjectName + * Supplies an optional pointer to the unicode string representing the object's name. + * + * @param Layout + * Supplies a pointer to the layout structure that will receive the calculated sizes. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +OB::LifeCycle::CalculateOptionalHeaderSize(IN POBJECT_CREATE_INFORMATION CreateInfo, + IN POBJECT_TYPE ObjectType, + IN PUNICODE_STRING ObjectName, + OUT POBJECT_OPTIONAL_HEADER_LAYOUT Layout) +{ + /* Zero the layout structure */ + RTL::Memory::ZeroMemory(Layout, sizeof(OBJECT_OPTIONAL_HEADER_LAYOUT)); + + /* Check if the CreateInfo pointer is valid */ + if(CreateInfo == NULLPTR) + { + /* Assign default sizes */ + Layout->NameInfoSize = sizeof(OBJECT_HEADER_NAME_INFO); + Layout->CreatorInfoSize = sizeof(OBJECT_HEADER_CREATOR_INFO); + } + else + { + /* Determine if quota tracking is required */ + if(ObjectType != NULLPTR && + (((CreateInfo->PagedPoolCharge != ObjectType->TypeInfo.DefaultPagedPoolCharge || + CreateInfo->NonPagedPoolCharge != ObjectType->TypeInfo.DefaultNonPagedPoolCharge || + CreateInfo->SecurityDescriptorCharge > SE_DEFAULT_SECURITY_QUOTA) && + PS::Process::GetCurrentProcess() != PS::Process::GetSystemProcess()) || + (CreateInfo->Attributes & OBJECT_EXCLUSIVE))) + { + /* Set the size for the Quota Information header */ + Layout->QuotaInfoSize = sizeof(OBJECT_HEADER_QUOTA_INFO); + } + + /* Check if the object type requires maintaining handle counts */ + if(ObjectType && ObjectType->TypeInfo.MaintainHandleCount) + { + /* Set the size for the Handle Information header */ + Layout->HandleInfoSize = sizeof(OBJECT_HEADER_HANDLE_INFO); + } + + /* Check if a valid object name was provided */ + if(ObjectName && ObjectName->Buffer) + { + /* Set the size for the Name Information header */ + Layout->NameInfoSize = sizeof(OBJECT_HEADER_NAME_INFO); + } + + /* Check if the object type requires maintaining a creator type list */ + if(ObjectType && ObjectType->TypeInfo.MaintainTypeList) + { + /* Set the size for the Creator Information header */ + Layout->CreatorInfoSize = sizeof(OBJECT_HEADER_CREATOR_INFO); + } + } + + /* Calculate the total size required for all optional headers */ + Layout->TotalSize = Layout->QuotaInfoSize + + Layout->HandleInfoSize + + Layout->NameInfoSize + + Layout->CreatorInfoSize; +} + +/** + * Captures and validates object creation information and attributes passed. + * + * @param ObjectType + * Optionally supplies a pointer to the type of object being created. + * + * @param ProcessorMode + * Supplies the processor mode used to probe user-mode structures for accessibility. + * + * @param OwnerProcessorMode + * Supplies the processor mode that will own the object. + * + * @param ObjectAttributes + * Supplies a pointer to the object attributes structure. + * + * @param CapturedObjectName + * Supplies a pointer to a Unicode string descriptor that will receive the captured name. + * + * @param ObjectCreateInfo + * Supplies a pointer to the internal structure receiving the validated creation parameters. + * + * @param UseLookaside + * Indicates whether lookaside lists should be used for name buffer allocations. + * + * @return This routine returns a status code indicating the success or failure of the operation. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +OB::LifeCycle::CaptureObjectCreateInformation(IN POBJECT_TYPE ObjectType, + IN KPROCESSOR_MODE ProcessorMode, + IN KPROCESSOR_MODE OwnerProcessorMode, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN OUT PUNICODE_STRING CapturedObjectName, + IN POBJECT_CREATE_INFORMATION ObjectCreateInfo, + IN BOOLEAN UseLookaside) +{ + PSECURITY_QUALITY_OF_SERVICE SecurityQos; + PSECURITY_DESCRIPTOR SecurityDescriptor; + PUNICODE_STRING ObjectName; + XTSTATUS Status; + ULONG Size; + + /* Initialize local state variables */ + ObjectName = NULLPTR; + Status = STATUS_SUCCESS; + + /* Zero out the output structure */ + RTL::Memory::ZeroMemory(ObjectCreateInfo, sizeof(OBJECT_CREATE_INFORMATION)); + + /* Enter structured exception handler */ + __try + { + /* Check if the caller supplied object attributes */ + if(ObjectAttributes) + { + /* Check if the request originated from user mode */ + if(ProcessorMode != KernelMode) + { + /* Probe the object attributes structure */ + MM::Probe::ProbeForReadStructure(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG_PTR)); + } + + /* Validate structure length and verify that no unknown attributes are specified */ + if((ObjectAttributes->Length != sizeof(OBJECT_ATTRIBUTES)) || + (ObjectAttributes->Attributes & ~OBJECT_VALID_ATTRIBUTES)) + { + /* Return error code */ + Status = STATUS_INVALID_PARAMETER; + } + else + { + /* Capture the root directory and sanitize basic attribute flags */ + ObjectCreateInfo->RootDirectory = ObjectAttributes->RootDirectory; + ObjectCreateInfo->Attributes = ObjectAttributes->Attributes & OBJECT_VALID_ATTRIBUTES; + + /* Check if the owner works in user mode */ + if(OwnerProcessorMode != KernelMode) + { + /* Enforce kernel handle restrictions */ + ObjectCreateInfo->Attributes &= ~OBJECT_KERNEL_HANDLE; + } + + /* Cache pointers for subsequent capture and validation */ + ObjectName = ObjectAttributes->ObjectName; + SecurityDescriptor = ObjectAttributes->SecurityDescriptor; + SecurityQos = ObjectAttributes->SecurityQualityOfService; + + /* Check if security descriptor is present */ + if(SecurityDescriptor) + { + /* Capture the security descriptor*/ + Status = SE::Descriptor::CaptureSecurityDescriptor(SecurityDescriptor, ProcessorMode, PagedPool, + TRUE, &ObjectCreateInfo->SecurityDescriptor); + if(Status != STATUS_SUCCESS) + { + /* Clear the descriptor pointer on failure */ + ObjectCreateInfo->SecurityDescriptor = NULLPTR; + } + else + { + /* Compute and assign the security quota charge for the descriptor */ + SE::Descriptor::ComputeQuotaInformationSize(ObjectCreateInfo->SecurityDescriptor, &Size); + ObjectCreateInfo->ProbeMode = ProcessorMode; + ObjectCreateInfo->SecurityDescriptorCharge = SE::Descriptor::ComputeSecurityQuota(Size); + } + } + + /* Check if previous operations succeeded and QoS data is available */ + if(Status == STATUS_SUCCESS && SecurityQos) + { + /* Check if the request originated from user mode */ + if(ProcessorMode != KernelMode) + { + /* Probe the QoS structure */ + MM::Probe::ProbeForReadStructure(SecurityQos, sizeof(SECURITY_QUALITY_OF_SERVICE), sizeof(ULONG)); + } + + /* Copy QoS data into the local structure and link the pointer */ + ObjectCreateInfo->SecurityQualityOfService = *SecurityQos; + ObjectCreateInfo->SecurityQos = &ObjectCreateInfo->SecurityQualityOfService; + } + } + } + } + __except (RTL::Exception::SystemFilter()) + { + /* Catch memory access violations */ + Status = EXCEPTION_CODE; + } + + /* Check If all structure and security captures succeeded */ + if(Status == STATUS_SUCCESS) + { + /* Validate if an object name is available */ + if(ObjectName) + { + /* Capture object name */ + Status = CaptureObjectName(ProcessorMode, ObjectName, CapturedObjectName, UseLookaside); + } + else + { + /* Initialize an empty name descriptor */ + CapturedObjectName->Buffer = NULLPTR; + CapturedObjectName->Length = 0; + CapturedObjectName->MaximumLength = 0; + + /* Check if this is a root directory */ + if(ObjectCreateInfo->RootDirectory) + { + /* Root directory cannot be used without an object name, set error code */ + Status = STATUS_OBJECT_NAME_INVALID; + } + } + } + + /* Check if any steps failed */ + if(Status != STATUS_SUCCESS) + { + /* Release object creation resources */ + ReleaseObjectCreateInformation(ObjectCreateInfo); + } + + /* Return status code */ + return Status; +} + +/** + * Captures an object name from user-mode or kernel-mode memory into a kernel buffer. + * + * @param ProcessorMode + * Supplies the processor mode used to probe user-mode structures for accessibility. + * + * @param ObjectName + * Supplies a pointer to the original Unicode string descriptor to be captured. + * + * @param CapturedObjectName + * Supplies a pointer to the Unicode string descriptor that will receive the captured buffer. + * + * @param UseLookaside + * Indicates whether a lookaside list should be used for the buffer allocation if applicable. + * + * @return Returns STATUS_SUCCESS if the name was captured safely, or an appropriate error code. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +OB::LifeCycle::CaptureObjectName(IN KPROCESSOR_MODE ProcessorMode, + IN PUNICODE_STRING ObjectName, + IN OUT PUNICODE_STRING CapturedObjectName, + IN BOOLEAN UseLookaside) +{ + UNICODE_STRING InputObjectName; + XTSTATUS Status; + ULONG Length; + PWCH Buffer; + + /* Initialize local variables */ + Buffer = NULLPTR; + Status = STATUS_SUCCESS; + + /* Initialize the captured name descriptor */ + CapturedObjectName->Buffer = NULLPTR; + CapturedObjectName->Length = 0; + CapturedObjectName->MaximumLength = 0; + + /* Enter structural exception handler */ + __try + { + /* Check if the string originates from user-mode */ + if(ProcessorMode != KernelMode) + { + /* Probe and copy the Unicode string descriptor */ + MM::Probe::ProbeForRead(ObjectName, sizeof(UNICODE_STRING), alignof(UNICODE_STRING)); + InputObjectName = *(CONST VOLATILE PUNICODE_STRING)ObjectName; + + /* Probe the string buffer for read access */ + MM::Probe::ProbeForRead(InputObjectName.Buffer, InputObjectName.Length, sizeof(WCHAR)); + } + else + { + /* Copy the descriptor */ + InputObjectName = *ObjectName; + } + + /* Check if the string is not empty */ + Length = InputObjectName.Length; + if(Length > 0) + { + /* Ensure the length is properly aligned */ + if(((Length & (sizeof(WCHAR) - 1)) != 0) || (Length == (MAXUSHORT - sizeof(WCHAR) + 1))) + { + /* Set status code */ + Status = STATUS_OBJECT_NAME_INVALID; + } + else + { + /* Allocate the name buffer */ + Buffer = AllocateObjectName(Length, UseLookaside, CapturedObjectName); + if(!Buffer) + { + /* Buffer allocation failed, set status code */ + Status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + /* Copy the string data into the buffer */ + RTL::Memory::CopyMemory(Buffer, InputObjectName.Buffer, Length); + + /* NULL-terminate the string */ + Buffer[Length / sizeof(WCHAR)] = (WCHAR)0; + } + } + } + } + __except (RTL::Exception::SystemFilter()) + { + /* Catch memory access violations */ + Status = EXCEPTION_CODE; + + /* Check if a buffer was allocated */ + if(Buffer) + { + /* Release the buffer */ + FreeObjectName(CapturedObjectName); + } + } + + /* Return status code */ + return Status; +} + +/** + * Creates a new object of the specified type and initializes it. + * + * @param ProcessorMode + * Supplies the processor mode used to probe parameters. + * + * @param ObjectType + * Supplies a pointer to the object type of the new object. + * + * @param ObjectAttributes + * Supplies an optional pointer to the object attributes structure. + * + * @param OwnerProcessorMode + * Supplies the processor mode that will own the object. + * + * @param ParseContext + * Supplies an optional pointer to a parse context structure. + * + * @param ObjectBodySize + * Supplies the size, in bytes, of the object body. + * + * @param PagedPoolCharge + * Supplies the paged pool charge for the object. If zero, default is used. + * + * @param NonPagedPoolCharge + * Supplies the non-paged pool charge for the object. If zero, default is used. + * + * @param Object + * Receives a pointer to the body of the newly created object. + * + * @return This routine returns a status code indicating the success or failure of the operation. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +OB::LifeCycle::CreateObject(IN KPROCESSOR_MODE ProcessorMode, + IN POBJECT_TYPE ObjectType, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN KPROCESSOR_MODE OwnerProcessorMode, + IN OUT PVOID ParseContext, + IN ULONG ObjectBodySize, + IN ULONG PagedPoolCharge, + IN ULONG NonPagedPoolCharge, + OUT PVOID *Object) +{ + POBJECT_CREATE_INFORMATION ObjectCreateInfo; + UNICODE_STRING CapturedObjectName; + POBJECT_HEADER ObjectHeader; + XTSTATUS Status; + + /* Allocate the buffer for creation parameters */ + ObjectCreateInfo = (POBJECT_CREATE_INFORMATION)EX::LookasideList::AllocateFromPerProcessorList(LookasideCreateInfoList); + if(!ObjectCreateInfo) + { + /* Allocation failed, return error code */ + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Capture and probe the object attributes */ + Status = CaptureObjectCreateInformation(ObjectType, ProcessorMode, OwnerProcessorMode, ObjectAttributes, + &CapturedObjectName, ObjectCreateInfo, FALSE); + if(Status != STATUS_SUCCESS) + { + /* Capture failed, clean up and return error code */ + EX::LookasideList::FreeToPerProcessorList(LookasideCreateInfoList, ObjectCreateInfo); + return Status; + } + + /* Validate requested attributes */ + if(ObjectType && ObjectType->TypeInfo.InvalidAttributes & ObjectCreateInfo->Attributes) + { + /* Release the captured creation info structure */ + FreeObjectCreateInformation(ObjectCreateInfo); + + /* Check if object name was captured */ + if(CapturedObjectName.Buffer) + { + /* Release the object name buffer */ + FreeObjectName(&CapturedObjectName); + } + + /* Return error code */ + return STATUS_INVALID_PARAMETER; + } + + /* Check if paged pool charge was specified */ + if(PagedPoolCharge == 0) + { + /* Use default paged pool charge */ + PagedPoolCharge = ObjectType->TypeInfo.DefaultPagedPoolCharge; + } + + /* Check if non-paged pool charge was specified */ + if(NonPagedPoolCharge == 0) + { + /* Use default non-paged pool charge */ + NonPagedPoolCharge = ObjectType->TypeInfo.DefaultNonPagedPoolCharge; + } + + /* Set the paged and non-paged pool charges */ + ObjectCreateInfo->PagedPoolCharge = PagedPoolCharge; + ObjectCreateInfo->NonPagedPoolCharge = NonPagedPoolCharge; + + /* Allocate the physical memory layout for the object */ + Status = AllocateObject(ObjectCreateInfo, OwnerProcessorMode, ObjectType, + &CapturedObjectName, ObjectBodySize, &ObjectHeader); + if(Status != STATUS_SUCCESS) + { + /* Allocation failed, cleanup the captured data */ + FreeObjectCreateInformation(ObjectCreateInfo); + + /* Check if object name was captured */ + if(CapturedObjectName.Buffer) + { + /* Free the captured object name */ + FreeObjectName(&CapturedObjectName); + } + + /* Return error code */ + return Status; + } + + /* Point the output pointer to the object */ + *Object = &ObjectHeader->Body; + + /* Check if the object is permanent */ + if(ObjectHeader->Flags & OBJECT_FLAG_PERMANENT) + { + /* Apply security constraints */ + if(!SE::Privileges::CheckSinglePrivilege(SE_LUID_CREATE_PERMANENT_PRIVILEGE, ProcessorMode)) + { + /* Privilege check failed, release the object and return error code */ + FreeObject(*Object); + return STATUS_PRIVILEGE_NOT_HELD; + } + } + + /* Return success */ + return STATUS_SUCCESS; +} + +/** + * Enqueues an object header into a deferred deletion queue and queues a system worker thread if the list was empty. + * + * @param ObjectHeader + * Supplies a pointer to the object header to be queued for deletion. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +OB::LifeCycle::DeferObjectDeletion(IN POBJECT_HEADER ObjectHeader) +{ + PVOID OldListHead, ActualListHead; + + /* Capture the snapshot of the removal list */ + OldListHead = RemoveObjectList; + + /* Enter the CAS loop */ + while(TRUE) + { + /* Link the current head to object header */ + ObjectHeader->NextToFree = OldListHead; + + /* Attempt to push the header to the list head */ + ActualListHead = RTL::Atomic::CompareExchangePointer(&RemoveObjectList, OldListHead, ObjectHeader); + if(ActualListHead == OldListHead) + { + /* Successfully pushed to the stack, break the loop */ + break; + } + + /* The list changed mid-flight, update the snapshot and retry */ + OldListHead = ActualListHead; + } + + /* Check if the list was empty */ + if(!OldListHead) + { + /* Queue the deletion work item to the system worker queue */ + EX::WorkItem::QueueWorkItem(&RemoveObjectWorkItem, CriticalWorkQueue); + } +} + +/** + * Destructs an object, including invoking security and type-specific deletion callbacks and freeing memory. + * + * @param Object + * Supplies a pointer to the object being destroyed. + * + * @param WorkerThread + * Indicates whether this routine is executed in the context of the system worker thread. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +OB::LifeCycle::DeleteObject(IN PVOID Object, + IN BOOLEAN WorkerThread) +{ + POBJECT_HEADER_CREATOR_INFO CreatorInfo; + POBJECT_HEADER_NAME_INFO NameInfo; + POBJECT_HEADER ObjectHeader; + POBJECT_TYPE ObjectType; + + /* Resolve object header */ + ObjectHeader = CONTAIN_RECORD(Object, OBJECT_HEADER, Body); + + /* Resolve creator information, name information pointers and object type*/ + CreatorInfo = GetObjectCreatorInformation(ObjectHeader); + NameInfo = GetObjectNameInformation(ObjectHeader); + ObjectType = ObjectHeader->Type; + + /* Check if the object has creator info and is linked in a type tracking list */ + if(CreatorInfo && !RTL::LinkedList::ListEmpty(&CreatorInfo->TypeList)) + { + /* Acquire exclusive access to the object type tracking list */ + KE::KThread::EnterCriticalRegion(); + KE::PushLock::AcquireExclusivePushLock(&ObjectType->TypeLock); + + /* Unlink the object from the creator type list */ + RTL::LinkedList::RemoveEntryList(&CreatorInfo->TypeList); + + /* Release the object type tracking list */ + KE::PushLock::ReleaseExclusivePushLock(&ObjectType->TypeLock); + KE::KThread::LeaveCriticalRegion(); + } + + /* Check if the object has an allocated name buffer */ + if(NameInfo && NameInfo->Name.Buffer) + { + /* Free the name buffer pool allocation */ + FreeObjectName(&NameInfo->Name); + + /* Clear the name information fields */ + NameInfo->Name.Buffer = NULLPTR; + NameInfo->Name.Length = 0; + NameInfo->Name.MaximumLength = 0; + } + + /* Check if a security descriptor is attached */ + if(ObjectHeader->SecurityDescriptor) + { + /* Invoke the security procedure to delete the descriptor */ + ObjectType->TypeInfo.SecurityProcedure(Object, DeleteSecurityDescriptor, NULLPTR, NULLPTR, NULLPTR, + &ObjectHeader->SecurityDescriptor, NonPagedPool, NULLPTR); + } + + /* Check if a custom deletion procedure is registered */ + if(ObjectType->TypeInfo.DeleteProcedure) + { + /* Check if execution is occurring outside of the system worker thread */ + if(!WorkerThread) + { + /* Mark the object for deferred deletion */ + ObjectHeader->Flags |= OBJECT_FLAG_DEFER_DELETE; + } + + /* Invoke the type-specific delete procedure */ + ObjectType->TypeInfo.DeleteProcedure(Object); + } + + /* Free the object memory */ + FreeObject(Object); +} + +/** + * Decrements the pointer reference count of an object. + * + * @param Object + * Supplies a pointer to the object body to dereference. + * + * @return This routine returns the updated reference count. + */ +XTFASTCALL +LONG_PTR +OB::LifeCycle::DereferenceObject(IN PVOID Object) +{ + POBJECT_HEADER ObjectHeader; + LONG_PTR NewCount; + + /* Resolve the object header */ + ObjectHeader = CONTAIN_RECORD(Object, OBJECT_HEADER, Body); + + /* Decrement the reference count */ + NewCount = RTL::Atomic::Decrement64(&ObjectHeader->PointerCount); + + /* Check for object expiration */ + if(NewCount == 0) + { + /* Check if object can be deleted */ + if(KE::RunLevel::GetCurrentRunLevel() == PASSIVE_LEVEL) + { + /* Delete the object immediately */ + DeleteObject(Object, FALSE); + } + else + { + /* Defer deletion to worker thread */ + DeferObjectDeletion(ObjectHeader); + } + } + + /* Return the updated reference count */ + return NewCount; +} + +/** + * Decrements the pointer reference count of an object by a specified amount. + * + * @param Object + * Supplies a pointer to the object body to dereference. + * + * @param Count + * Specifies a number of references to release. + * + * @return This routine returns the updated reference count. + */ +XTFASTCALL +LONG_PTR +OB::LifeCycle::DereferenceObject(IN PVOID Object, + IN ULONG Count) +{ + POBJECT_HEADER ObjectHeader; + LONG_PTR NewCount; + + /* Resolve the object header */ + ObjectHeader = CONTAIN_RECORD(Object, OBJECT_HEADER, Body); + + /* Decrement the reference count */ + NewCount = RTL::Atomic::ExchangeAdd64(&ObjectHeader->PointerCount, -(LONG_PTR)(Count)) - Count; + + /* Check for object expiration */ + if(NewCount == 0) + { + /* Defer deletion to worker thread */ + DeferObjectDeletion(ObjectHeader); + } + + /* Return the updated reference count */ + return NewCount; +} + +/** + * Decrements the reference count of an object and defers its deletion to a worker thread if the count reaches zero. + * + * @param Object + * Supplies a pointer to the object body. + * + * @return This routine returns the updated reference count. + * + * @since XT 1.0 + */ +XTFASTCALL +LONG_PTR +OB::LifeCycle::DereferenceObjectDeferDelete(IN PVOID Object) +{ + POBJECT_HEADER ObjectHeader; + LONG_PTR NewCount; + + /* Resolve the object header */ + ObjectHeader = CONTAIN_RECORD(Object, OBJECT_HEADER, Body); + + /* Decrement the reference count */ + NewCount = RTL::Atomic::Decrement64(&ObjectHeader->PointerCount); + + /* Check for object expiration */ + if(NewCount == 0) + { + /* Defer deletion to worker thread */ + DeferObjectDeletion(ObjectHeader); + } + + /* Return the updated reference count */ + return NewCount; +} + +/** + * Decrements the query reference count of a NameInfo structure and frees its resources if the count reaches zero. + * + * @param NameInfo + * Supplies a pointer to the object header name information structure. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTFASTCALL +VOID +OB::LifeCycle::DereferenceObjectNameInformation(IN POBJECT_HEADER_NAME_INFO NameInfo) +{ + POBJECT_DIRECTORY Directory; + + /* Ensure the name info pointer is valid */ + if(!NameInfo) + { + /* Name info pointer is invalid, return */ + return; + } + + /* Decrement the query reference count */ + if(RTL::Atomic::Decrement32((PLONG)&NameInfo->QueryReferences) != 0) + { + /* References are still active, return */ + return; + } + + /* Check if the name buffer is allocated */ + if(NameInfo->Name.Buffer) + { + /* Free the name buffer */ + FreeObjectName(&NameInfo->Name); + + /* Reset the name string fields */ + NameInfo->Name.Buffer = NULLPTR; + NameInfo->Name.Length = 0; + NameInfo->Name.MaximumLength = 0; + } + + /* Extract the directory pointer and check if it is valid */ + Directory = NameInfo->Directory; + if(Directory != NULLPTR) + { + /* Clear the directory reference */ + NameInfo->Directory = NULLPTR; + + /* Defer the deletion of the directory object */ + DereferenceObjectDeferDelete(Directory); + } +} + +/** + * Frees the memory allocated for an object and its associated optional headers. + * + * @param Object + * Supplies a pointer to the object body to be freed. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +OB::LifeCycle::FreeObject(IN PVOID Object) +{ + POBJECT_HEADER_HANDLE_INFO HandleInfo; + POBJECT_HEADER_NAME_INFO NameInfo; + POBJECT_HEADER ObjectHeader; + POBJECT_TYPE ObjectType; + PVOID AllocationBase; + + /* Resolve the object header and object type */ + ObjectHeader = CONTAIN_RECORD(Object, OBJECT_HEADER, Body); + ObjectType = ObjectHeader->Type; + + /* Calculate the base of the pool allocation */ + AllocationBase = GetObjectAllocationBase(ObjectHeader); + + /* Check if the object type exists */ + if(ObjectType) + { + /* Decrement the count of active objects for this type */ + RTL::Atomic::Decrement32((VOLATILE PLONG)&ObjectType->TotalNumberOfObjects); + } + + /* Check if the object is not fully initialized */ + if(ObjectHeader->Flags & OBJECT_FLAG_NEW_OBJECT) + { + /* Check if creation information exists */ + if(ObjectHeader->ObjectCreateInfo) + { + /* Free the captured creation parameters */ + FreeObjectCreateInformation((ObjectHeader->ObjectCreateInfo)); + + /* Nullify the pointer */ + ObjectHeader->ObjectCreateInfo = NULLPTR; + } + } + else + { + /* Fully initialized object, return quotas */ + ReturnObjectQuota(ObjectHeader, ObjectType); + } + + /* Locate handle information */ + HandleInfo = (POBJECT_HEADER_HANDLE_INFO)(!ObjectHeader->HandleInfoOffset ? NULLPTR : (PCHAR)ObjectHeader - + ObjectHeader->HandleInfoOffset); + if(HandleInfo && !(ObjectHeader->Flags & OBJECT_FLAG_SINGLE_PROCESS)) + { + /* Free the handle */ + MM::Allocator::FreePool(HandleInfo->HandleCountDatabase); + } + + /* Locate name information */ + NameInfo = (POBJECT_HEADER_NAME_INFO)(!ObjectHeader->NameInfoOffset ? NULLPTR : (PCHAR)ObjectHeader - + ObjectHeader->NameInfoOffset); + if(NameInfo && NameInfo->Name.Buffer) + { + /* Free the object name buffer */ + FreeObjectName(&NameInfo->Name); + } + + /* Mark object type and free memory */ + ObjectHeader->Type = (POBJECT_TYPE)(ULONG_PTR)0xBAD0B0B0; + MM::Allocator::FreePool(AllocationBase, (ObjectType == NULLPTR ? TAG_OB_OBJECT_TYPE : ObjectType->Key)); +} + +/** + * Releases creation information resources and returns them back to the lookaside list. + * + * @param ObjectCreateInfo + * Supplies a pointer to the object creation information structure to be freed. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTFASTCALL +VOID +OB::LifeCycle::FreeObjectCreateInformation(IN POBJECT_CREATE_INFORMATION CreateInfo) +{ + /* Release the object creation information */ + ReleaseObjectCreateInformation(CreateInfo); + + /* Free the attributes from the lookaside list */ + EX::LookasideList::FreeToPerProcessorList(LookasideCreateInfoList, CreateInfo); +} + + +/** + * Frees an object name buffer either to the general pool or a dedicated lookaside list. + * + * @param ObjectName + * Supplies a pointer to the Unicode string whose buffer is being released. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTFASTCALL +VOID +OB::LifeCycle::FreeObjectName(IN OUT PUNICODE_STRING ObjectName) +{ + /* Check if the buffer size differs from the lookaside list entry size */ + if(ObjectName->MaximumLength != OBJECT_NAME_BUFFER_SIZE) + { + /* Size mismatch, free the allocation to the system pool */ + MM::Allocator::FreePool(ObjectName->Buffer); + } + else + { + /* Size matches, return the buffer to the lookaside list */ + EX::LookasideList::FreeToPerProcessorList(LookasideNameBufferList, ObjectName->Buffer); + } +} + +/** + * Resolves the base address of the pool allocation for a given object. + * + * @param ObjectHeader + * Supplies a pointer to the object header. + * + * @return This routine returns the pointer to the start of the memory block. + * + * @since XT 1.0 + */ +XTAPI +PVOID +OB::LifeCycle::GetObjectAllocationBase(IN POBJECT_HEADER ObjectHeader) +{ + /* Check if the Quota Information header is present */ + if(ObjectHeader->QuotaInfoOffset) + { + /* Return the base address including the Quota Information header */ + return (PUCHAR)ObjectHeader - ObjectHeader->QuotaInfoOffset; + } + + /* Check if the Handle Information header is present */ + if(ObjectHeader->HandleInfoOffset) + { + /* Return the base address including the Handle Information header */ + return (PUCHAR)ObjectHeader - ObjectHeader->HandleInfoOffset; + } + + /* Check if the Name Information header is present */ + if(ObjectHeader->NameInfoOffset) + { + /* Return the base address including the Name Information header */ + return (PUCHAR)ObjectHeader - ObjectHeader->NameInfoOffset; + } + + /* Check if the Creator Information header is present */ + if(ObjectHeader->Flags & OBJECT_FLAG_CREATOR_INFO) + { + /* Return the base address including the Creator Information header */ + return (PUCHAR)ObjectHeader - sizeof(OBJECT_HEADER_CREATOR_INFO); + } + + /* No optional headers exist, return the header address */ + return ObjectHeader; +} + +/** + * Retrieves the optional creator information header for a given object. + * + * @param Header + * Supplies a pointer to the object's base header. + * + * @return This routine returns a pointer to the creator information, or NULLPTR if not present. + * + * @since XT 1.0 + */ +XTFASTCALL +POBJECT_HEADER_CREATOR_INFO +OB::LifeCycle::GetObjectCreatorInformation(IN POBJECT_HEADER Header) +{ + /* Verify if the creator flag is set */ + if((Header->Flags & OBJECT_FLAG_CREATOR_INFO) == 0) + { + /* Creator information not present, return NULL pounter */ + return NULLPTR; + } + + /* Return the creator information */ + return (POBJECT_HEADER_CREATOR_INFO)((PCHAR)Header - sizeof(OBJECT_HEADER_CREATOR_INFO)); +} + +/** + * Retrieves the optional name information header for a given object. + * + * @param Header + * Supplies a pointer to the object's base header. + * + * @return This routine returns a pointer to the name information, or NULLPTR if not present. + * + * @since XT 1.0 + */ +XTFASTCALL +POBJECT_HEADER_NAME_INFO +OB::LifeCycle::GetObjectNameInformation(IN POBJECT_HEADER Header) +{ + /* Check if the header is present */ + if(!Header->NameInfoOffset) + { + /* Name information not present, return NULL pointer */ + return NULLPTR; + } + + /* Return the name information */ + return (POBJECT_HEADER_NAME_INFO)((PCHAR)Header - Header->NameInfoOffset); +} + +/** + * Initializes the Object lifecycle subsystem. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +OB::LifeCycle::InitializeObjectLifeCycle(VOID) +{ + /* Clear tracking pointer */ + RemoveObjectList = NULLPTR; + + /* Initialize deferred removal work item */ + EX::WorkItem::InitializeWorkItem(&RemoveObjectWorkItem, ProcessDeferredDeletionQueue, NULLPTR); +} + +/** + * Processes the deferred object removal queue. + * + * @param Parameter + * Supplies an optional context parameter. + * + * @return This routine does not return a value. + * + * @since XT 1.0 + */ +XTAPI +VOID +OB::LifeCycle::ProcessDeferredDeletionQueue(IN PVOID Parameter) +{ + POBJECT_HEADER ObjectHeader; + POBJECT_HEADER NextObject; + PVOID OldValue; + + /* Process the deferred removal queue */ + while(TRUE) + { + /* Exchange the queue pointer */ + ObjectHeader = (POBJECT_HEADER)RTL::Atomic::ExchangePointer(&RemoveObjectList, OBJECT_REMOVE_QUEUE_LOCKED); + + /* Traverse and process the detached list */ + while(ObjectHeader && ObjectHeader != (POBJECT_HEADER)OBJECT_REMOVE_QUEUE_LOCKED) + { + /* Capture the pointer to the next object */ + NextObject = (POBJECT_HEADER)ObjectHeader->NextToFree; + + /* Destroy object */ + DeleteObject(&ObjectHeader->Body, TRUE); + + /* Advance to the next object */ + ObjectHeader = NextObject; + } + + /* Check if the queue is still locked */ + if(RemoveObjectList == OBJECT_REMOVE_QUEUE_LOCKED) + { + /* Attempt to clear the lock */ + OldValue = RTL::Atomic::CompareExchangePointer(&RemoveObjectList, OBJECT_REMOVE_QUEUE_LOCKED, NULLPTR); + if(OldValue == OBJECT_REMOVE_QUEUE_LOCKED) + { + /* Empty list, exit the worker */ + break; + } + } + } +} + +/** + * Increments the pointer reference count of an object. + * + * @param Object + * Supplies a pointer to the object body to reference. + * + * @return This routine returns the updated reference count. + */ +XTFASTCALL +LONG_PTR +OB::LifeCycle::ReferenceObject(IN PVOID Object) +{ + POBJECT_HEADER ObjectHeader; + + /* Resolve the object header */ + ObjectHeader = CONTAIN_RECORD(Object, OBJECT_HEADER, Body); + + /* Increment the reference count */ + return RTL::Atomic::Increment64(&ObjectHeader->PointerCount); +} + +/** + * Increments the pointer reference count of an object by a specified amount. + * + * @param Object + * Supplies a pointer to the object body to reference. + * + * @param Count + * Specifies a number of references to acquire. + * + * @return This routine returns the updated reference count. + */ +XTFASTCALL +LONG_PTR +OB::LifeCycle::ReferenceObject(IN PVOID Object, + IN ULONG Count) +{ + POBJECT_HEADER ObjectHeader; + + /* Resolve the object header */ + ObjectHeader = CONTAIN_RECORD(Object, OBJECT_HEADER, Body); + + /* Increment the reference count */ + return RTL::Atomic::ExchangeAdd64(&ObjectHeader->PointerCount, Count) + Count; +} + +/** + * Increments the query reference count of a NameInfo structure. + * + * @param ObjectHeader + * Supplies a pointer to the object header associated with the NameInfo. + * + * @return This routine returns a pointer to the referenced NameInfo, or NULLPTR if the reference could not be acquired. + * + * @since XT 1.0 + */ +XTFASTCALL +POBJECT_HEADER_NAME_INFO +OB::LifeCycle::ReferenceObjectNameInformation(IN POBJECT_HEADER ObjectHeader) +{ + LONG OldReferences, NewReferences, ActualReferences; + POBJECT_HEADER_NAME_INFO NameInfo; + + /* Resolve the name information structure from the object header */ + NameInfo = GetObjectNameInformation(ObjectHeader); + + /* Ensure the name info structure is present */ + if(!NameInfo) + { + /* Return NULL pointer */ + return NULLPTR; + } + + /* Initialize the old reference count snapshot */ + OldReferences = *(volatile LONG*)&NameInfo->QueryReferences; + + /* Enter the CAS loop */ + while(TRUE) + { + /* Check if the reference count has dropped to zero */ + if((OldReferences & ~OBJECT_NAME_LOCKED) == 0) + { + /* Abort and return NULL pointer */ + return NULLPTR; + } + + /* Calculate the incremented reference count */ + NewReferences = OldReferences + 1; + + /* Attempt to commit the incremented count */ + ActualReferences = RTL::Atomic::CompareExchange32((PLONG)&NameInfo->QueryReferences, OldReferences, NewReferences); + if(ActualReferences == OldReferences) + { + /* Break the loop */ + break; + } + + /* State changed, update the snapshot and retry */ + OldReferences = ActualReferences; + } + + /* Check if the name is currently locked */ + if(OldReferences & OBJECT_NAME_LOCKED) + { + /* Synchronize with the directory lock */ + KE::PushLock::WaitOnPushLock(&NameInfo->Directory->Lock); + } + + /* Return the referenced name info */ + return NameInfo; +} + +/** + * Releases security resources associated with an object creation information structure. + * + * @param CreateInfo + * Supplies a pointer to the object creation information structure to be released. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTFASTCALL +VOID +OB::LifeCycle::ReleaseObjectCreateInformation(IN POBJECT_CREATE_INFORMATION CreateInfo) +{ + /* Verify if a security descriptor was captured and is actively attached */ + if(CreateInfo->SecurityDescriptor) + { + /* Release the descriptor */ + SE::Descriptor::ReleaseSecurityDescriptor(CreateInfo->SecurityDescriptor, CreateInfo->ProbeMode, TRUE); + + /* Nullify the pointer */ + CreateInfo->SecurityDescriptor = NULLPTR; + } +} + +/** + * Processes and returns pool quotas associated with an object's execution block. + * + * @param ObjectHeader + * Supplies a pointer to the object header. + * + * @param ObjectType + * Supplies a pointer to the object type definition. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +OB::LifeCycle::ReturnObjectQuota(IN POBJECT_HEADER Header, + IN POBJECT_TYPE ObjectType) +{ + POBJECT_HEADER_QUOTA_INFO Quota; + ULONG NonPagedPoolCharge; + ULONG PagedPoolCharge; + + /* Verify if a quota block is charged for the object */ + if(!Header->QuotaBlockCharged) + { + /* No quota charged, nothing to do */ + return; + } + + /* Retrieve the quota information header */ + Quota = (POBJECT_HEADER_QUOTA_INFO)(Header->QuotaInfoOffset == 0 ? NULLPTR : ((PCHAR)Header - Header->QuotaInfoOffset)); + if(Quota) + { + /* Extract the pool charges */ + NonPagedPoolCharge = Quota->NonPagedPoolCharge; + PagedPoolCharge = Quota->PagedPoolCharge + Quota->SecurityDescriptorCharge; + } + else + { + /* No quota header present, use default charges */ + NonPagedPoolCharge = ObjectType->TypeInfo.DefaultNonPagedPoolCharge; + PagedPoolCharge = ObjectType->TypeInfo.DefaultPagedPoolCharge; + + /* Check if the object holds a default security descriptor quota */ + if(Header->Flags & OBJECT_FLAG_SECURITY_QUOTA) + { + /* Add the default security quota to the paged pool charge */ + PagedPoolCharge += SE_DEFAULT_SECURITY_QUOTA; + } + } + + /* Return the calculated pool charges */ + PS::Quota::ReturnSharedPoolQuota((PEPROCESS_QUOTA_BLOCK)Header->QuotaBlockCharged, + PagedPoolCharge, NonPagedPoolCharge); + + /* Nullify the quota block pointer */ + Header->QuotaBlockCharged = NULLPTR; +} diff --git a/xtoskrnl/ob/obdir.cc b/xtoskrnl/ob/obdir.cc new file mode 100644 index 0000000..3881fa7 --- /dev/null +++ b/xtoskrnl/ob/obdir.cc @@ -0,0 +1,582 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/ob/obdir.cc + * DESCRIPTION: Object Manager Directory + * DEVELOPERS: Aiken Harris + */ + +#include + + +/** + * Acquires an exclusive pushlock for the specified object directory. + * + * @param Directory + * Supplies a pointer to the object directory being locked. + * + * @param LookupContext + * Supplies a pointer to the lookup context used to track the lock state. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTFASTCALL +VOID +OB::Directory::AcquireExclusiveDirectoryLock(IN OUT POBJECT_DIRECTORY Directory, + IN OUT POBJECT_LOOKUP_CONTEXT LookupContext) +{ + /* Set the initial lock state */ + LookupContext->LockStateSignature = OBJECT_LOCK_STATE_WAIT_EXCLUSIVE; + + /* Disable kernel APCs */ + KE::KThread::EnterCriticalRegion(); + + /* Acquire the directory lock for exclusive access */ + KE::PushLock::AcquireExclusivePushLock(&Directory->Lock); + + /* Update the lock state */ + LookupContext->LockStateSignature = OBJECT_LOCK_STATE_OWNED_EXCLUSIVE; +} + +/** + * Acquires a shared pushlock for the specified object directory. + * + * @param Directory + * Supplies a pointer to the object directory being locked. + * + * @param LookupContext + * Supplies a pointer to the lookup context used to track the lock state. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTFASTCALL +VOID +OB::Directory::AcquireSharedDirectoryLock(IN POBJECT_DIRECTORY Directory, + IN POBJECT_LOOKUP_CONTEXT LookupContext) +{ + /* Set the initial lock state */ + LookupContext->LockStateSignature = OBJECT_LOCK_WAITSHARED_SIGNATURE; + + /* Disable kernel APCs */ + KE::KThread::EnterCriticalRegion(); + + /* Acquire the directory lock for shared access */ + KE::PushLock::AcquireSharedPushLock(&Directory->Lock); + + /* Update the lock state */ + LookupContext->LockStateSignature = OBJECT_LOCK_OWNEDSHARED_SIGNATURE; +} + +/** + * Computes the case-insensitive hash index for an object name. + * + * @param Name + * Supplies a pointer to the unicode string containing the object name. + * + * @return This routine returns the computed hash index. + * + * @since XT 1.0 + */ +XTFASTCALL +ULONG +OB::Directory::ComputeObjectNameHash(IN PCUNICODE_STRING Name) +{ + ULONG CharacterLength, HashIndex; + WCHAR Character; + PWCH Buffer; + + /* Initialize the buffer pointer, character count, and starting hash value */ + Buffer = Name->Buffer; + CharacterLength = Name->Length / sizeof(WCHAR); + HashIndex = 0; + + /* Iterate through each wide character */ + while(CharacterLength--) + { + /* Fetch the current character and advance the buffer pointer */ + Character = *Buffer++; + + /* Apply the NT string hashing polynomial */ + HashIndex += (HashIndex << 1) + (HashIndex >> 1); + + /* Check if the character is uppercase, numeric, or a symbol */ + if(Character < L'a') + { + /* Add the unmodified character */ + HashIndex += Character; + } + else if(Character > L'z') + { + /* Convert extended Unicode characters to uppercase */ + HashIndex += RTL::Nls::ToUpperUnicodeCharacter(Character); + } + else + { + /* Perform ASCII conversion from lowercase to uppercase */ + HashIndex += (Character - (L'a' - L'A')); + } + } + + /* Return the final hash value */ + return HashIndex % OBJECT_NUMBER_HASH_BUCKETS; +} + +/** + * Removes an object directory entry from its hash bucket and frees its memory. + * + * @param LookupContext + * Supplies a pointer to the lookup context containing the directory and hash index. + * + * @return This routine returns TRUE if the entry was successfully unlinked and freed, or FALSE otherwise. + * + * @since XT 1.0 + */ +XTAPI +BOOLEAN +OB::Directory::DeleteDirectoryEntry(IN POBJECT_LOOKUP_CONTEXT LookupContext) +{ + POBJECT_DIRECTORY_ENTRY DirectoryEntry, *HashBucket; + POBJECT_DIRECTORY Directory; + + /* Extract the directory from the lookup context */ + Directory = LookupContext->Directory; + + /* Verify that the directory pointer is valid */ + if(Directory) + { + /* Resolve the head of the collision chain */ + HashBucket = (POBJECT_DIRECTORY_ENTRY*)&Directory->HashBuckets[LookupContext->HashIndex]; + + /* Retrieve the entry at the head of the chain */ + DirectoryEntry = *HashBucket; + + /* Ensure there isan entry to delete */ + if(DirectoryEntry) + { + /* Unlink the entry from the hash bucket chain */ + *HashBucket = DirectoryEntry->ChainLink; + DirectoryEntry->ChainLink = NULLPTR; + + /* Free the memory */ + MM::Allocator::FreePool(DirectoryEntry); + + /* Return TRUE */ + return TRUE; + } + } + + /* No entry found, return FALSE */ + return FALSE; +} + +/** + * Initializes the Object Manager lookup context. + * + * @param LookupContext + * Supplies a pointer to the caller-allocated lookup context to initialize. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTFASTCALL +VOID +OB::Directory::InitializeLookupContext(OUT POBJECT_LOOKUP_CONTEXT LookupContext) +{ + /* Initialize the lookup context */ + LookupContext->Directory = NULLPTR; + LookupContext->DirectoryLocked = FALSE; + LookupContext->LockStateSignature = OBJECT_LOCK_STATE_INITIALIZED; + LookupContext->Object = NULLPTR; +} + +/** + * Inserts a newly created object into an object directory. + * + * @param Directory + * Pointer to the object directory where the object will be inserted. + * + * @param LookupContext + * Pointer to the lookup context containing the precomputed hash index. + * + * @param ObjectHeader + * Pointer to the object header of the object being inserted. + * + * @return TRUE if the insertion was successful, FALSE if pool allocation failed. + * + * @since XT 1.0 + */ +XTAPI +BOOLEAN +OB::Directory::InsertDirectoryEntry(IN OUT POBJECT_DIRECTORY Directory, + IN POBJECT_LOOKUP_CONTEXT LookupContext, + IN OUT POBJECT_HEADER ObjectHeader) +{ + POBJECT_DIRECTORY_ENTRY *HashBucket, NewEntry; + POBJECT_HEADER_NAME_INFO NameInfo; + XTSTATUS Status; + + /* Extract the name information header */ + NameInfo = OB::LifeCycle::GetObjectNameInformation(ObjectHeader); + + /* Allocate the directory entry */ + Status = MM::Allocator::AllocatePool(PagedPool, sizeof(OBJECT_DIRECTORY_ENTRY), + (PVOID *)&NewEntry, TAG_OB_DIRECTORY); + if(Status != STATUS_SUCCESS || !NewEntry) + { + /* Allocation failed, return FALSE */ + return FALSE; + } + + /* Resolve the hash bucket head */ + HashBucket = (POBJECT_DIRECTORY_ENTRY*)&Directory->HashBuckets[LookupContext->HashIndex]; + + /* Insert the entry at the head of the collision chain */ + NewEntry->ChainLink = *HashBucket; + *HashBucket = NewEntry; + + /* Link the entry to the object body and establish the parent directory */ + NewEntry->Object = &ObjectHeader->Body; + NameInfo->Directory = Directory; + + /* Return TRUE */ + return TRUE; +} + +/** + * Acquires an exclusive lock on the specified object directory. + * + * @param Directory + * Supplies a pointer to the object directory to lock. + * + * @param LookupContext + * Supplies a pointer to the active lookup context used to track the lock state. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTFASTCALL +VOID +OB::Directory::LockLookupContext(IN POBJECT_DIRECTORY Directory, + IN POBJECT_LOOKUP_CONTEXT LookupContext) +{ + /* Acquire exclusive access to the directory */ + AcquireExclusiveDirectoryLock(Directory, LookupContext); + + /* Cache the directory pointer and lock ownership */ + LookupContext->Directory = Directory; + LookupContext->DirectoryLocked = TRUE; +} + +/** + * Looks up an object directory entry by name within the namespace. + * + * @param Directory + * Supplies a pointer to the primary object directory to search. + * + * @param Name + * Supplies a pointer to the name of the object to locate. + * + * @param Attributes + * Supplies the object attributes bitmask. + * + * @param SearchGlobalDirectory + * Specifies whether to fallback into the device map's global shadow directory if the object is not found + * in the primary directory. + * + * @param LookupContext + * Supplies a pointer to the tracking context. + * + * @return This routine returns a pointer to the referenced object body, or NULLPTR if resolution fails. + * + * @since XT 1.0 + */ +XTAPI +PVOID +OB::Directory::LookupDirectoryEntry(IN POBJECT_DIRECTORY Directory, + IN PUNICODE_STRING Name, + IN ULONG Attributes, + IN BOOLEAN SearchGlobalDirectory, + OUT POBJECT_LOOKUP_CONTEXT LookupContext) +{ + POBJECT_HEADER_NAME_INFO PreviousNameInfo; + POBJECT_DIRECTORY CurrentDirectory; + BOOLEAN CaseInsensitive; + PVOID FoundObject; + ULONG HashIndex; + + /* Initialize the default return value */ + FoundObject = NULLPTR; + + /* Check if global device map traversal is available */ + if(!OB::DeviceMap::GetUniqueDeviceMaps()) + { + /* Override caller's request to prevent shadow directory traversal */ + SearchGlobalDirectory = FALSE; + } + + /* Guard against invalid parameter combination */ + if(Directory != NULLPTR && Name != NULLPTR && Name->Length != 0 && Name->Buffer != NULLPTR) + { + /* Determine if the string comparison should ignore case */ + CaseInsensitive = (Attributes & OBJECT_CASE_INSENSITIVE) ? TRUE : FALSE; + + /* Precompute the hash index */ + HashIndex = ComputeObjectNameHash(Name); + + /* Initialize the traversal pointer */ + CurrentDirectory = Directory; + + /* Cache the computed hash index in the lookup context */ + LookupContext->HashIndex = (USHORT)HashIndex; + + /* Iterate through the directory and linked shadow boundaries */ + while(CurrentDirectory) + { + /* Check if the directory is locked */ + if(!LookupContext->DirectoryLocked) + { + /* Acquire the directory lock */ + AcquireSharedDirectoryLock(CurrentDirectory, LookupContext); + } + + /* Traverse the hash bucket chain to find a matching entry */ + FoundObject = SearchDirectory(CurrentDirectory, Name, CaseInsensitive, HashIndex, LookupContext); + if(FoundObject) + { + /* Match found, secure the object and its name info */ + OB::LifeCycle::ReferenceObjectNameInformation(CONTAIN_RECORD(FoundObject, OBJECT_HEADER, Body)); + OB::LifeCycle::ReferenceObject(FoundObject); + + /* Check if the directory is locked */ + if(!LookupContext->DirectoryLocked) + { + /* Drop the lock and restore standard kernel APC delivery */ + OB::Directory::ReleaseDirectoryLock(CurrentDirectory, LookupContext); + } + + /* Target resolved, break the loop */ + break; + } + + /* Check if the directory is locked */ + if(!LookupContext->DirectoryLocked) + { + /* Drop the lock and restore standard kernel APC delivery */ + OB::Directory::ReleaseDirectoryLock(CurrentDirectory, LookupContext); + } + + /* Check if fallback to the global shadow directory is possible */ + if(SearchGlobalDirectory && CurrentDirectory->DeviceMap != NULLPTR) + { + /* Resolve the shadow global devices directory in next iteration */ + CurrentDirectory = OB::DeviceMap::GetGlobalDevicesDirectory(CurrentDirectory); + } + else + { + /* Terminate the traversal loop */ + CurrentDirectory = NULLPTR; + } + } + } + + /* Check if the lookup context contains a previously resolved object */ + if(LookupContext->Object != NULLPTR) + { + /* Resolve the old object's name information header */ + PreviousNameInfo = OB::LifeCycle::GetObjectNameInformation(CONTAIN_RECORD(LookupContext->Object, + OBJECT_HEADER, + Body)); + + /* Release the references */ + OB::LifeCycle::DereferenceObjectNameInformation(PreviousNameInfo); + OB::LifeCycle::DereferenceObject(LookupContext->Object); + } + + /* Store the newly found object payload inside the caller's lookup context */ + LookupContext->Object = FoundObject; + + /* Return the object body */ + return FoundObject; +} + +/** + * Releases a previously acquired object directory lock. + * + * @param Directory + * Supplies a pointer to the object directory to unlock. + * + * @param LookupContext + * Supplies a pointer to the lookup context for state tracking. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTFASTCALL +VOID +OB::Directory::ReleaseDirectoryLock(IN OUT POBJECT_DIRECTORY Directory, + IN OUT POBJECT_LOOKUP_CONTEXT LookupContext) +{ + /* Release the directory lock */ + KE::PushLock::ReleasePushLock(&Directory->Lock); + + /* Update the lock state */ + LookupContext->LockStateSignature = OBJECT_LOCK_STATE_RELEASED_SIGNATURE; + + /* Restore APC delivery */ + KE::KThread::LeaveCriticalRegion(); +} + +/** + * Releases all resources held within an Object Manager lookup context. + * + * @param LookupContext + * Supplies a pointer to the active lookup context to be released. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTFASTCALL +VOID +OB::Directory::ReleaseLookupContext(IN OUT POBJECT_LOOKUP_CONTEXT LookupContext) +{ + /* Verify if a directory lock is held */ + if(LookupContext->DirectoryLocked) + { + /* Release the directory lock */ + ReleaseDirectoryLock(LookupContext->Directory, LookupContext); + + /* Update lookup context state */ + LookupContext->Directory = NULLPTR; + LookupContext->DirectoryLocked = FALSE; + } + + /* Release the object references held within a lookup context */ + ReleaseLookupContextObject(LookupContext); +} + +/** + * Releases the object reference held within a lookup context. + * + * @param LookupContext + * Supplies a pointer to the active lookup context containing the object to be released. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTFASTCALL +VOID +OB::Directory::ReleaseLookupContextObject(IN OUT POBJECT_LOOKUP_CONTEXT LookupContext) +{ + POBJECT_HEADER_NAME_INFO NameInfo; + POBJECT_HEADER ObjectHeader; + PVOID Object; + + /* Extract the cached object pointer from the lookup context */ + Object = LookupContext->Object; + if(Object) + { + /* Resolve the object header */ + ObjectHeader = CONTAIN_RECORD(Object, OBJECT_HEADER, Body); + + /* Retrieve the optional name information header */ + NameInfo = OB::LifeCycle::GetObjectNameInformation(ObjectHeader); + + /* Verify if the object has a registered name */ + if(NameInfo) + { + /* Drop the name query reference */ + OB::LifeCycle::DereferenceObjectNameInformation(NameInfo); + } + + /* Release the pointer reference */ + OB::LifeCycle::DereferenceObject(Object); + + /* Purge the pointer */ + LookupContext->Object = NULLPTR; + } +} + +/** + * Traverses an object directory hash bucket to find a matching entry. + * + * @param Directory + * Supplies a pointer to the object directory being searched. + * + * @param Name + * Supplies ap ointer to the target object name. + * + * @param CaseInsensitive + * Specifies whether the string comparison should ignore case. + * + * @param HashIndex + * Supplies the pre-computed hash bucket index used to locate the collision chain. + * + * @param LookupContext + * Supplies a pointer to the caller's lookup context. + * + * @return This routine returns a pointer to the resolved object body if found, or NULLPTR otherwise. + * + * @since XT 1.0 + */ +XTFASTCALL +PVOID +OB::Directory::SearchDirectory(IN POBJECT_DIRECTORY Directory, + IN PCUNICODE_STRING Name, + IN BOOLEAN CaseInsensitive, + IN ULONG HashIndex, + IN POBJECT_LOOKUP_CONTEXT LookupContext) +{ + POBJECT_DIRECTORY_ENTRY DirectoryEntry, *HeadDirectoryEntry, *LookupBucket; + POBJECT_HEADER_NAME_INFO NameInfo; + POBJECT_HEADER ObjectHeader; + + /* Resolve the head pointer of the target hash bucket chain */ + LookupBucket = (POBJECT_DIRECTORY_ENTRY*)&Directory->HashBuckets[HashIndex]; + + /* Iterate through the collision chain */ + HeadDirectoryEntry = LookupBucket; + while((DirectoryEntry = *HeadDirectoryEntry) != NULLPTR) + { + /* Resolve the underlying object header */ + ObjectHeader = CONTAIN_RECORD(DirectoryEntry->Object, OBJECT_HEADER, Body); + + /* Retrieve the optional name information header */ + NameInfo = OB::LifeCycle::GetObjectNameInformation(ObjectHeader); + + /* Check if the names match */ + if((Name->Length == NameInfo->Name.Length) && + RTL::Unicode::CompareString(Name, &NameInfo->Name, CaseInsensitive) == 0) + { + /* Check if it is already at the head of the bucket */ + if(HeadDirectoryEntry != LookupBucket) + { + /* Check if the directory is locked */ + if(LookupContext->DirectoryLocked || KE::PushLock::ConvertSharedPushLockToExclusive(&Directory->Lock)) + { + /* Relocate the entry to the front */ + *HeadDirectoryEntry = DirectoryEntry->ChainLink; + DirectoryEntry->ChainLink = *LookupBucket; + *LookupBucket = DirectoryEntry; + } + } + + /* Return the pointer to the object body */ + return DirectoryEntry->Object; + } + + /* Advance to the next node entry */ + HeadDirectoryEntry = &DirectoryEntry->ChainLink; + } + + /* Return NULL pointer*/ + return NULLPTR; +} diff --git a/xtoskrnl/ob/obmgr.cc b/xtoskrnl/ob/obmgr.cc new file mode 100644 index 0000000..0bb238e --- /dev/null +++ b/xtoskrnl/ob/obmgr.cc @@ -0,0 +1,120 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/ob/obmgr.cc + * DESCRIPTION: Object Manager + * DEVELOPERS: Aiken Harris + */ + +#include + + +/** + * Initializes the Object Manager subsystem. + * + * @return This routine returns a status code indicating the success or failure of the operation. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +OB::Manager::InitializeObjectManager(VOID) +{ + XTSTATUS Status; + + /* Zero out the static global lookaside structures */ + RTL::Memory::ZeroMemory(&CreateInfoList, sizeof(GENERAL_LOOKASIDE)); + RTL::Memory::ZeroMemory(&NameBufferList, sizeof(GENERAL_LOOKASIDE)); + + /* Initialize the system-wide global lookaside list for object create information structures */ + EX::LookasideList::InitializeLookasideList(&CreateInfoList, NonPagedPool, + sizeof(OBJECT_CREATE_INFORMATION), TAG_OB_CREATE_INFO, + 64, EX::LookasideList::GetSystemLookasideListHead()); + + /* Initialize the system-wide global lookaside list for object name buffers */ + EX::LookasideList::InitializeLookasideList(&NameBufferList, PagedPool, + OBJECT_NAME_BUFFER_SIZE, TAG_OB_NAME, + 32, EX::LookasideList::GetSystemLookasideListHead()); + + /* Initialize system lookaside list for BSP */ + InitializeSystemLookasideList(); + + /* Initialize device map support */ + OB::DeviceMap::InitializeDeviceMap(); + + OB::LifeCycle::InitializeObjectLifeCycle(); + + /* Initialize object type registry */ + Status = OB::TypeRegistry::InitializeObjectTypeRegistry(); + if(Status != STATUS_SUCCESS) + { + /* Initialization failed, return error code */ + return Status; + } + + /* Return success */ + return STATUS_SUCCESS; +} + +/** + * Initializes the per-processor lookaside lists for the Object Manager. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +OB::Manager::InitializeSystemLookasideList(VOID) +{ + PKPROCESSOR_CONTROL_BLOCK Prcb; + PGENERAL_LOOKASIDE LookasideList; + XTSTATUS Status; + + /* Retrieve the Processor Control Block */ + Prcb = KE::Processor::GetCurrentProcessorControlBlock(); + + /* Allocate a contiguous memory block for lookaside lists */ + Status = MM::Allocator::AllocatePool(NonPagedPool, sizeof(GENERAL_LOOKASIDE) * 2, + (PVOID*)&LookasideList, TAG_OB_CREATE_INFO); + + /* Wire the global pointer for create information structures */ + Prcb->LookasideList[LookasideCreateInfoList].Global = &CreateInfoList; + + /* Check if the contiguous allocation was successful */ + if(Status == STATUS_SUCCESS && LookasideList) + { + /* Map the pointer to the first half of the contiguous block */ + Prcb->LookasideList[LookasideCreateInfoList].Local = &LookasideList[0]; + + /* Initialize the local list */ + EX::LookasideList::InitializeLookasideList(&LookasideList[0], NonPagedPool, + sizeof(OBJECT_CREATE_INFORMATION), TAG_OB_CREATE_INFO, + 64, EX::LookasideList::GetSystemLookasideListHead()); + } + else + { + /* Allocation failed, use shared global list */ + Prcb->LookasideList[LookasideCreateInfoList].Local = &CreateInfoList; + } + + /* Wire the global pointer for object name buffers */ + Prcb->LookasideList[LookasideNameBufferList].Global = &NameBufferList; + + /* Check if the contiguous allocation was successful */ + if(Status == STATUS_SUCCESS && LookasideList) + { + /* Map the pointer to the second half of the contiguous block */ + Prcb->LookasideList[LookasideNameBufferList].Local = &LookasideList[1]; + + /* Initialize the local list */ + EX::LookasideList::InitializeLookasideList(&LookasideList[1], PagedPool, + OBJECT_NAME_BUFFER_SIZE, TAG_OB_NAME, + 32, EX::LookasideList::GetSystemLookasideListHead()); + } + else + { + /* Allocation failed, use shared global list */ + Prcb->LookasideList[LookasideNameBufferList].Local = &NameBufferList; + } +} diff --git a/xtoskrnl/ob/security.cc b/xtoskrnl/ob/security.cc new file mode 100644 index 0000000..1a8634a --- /dev/null +++ b/xtoskrnl/ob/security.cc @@ -0,0 +1,60 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/ob/security.cc + * DESCRIPTION: Object Manager Security API + * DEVELOPERS: Aiken Harris + */ + +#include + + +/** + * Provides the default security procedure for handling object security descriptors. + * + * @param Object + * Supplies a pointer to the object whose security descriptor is being processed. + * + * @param OperationCode + * Supplies the specific security operation to perform (e.g., Assign, Delete, Query, Set). + * + * @param SecurityInformation + * Supplies a bitmask indicating which specific components of the security descriptor are involved. + * + * @param SecurityDescriptor + * Supplies a pointer to a caller-allocated buffer. For a set/assign operation, this contains the new + * security data. For a query operation, it receives the requested data. + * + * @param Length + * Supplies a pointer to a variable that specifies the size of the SecurityDescriptor buffer. + * On return for a query operation, it receives the actual number of bytes required. + * + * @param OldSecurityDescriptor + * Supplies a pointer to a variable that holds the current security descriptor of the object. + * + * @param PoolType + * Supplies the type of memory pool to use if a new security descriptor must be allocated. + * + * @param GenericMapping + * Supplies a pointer to the generic access mapping associated with the object's type. + * + * @return This routine returns a status code indicating the success or failure of the operation. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +OB::Security::ProcessObjectSecurityDescriptor(IN PVOID Object, + IN SECURITY_OPERATION_CODE OperationCode, + IN PSECURITY_INFORMATION SecurityInformation, + IN OUT PSECURITY_DESCRIPTOR SecurityDescriptor, + IN OUT PULONG Length, + IN OUT PSECURITY_DESCRIPTOR *OldSecurityDescriptor, + IN MMPOOL_TYPE PoolType, + IN PGENERIC_MAPPING GenericMapping) +{ + UNIMPLEMENTED; + + /* Return success */ + return STATUS_SUCCESS; +} diff --git a/xtoskrnl/ob/typereg.cc b/xtoskrnl/ob/typereg.cc new file mode 100644 index 0000000..fe312e7 --- /dev/null +++ b/xtoskrnl/ob/typereg.cc @@ -0,0 +1,447 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/ob/typereg.cc + * DESCRIPTION: Object Manager Type Registry + * DEVELOPERS: Aiken Harris + */ + +#include + + +/** + * Creates and initializes a new object type within the Object Manager namespace. + * + * @param TypeName + * Supplies the requested name of the new object type. + * + * @param Initializer + * Supplies the initialization parameters for the new object type. + * + * @param SecurityDescriptor + * Supplies an optional security descriptor. (Currently ignored). + * + * @param ObjectType + * Receives a pointer to the newly created object type. + * + * @return This routine returns a status code indicating the success or failure of the operation. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +OB::TypeRegistry::CreateObjectType(IN PUNICODE_STRING TypeName, + IN POBJECT_TYPE_INITIALIZER Initializer, + IN PSECURITY_DESCRIPTOR SecurityDescriptor, + OUT POBJECT_TYPE *ObjectType) +{ + POBJECT_HEADER_CREATOR_INFO CreatorInfo; + OBJECT_LOOKUP_CONTEXT LookupContext; + POBJECT_HEADER NewObjectTypeHeader; + POBJECT_TYPE NewObjectType; + UNICODE_STRING ObjectName; + XTSTATUS Status; + + /* Validate incoming parameters */ + Status = ValidateObjectTypeParameters(TypeName, Initializer); + if(Status != STATUS_SUCCESS) + { + /* Parameter validation failed, return error code */ + return Status; + } + + /* Initialize the lookup context */ + OB::Directory::InitializeLookupContext(&LookupContext); + + /* Check if the global type directory exists */ + if(ObjectTypeDirectory) + { + /* Acquire the directory lock and prepare for namespace lookup */ + OB::Directory::LockLookupContext(ObjectTypeDirectory, &LookupContext); + + /* Search the directory to check if an object type with this name already exists */ + if(OB::Directory::LookupDirectoryEntry(ObjectTypeDirectory, TypeName, OBJECT_CASE_INSENSITIVE, + FALSE, &LookupContext)) + { + /* A collision detected, release the directory lock and lookup context */ + OB::Directory::ReleaseLookupContext(&LookupContext); + + /* Reject the creation due to a naming conflict */ + return STATUS_OBJECT_NAME_COLLISION; + } + } + + /* Allocate paged memory for the persistent object type name */ + Status = MM::Allocator::AllocatePool(PagedPool, (ULONG)TypeName->MaximumLength, + (PVOID*)&ObjectName.Buffer, TAG_OB_NAME); + if(Status != STATUS_SUCCESS || ObjectName.Buffer == NULLPTR) + { + /* Pool allocation failed; release the lookup context and return error code */ + OB::Directory::ReleaseLookupContext(&LookupContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Copy the requested type name */ + ObjectName.MaximumLength = TypeName->MaximumLength; + RTL::Unicode::CopyString(&ObjectName, TypeName); + + /* Allocate the physical memory block for the Object Type */ + Status = OB::LifeCycle::AllocateObject(NULLPTR, KernelMode, MasterObjectType, &ObjectName, + sizeof(OBJECT_TYPE), &NewObjectTypeHeader); + if(Status != STATUS_SUCCESS) + { + /* Object allocation failed; release the directory lock and free allocated name buffer */ + OB::Directory::ReleaseLookupContext(&LookupContext); + MM::Allocator::FreePool(ObjectName.Buffer); + + /* Return error code */ + return Status; + } + + /* Resolve the object body pointer and assign the captured name */ + NewObjectTypeHeader->Flags |= OBJECT_FLAG_KERNEL_MODE | OBJECT_FLAG_PERMANENT; + NewObjectType = (POBJECT_TYPE)&NewObjectTypeHeader->Body; + NewObjectType->Name = ObjectName; + + /* Zero out the statistics block */ + RTL::Memory::ZeroMemory(&NewObjectType->TotalNumberOfObjects, + FIELD_OFFSET(OBJECT_TYPE, TypeInfo) - FIELD_OFFSET(OBJECT_TYPE, TotalNumberOfObjects)); + + /* Check if master object type is already initialized */ + if(MasterObjectType == NULLPTR) + { + /* Set the global master type, link its header to itself, and set the default tag */ + MasterObjectType = NewObjectType; + NewObjectTypeHeader->Type = MasterObjectType; + NewObjectType->Key = TAG_OB_OBJECT_TYPE; + NewObjectType->TotalNumberOfObjects = 1; + } + else + { + /* Generate a clean tag from object name */ + NewObjectType->Key = GenerateObjectPoolTag(TypeName); + } + + /* Initialize default objects and pool charges */ + InitializeObjectType(NewObjectType, Initializer, &ObjectName); + + /* Suspend APCs and acquire exclusive access to the master type tracking list */ + KE::KThread::EnterCriticalRegion(); + KE::PushLock::AcquireExclusivePushLock(&MasterObjectType->TypeLock); + + /* Retrieve the creator information header */ + CreatorInfo = OB::LifeCycle::GetObjectCreatorInformation(NewObjectTypeHeader); + if(CreatorInfo != NULLPTR) + { + /* Link the new object type */ + RTL::LinkedList::InsertTailList(&MasterObjectType->TypeList, &CreatorInfo->TypeList); + + /* Prevent double-insertion during handle creation */ + NewObjectTypeHeader->Flags &= ~OBJECT_FLAG_CREATOR_INFO; + } + + /* Assign a type index */ + NewObjectType->Index = MasterObjectType->TotalNumberOfObjects; + if(NewObjectType->Index < OBJECT_MAX_DEFINED_OBJECT_TYPES) + { + /* Register the new type in the global table */ + ObjectTypesTable[NewObjectType->Index - 1] = NewObjectType; + } + + /* Release the master type lock and restore APC delivery */ + KE::PushLock::ReleaseExclusivePushLock(&MasterObjectType->TypeLock); + KE::KThread::LeaveCriticalRegion(); + + /* Publish into the namespace directory */ + if(ObjectTypeDirectory == NULLPTR || + OB::Directory::InsertDirectoryEntry(ObjectTypeDirectory, &LookupContext, NewObjectTypeHeader)) + { + /* Verify if the directory is present */ + if(ObjectTypeDirectory) + { + /* Increment the reference count */ + OB::LifeCycle::ReferenceObject(ObjectTypeDirectory); + } + + /* Release the directory lock and context */ + OB::Directory::ReleaseLookupContext(&LookupContext); + + /* Return the pointer to the object type */ + *ObjectType = NewObjectType; + return STATUS_SUCCESS; + } + else + { + /* Directory insertion failed, release lock and return error code */ + OB::Directory::ReleaseLookupContext(&LookupContext); + return STATUS_INSUFFICIENT_RESOURCES; + } +} + +/** + * Cleans up internal resources associated with an object type before its deletion. + * + * @param Object + * Supplies a pointer to the Object Type body being deleted. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +OB::TypeRegistry::DeleteObjectType(IN PVOID Object) +{ + /* Nothing to do */ +} + +/** + * Generates a 4-byte pool tag based on the object type name. + * + * @param TypeName + * Supplies the name of the object type. + * + * @return This routine returns a 32-bit pool tag. + * + * @since XT 1.0 + */ +XTAPI +ULONG +OB::TypeRegistry::GenerateObjectPoolTag(IN PUNICODE_STRING TypeName) +{ + ULONG Index, Tag; + PUCHAR TagBytes; + + /* Default tag to four spaces */ + Tag = SIGNATURE32(' ', ' ', ' ', ' '); + TagBytes = (PUCHAR)&Tag; + + /* Extract up to the first 4 characters */ + for(Index = 0; Index < 4; Index++) + { + /* Verify if the current index falls within the valid bounds */ + if(Index < (TypeName->Length / sizeof(WCHAR))) + { + /* Strip the high unicode byte and store the resulting ASCII character */ + TagBytes[Index] = (UCHAR)(TypeName->Buffer[Index] & 0xFF); + } + } + + /* Return the tag */ + return Tag; +} + +/** + * Initializes the internal body and configuration of a newly allocated object type. + * + * @param ObjectType + * Supplies a pointer to the object type to initialize. + * + * @param Initializer + * Supplies the initialization parameters. + * + * @param TypeName + * Supplies the name of the object type. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +OB::TypeRegistry::InitializeObjectType(IN OUT POBJECT_TYPE ObjectType, + IN POBJECT_TYPE_INITIALIZER Initializer, + IN PUNICODE_STRING TypeName) +{ + ULONG HeaderCharge; + + /* Calculate standard header charges */ + HeaderCharge = sizeof(OBJECT_HEADER) + sizeof(OBJECT_HEADER_NAME_INFO); + + /* Verify if the object type requires handle count tracking */ + if(Initializer->MaintainHandleCount) + { + /* Include the size of the handle information header in the total charge */ + HeaderCharge += sizeof(OBJECT_HEADER_HANDLE_INFO); + } + + /* Copy initialization data */ + RTL::Memory::CopyMemory(&ObjectType->TypeInfo, Initializer, sizeof(OBJECT_TYPE_INITIALIZER)); + + /* Enforce global type list maintenance and configure the target pool type */ + ObjectType->TypeInfo.MaintainTypeList = TRUE; + ObjectType->TypeInfo.PoolType = Initializer->PoolType; + + /* Check pool type */ + if(Initializer->PoolType == NonPagedPool) + { + /* Apply the charge to the non-paged pool requirement */ + ObjectType->TypeInfo.DefaultNonPagedPoolCharge += HeaderCharge; + } + else + { + /* Apply the charge to the paged pool requirement */ + ObjectType->TypeInfo.DefaultPagedPoolCharge += HeaderCharge; + } + + /* Check if the caller supplied a custom procedure for security descriptor operations */ + if(Initializer->SecurityProcedure == NULLPTR) + { + /* No custom procedure provided, attach the generic system handler */ + ObjectType->TypeInfo.SecurityProcedure = OB::Security::ProcessObjectSecurityDescriptor; + } + + /* Initialize synchronization mechanisms */ + KE::PushLock::InitializePushLock(&ObjectType->TypeLock); + + /* Initialize the queue for registered callbacks and the tracking list */ + RTL::LinkedList::InitializeListHead(&ObjectType->CallbackList); + RTL::LinkedList::InitializeListHead(&ObjectType->TypeList); + + /* Check if the object type has a default object */ + if(ObjectType->TypeInfo.UseDefaultObject) + { + /* Map to the global default event and append the SYNCHRONIZE access right */ + ObjectType->DefaultObject = &DefaultEvent; + ObjectType->TypeInfo.ValidAccessMask |= SYNCHRONIZE; + } + else if(TypeName->Length == 8 && !RTL::WideString::CompareWideString(TypeName->Buffer, L"File", 0)) + { + /* Point the synchronization offset directly to the internal Event field */ + ObjectType->DefaultObject = (PVOID)(ULONG_PTR)FIELD_OFFSET(FILE_OBJECT, Event); + } + else if(TypeName->Length == 24 && !RTL::WideString::CompareWideString(TypeName->Buffer, L"WaitablePort", 0)) + { + /* Point the synchronization offset directly to the WaitEvent field */ + ObjectType->DefaultObject = (PVOID)(ULONG_PTR)FIELD_OFFSET(LPCP_PORT_OBJECT, WaitEvent); + } + else + { + /* Disable synchronization for this object type */ + ObjectType->DefaultObject = NULLPTR; + } +} + +/** + * Initializes the global Object Type Registry and bootstraps the master type object. + * + * @return This routine returns a status code indicating the success or failure of the operation. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +OB::TypeRegistry::InitializeObjectTypeRegistry(VOID) +{ + OBJECT_TYPE_INITIALIZER ObjectTypeInitializer; + UNICODE_STRING TypeName; + XTSTATUS Status; + + /* Initialize the global directory pointer */ + ObjectTypeDirectory = NULLPTR; + + /* Clear the global object type lookup table and the default synchronization event block */ + RTL::Memory::ZeroMemory(ObjectTypesTable, sizeof(ObjectTypesTable)); + RTL::Memory::ZeroMemory(&DefaultEvent, sizeof(KEVENT)); + + /* Clear the initialization structure */ + RTL::Memory::ZeroMemory(&ObjectTypeInitializer, sizeof(OBJECT_TYPE_INITIALIZER)); + + /* Configure the attributes and pool charges for the master "Type" object */ + ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(OBJECT_TYPE); + ObjectTypeInitializer.InvalidAttributes = OBJECT_OPENLINK; + ObjectTypeInitializer.Length = sizeof(OBJECT_TYPE_INITIALIZER); + ObjectTypeInitializer.MaintainTypeList = TRUE; + ObjectTypeInitializer.PoolType = NonPagedPool; + ObjectTypeInitializer.UseDefaultObject = TRUE; + ObjectTypeInitializer.ValidAccessMask = OBJECT_TYPE_ALL_ACCESS; + + /* Register default destructor */ + ObjectTypeInitializer.DeleteProcedure = &DeleteObjectType; + + /* Initialize the descriptor for the "Type" name */ + RTL::Unicode::InitializeString(&TypeName, L"Type"); + + /* Create the master object type */ + Status = CreateObjectType(&TypeName, &ObjectTypeInitializer, NULLPTR, &MasterObjectType); + if(Status != STATUS_SUCCESS) + { + /* Object type creation failed, return error code */ + return STATUS_UNSUCCESSFUL; + } + + /* Return success */ + return STATUS_SUCCESS; +} + +/** + * Validates the parameters supplied for creating a new object type. + * + * @param TypeName + * Supplies the requested name of the new object type. + * + * @param Initializer + * Supplies the initialization parameters for the new object type. + * + * @return This routine returns a status code indicating the success or failure of the operation. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +OB::TypeRegistry::ValidateObjectTypeParameters(IN PUNICODE_STRING TypeName, + IN POBJECT_TYPE_INITIALIZER Initializer) +{ + ULONG Index; + PWCH Buffer; + + /* Check the basic string validity and alignment */ + if(TypeName == NULLPTR || TypeName->Length == 0 || (TypeName->Length % sizeof(WCHAR)) != 0) + { + /* Invalid type name, return error code */ + return STATUS_INVALID_PARAMETER; + } + + /* Check initializer block integrity and allowed flags */ + if(Initializer == NULLPTR || + (Initializer->InvalidAttributes & ~OBJECT_VALID_ATTRIBUTES) != 0 || + Initializer->Length != sizeof(OBJECT_TYPE_INITIALIZER)) + { + /* Invalid initializer block, return error code */ + return STATUS_INVALID_PARAMETER; + } + + /* Check the maintaining handle count and procedure presence */ + if(Initializer->MaintainHandleCount && + Initializer->OpenProcedure == NULLPTR && + Initializer->CloseProcedure == NULLPTR) + { + /* Invalid initializer block, return error code */ + return STATUS_INVALID_PARAMETER; + } + + /* Validate memory pool type */ + if(!Initializer->UseDefaultObject && Initializer->PoolType != NonPagedPool) + { + /* Invalid pool type, return error code */ + return STATUS_INVALID_PARAMETER; + } + + /* Extract the buffer pointer */ + Buffer = TypeName->Buffer; + Index = TypeName->Length / sizeof(WCHAR); + + /* Scan for illegal path separators in the type name */ + while(Index--) + { + /* Check for path separator */ + if(*Buffer++ == OBJECT_NAME_PATH_SEPARATOR) + { + /* Invalid type name, return error code */ + return STATUS_OBJECT_NAME_INVALID; + } + } + + /* Return success */ + return STATUS_SUCCESS; +}