Files
exectos/xtoskrnl/ob/typereg.cc
Aiken Harris 55e1373fc5
Some checks failed
Builds / ExectOS (amd64, debug) (push) Failing after 29s
Builds / ExectOS (i686, debug) (push) Failing after 27s
Builds / ExectOS (amd64, release) (push) Failing after 38s
Builds / ExectOS (i686, release) (push) Failing after 35s
Apply access right definitions in Object Manager type registry
2026-07-09 09:04:53 +02:00

448 lines
15 KiB
C++

/**
* PROJECT: ExectOS
* COPYRIGHT: See COPYING.md in the top level directory
* FILE: xtoskrnl/ob/typereg.cc
* DESCRIPTION: Object Manager Type Registry
* DEVELOPERS: Aiken Harris <harraiken91@gmail.com>
*/
#include <xtos.hh>
/**
* 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 |= SE_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 = SE_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;
}