From c189dfa9913598ae04812e58dd67f27e90037d61 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 16 Dec 2006 22:27:18 +0000 Subject: [PATCH] implemented and finalized initial HandleSystem API --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40223 --- core/interfaces/IHandleSys.h | 42 +++- core/msvc8/sourcemod_mm.vcproj | 12 ++ core/systems/HandleSys.cpp | 379 +++++++++++++++++++++++++++++++++ core/systems/HandleSys.h | 75 +++++++ 4 files changed, 497 insertions(+), 11 deletions(-) create mode 100644 core/systems/HandleSys.cpp create mode 100644 core/systems/HandleSys.h diff --git a/core/interfaces/IHandleSys.h b/core/interfaces/IHandleSys.h index a1eeb4a9..2621ba6d 100644 --- a/core/interfaces/IHandleSys.h +++ b/core/interfaces/IHandleSys.h @@ -15,6 +15,19 @@ namespace SourceMod typedef unsigned int HandleType_t; typedef unsigned int Handle_t; + /** + * About type checking: + * Types can be inherited - a Parent type ("Supertype") can have child types. + * When accessing handles, type checking is done. This table shows how this is resolved: + * + * HANDLE CHECK -> RESULT + * ------ ----- ------ + * Parent Parent Success + * Parent Child Fail + * Child Parent Success + * Child Child Success + */ + enum HandleError { HandleError_None = 0, /* No error */ @@ -27,12 +40,12 @@ namespace SourceMod struct HandleAccess { - HandleAccess() : canRead(true), canDelete(true), canInherit(true) + HandleAccess() : canRead(true), canDelete(true), canInherit(true), canCreate(true) { } - bool canCreate; /* Instances can be created by other objects */ - bool canRead; /* Handle and type can be read by other objects */ - bool canDelete; /* Handle can be deleted by other objects */ + bool canCreate; /* Instances can be created by other objects (this makes it searchable) */ + bool canRead; /* Handles can be read by other objects */ + bool canDelete; /* Handles can be deleted by other objects */ bool canInherit; /* Handle type can be inherited */ }; @@ -87,7 +100,8 @@ namespace SourceMod * @param name Name of handle type (NULL or "" to be anonymous) * @param dispatch Pointer to a valid IHandleTypeDispatch object. * @param parent Parent handle to inherit from, 0 for none. - * @param security Pointer to a temporary HandleSecurity object, NULL to use defaults. + * @param security Pointer to a temporary HandleSecurity object, NULL to use default + * or inherited permissions. * @return A new HandleType_t unique ID. */ virtual HandleType_t CreateTypeEx(const char *name, @@ -135,13 +149,13 @@ namespace SourceMod * * @param type Type to use on the handle. * @param object Object to bind to the handle. - * @param owner Identity token for object using this handle. + * @param source Identity token for object using this handle (for example, a script). * @param ident Identity token if any security rights are needed. * @return A new Handle_t, or 0 on failure. */ - virtual Handle_t CreateHandleEx(HandleType_t type, + virtual Handle_t CreateHandle(HandleType_t type, void *object, - IdentityToken_t owner, + IdentityToken_t source, IdentityToken_t ident) =0; /** @@ -151,27 +165,33 @@ namespace SourceMod * @param type Type to use on the handle. * @param object Object to bind to the handle. * @param ctx Plugin context that will own this handle. NULL for none. + * @param ident Identity token if any security rights are needed. * @return A new Handle_t. */ - virtual Handle_t CreateScriptHandle(HandleType_t type, void *object, sp_context_t *ctx) =0; + virtual Handle_t CreateScriptHandle(HandleType_t type, + void *object, + sp_context_t *ctx, + IdentityToken_t ident) =0; /** * @brief Destroys a handle. * * @param type Handle_t identifier to destroy. + * @param ident Identity token, for destroying secure handles (0 for none). * @return A HandleError error code. */ - virtual HandleError DestroyHandle(Handle_t handle) =0; + virtual HandleError DestroyHandle(Handle_t handle, IdentityToken_t ident) =0; /** * @brief Retrieves the contents of a handle. * * @param handle Handle_t from which to retrieve contents. * @param type Expected type to read as. + * @param ident Identity token to validate as. * @param object Address to store object in. * @return HandleError error code. */ - virtual HandleError ReadHandle(Handle_t handle, HandleType_t type, void **object) =0; + virtual HandleError ReadHandle(Handle_t handle, HandleType_t type, IdentityToken_t ident, void **object) =0; }; }; diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj index 3c946012..f0900e3f 100644 --- a/core/msvc8/sourcemod_mm.vcproj +++ b/core/msvc8/sourcemod_mm.vcproj @@ -267,6 +267,10 @@ RelativePath="..\systems\ForwardSys.h" > + + @@ -291,6 +295,10 @@ RelativePath="..\systems\ForwardSys.cpp" > + + @@ -311,6 +319,10 @@ RelativePath="..\interfaces\IForwardSys.h" > + + diff --git a/core/systems/HandleSys.cpp b/core/systems/HandleSys.cpp new file mode 100644 index 00000000..78c32af1 --- /dev/null +++ b/core/systems/HandleSys.cpp @@ -0,0 +1,379 @@ +#include "HandleSys.h" +#include "PluginSys.h" + +HandleSystem g_HandleSys; + +HandleSystem::HandleSystem() +{ + m_Handles = new QHandle[HANDLESYS_MAX_HANDLES + 1]; + memset(m_Handles, 0, sizeof(QHandle) * (HANDLESYS_MAX_HANDLES + 1)); + + m_Types = new QHandleType[HANDLESYS_TYPEARRAY_SIZE]; + memset(m_Types, 0, sizeof(QHandleType) * HANDLESYS_TYPEARRAY_SIZE); + + m_TypeLookup = sm_trie_create(); + m_strtab = new BaseStringTable(512); + + m_TypeTail = 0; +} + +HandleSystem::~HandleSystem() +{ + delete [] m_Handles; + delete [] m_Types; + sm_trie_destroy(m_TypeLookup); + delete m_strtab; +} + +HandleType_t HandleSystem::CreateType(const char *name, IHandleTypeDispatch *dispatch) +{ + return CreateTypeEx(name, dispatch, 0, NULL); +} + +HandleType_t HandleSystem::CreateChildType(const char *name, HandleType_t parent, IHandleTypeDispatch *dispatch) +{ + return CreateTypeEx(name, dispatch, parent, NULL); +} + +HandleType_t HandleSystem::CreateTypeEx(const char *name, + IHandleTypeDispatch *dispatch, + HandleType_t parent, + const HandleSecurity *security) +{ + if (!dispatch) + { + return 0; + } + + bool isChild = false; + + if (parent != 0) + { + isChild = true; + if (parent & HANDLESYS_SUBTYPE_MASK) + { + return 0; + } + if (parent >= HANDLESYS_TYPEARRAY_SIZE + || m_Types[parent].dispatch != NULL + || m_Types[parent].sec.all.canInherit == false) + { + return 0; + } + } + + if (!security) + { + if (isChild) + { + security = &m_Types[parent].sec; + } else { + static HandleSecurity def_h = {0, HandleAccess() }; + security = &def_h; + } + } + + if (security->all.canCreate && sm_trie_retrieve(m_TypeLookup, name, NULL)) + { + return 0; + } + + unsigned int index; + + if (isChild) + { + QHandleType *pParent = &m_Types[parent]; + if (pParent->children >= HANDLESYS_MAX_SUBTYPES) + { + return 0; + } + index = 0; + for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++) + { + if (m_Types[parent + i].dispatch == NULL) + { + index = parent + i; + break; + } + } + if (!index) + { + return 0; + } + pParent->children++; + } else { + if (m_FreeTypes == 0) + { + /* Reserve another index */ + if (m_TypeTail >= HANDLESYS_TYPEARRAY_SIZE) + { + return 0; + } else { + m_TypeTail += (HANDLESYS_MAX_SUBTYPES + 1); + index = m_TypeTail; + } + } else { + /* The "free array" is compacted into the normal array for easiness */ + index = m_Types[m_FreeTypes--].freeID; + } + } + + QHandleType *pType = &m_Types[index]; + + pType->dispatch = dispatch; + if (name && name[0] != '\0') + { + pType->nameIdx = m_strtab->AddString(name); + sm_trie_insert(m_TypeLookup, name, (void *)pType); + } else { + pType->nameIdx = -1; + } + pType->sec = *security; + pType->opened = 0; + + if (!isChild) + { + pType->children = 0; + } + + return index; +} + +bool HandleSystem::FindHandleType(const char *name, HandleType_t *type) +{ + QHandleType *_type; + + if (!sm_trie_retrieve(m_TypeLookup, name, (void **)&_type)) + { + return false; + } + + unsigned int offset = _type - m_Types; + + if (type) + { + *type = offset; + } + + return true; +} + +Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityToken_t source, IdentityToken_t ident) +{ + if (!type + || type >= HANDLESYS_TYPEARRAY_SIZE + || m_Types[type].dispatch == NULL) + { + return 0; + } + + /* Check the security of this handle */ + QHandleType *pType = &m_Types[type]; + if (!pType->sec.all.canCreate + && pType->sec.owner != ident) + { + return 0; + } + + unsigned int handle = 0; + if (m_FreeHandles == 0) + { + if (m_HandleTail >= HANDLESYS_MAX_HANDLES) + { + return 0; + } + handle = ++m_HandleTail; + } else { + handle = m_Handles[m_FreeHandles--].freeID; + } + + QHandle *pHandle = &m_Handles[handle]; + + pHandle->type = type; + pHandle->source = source; + pHandle->set = true; + pHandle->object = object; + + if (++m_HSerial >= HANDLESYS_MAX_SERIALS) + { + m_HSerial = 1; + } + pHandle->serial = m_HSerial; + + Handle_t hash = pHandle->serial; + hash <<= 16; + hash |= handle; + + pType->opened++; + + return hash; +} + +Handle_t HandleSystem::CreateScriptHandle(HandleType_t type, + void *object, + sp_context_t *ctx, + IdentityToken_t ident) +{ + IPlugin *pPlugin = g_PluginSys.FindPluginByContext(ctx); + + return CreateHandle(type, object, pPlugin->GetIdentity(), ident); +} + +HandleError HandleSystem::DestroyHandle(Handle_t handle, IdentityToken_t ident) +{ + unsigned int serial = (handle >> 16); + unsigned int index = (handle & HANDLESYS_HANDLE_MASK); + + if (index == 0 || index == 0xFFFF) + { + return HandleError_Index; + } + + QHandle *pHandle = &m_Handles[index]; + QHandleType *pType = &m_Types[pHandle->type]; + + if (!pType->sec.all.canDelete + && pType->sec.owner != ident) + { + return HandleError_Access; + } + if (!pHandle->set) + { + return HandleError_Freed; + } + if (pHandle->serial != serial) + { + return HandleError_Changed; + } + + pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object); + pType->opened--; + pHandle->set = false; + m_Handles[++m_FreeTypes].freeID = index; + + return HandleError_None; +} + +inline HandleType_t TypeParent(HandleType_t type) +{ + return (type & ~HANDLESYS_SUBTYPE_MASK); +} + +HandleError HandleSystem::ReadHandle(Handle_t handle, + HandleType_t type, + IdentityToken_t ident, + void **object) +{ + unsigned int serial = (handle >> 16); + unsigned int index = (handle & HANDLESYS_HANDLE_MASK); + + if (index == 0 || index == 0xFFFF) + { + return HandleError_Index; + } + + QHandle *pHandle = &m_Handles[index]; + + /* Do sanity checks first */ + if (!pHandle->set) + { + return HandleError_Freed; + } + if (pHandle->serial != serial) + { + return HandleError_Changed; + } + + /* Check the type inheritance */ + if (pHandle->type & HANDLESYS_SUBTYPE_MASK) + { + if (pHandle->type != type + && (TypeParent(pHandle->type) != TypeParent(type))) + { + return HandleError_Type; + } + } else { + if (pHandle->type != type) + { + return HandleError_Type; + } + } + + /* Now do security checks */ + QHandleType *pType = &m_Types[pHandle->type]; + if (!pType->sec.all.canRead + && pType->sec.owner != ident) + { + return HandleError_Access; + } + + if (object) + { + *object = pHandle->object; + } + + return HandleError_None; +} + +bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t ident) +{ + if (type == 0 || type >= HANDLESYS_TYPEARRAY_SIZE) + { + return false; + } + + QHandleType *pType = &m_Types[type]; + + if (pType->sec.owner && pType->sec.owner != ident) + { + return false; + } + + if (pType->dispatch == NULL) + { + return false; + } + + /* Remove children if we have to */ + if (!(type & HANDLESYS_SUBTYPE_MASK)) + { + QHandleType *childType; + for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++) + { + childType = &m_Types[type + i]; + if (childType->dispatch) + { + RemoveType(type + i, childType->sec.owner); + } + } + /* Link us into the free chain */ + m_Types[++m_FreeTypes].freeID = type; + } + + /* Invalidate the type now */ + IHandleTypeDispatch *dispatch = pType->dispatch; + pType->dispatch = NULL; + + /* Make sure nothing is using this type. */ + if (pType->opened) + { + QHandle *pHandle; + for (unsigned int i=1; i<=HANDLESYS_MAX_HANDLES; i++) + { + pHandle = &m_Handles[i]; + if (!pHandle->set || pHandle->type != type) + { + continue; + } + dispatch->OnHandleDestroy(type, pHandle->object); + pHandle->set = false; + m_Handles[++m_FreeHandles].freeID = i; + if (--pType->opened == 0) + { + break; + } + } + } + + return true; +} diff --git a/core/systems/HandleSys.h b/core/systems/HandleSys.h new file mode 100644 index 00000000..84a78b55 --- /dev/null +++ b/core/systems/HandleSys.h @@ -0,0 +1,75 @@ +#ifndef _INCLUDE_SOURCEMOD_HANDLESYSTEM_H_ +#define _INCLUDE_SOURCEMOD_HANDLESYSTEM_H_ + +#include +#include "sm_globals.h" +#include "sm_trie.h" +#include "sourcemod.h" +#include "sm_memtable.h" + +#define HANDLESYS_MAX_HANDLES (1<<16) +#define HANDLESYS_MAX_TYPES (1<<9) +#define HANDLESYS_MAX_SUBTYPES 0xF +#define HANDLESYS_SUBTYPE_MASK 0xF +#define HANDLESYS_TYPEARRAY_SIZE (HANDLESYS_MAX_TYPES * (HANDLESYS_MAX_SUBTYPES + 1)) +#define HANDLESYS_MAX_SERIALS 0xFFFF +#define HANDLESYS_SERIAL_MASK 0xFFFF0000 +#define HANDLESYS_HANDLE_MASK 0x0000FFFF + +struct QHandle +{ + HandleType_t type; + void *object; + unsigned int freeID; + IdentityToken_t source; + bool set; + unsigned int serial; +}; + +struct QHandleType +{ + IHandleTypeDispatch *dispatch; + unsigned int freeID; + unsigned int children; + HandleSecurity sec; + unsigned int opened; + int nameIdx; +}; + +class HandleSystem : + public IHandleSys +{ +public: + HandleSystem(); + ~HandleSystem(); +public: //IHandleSystem + HandleType_t CreateType(const char *name, IHandleTypeDispatch *dispatch); + HandleType_t CreateTypeEx(const char *name, + IHandleTypeDispatch *dispatch, + HandleType_t parent, + const HandleSecurity *security); + HandleType_t CreateChildType(const char *name, HandleType_t parent, IHandleTypeDispatch *dispatch); + bool RemoveType(HandleType_t type, IdentityToken_t ident); + bool FindHandleType(const char *name, HandleType_t *type); + Handle_t CreateHandle(HandleType_t type, + void *object, + IdentityToken_t source, + IdentityToken_t ident); + Handle_t CreateScriptHandle(HandleType_t type, void *object, sp_context_t *ctx, IdentityToken_t ident); + HandleError DestroyHandle(Handle_t handle, IdentityToken_t ident); + HandleError ReadHandle(Handle_t handle, HandleType_t type, IdentityToken_t ident, void **object); +private: + QHandle *m_Handles; + QHandleType *m_Types; + Trie *m_TypeLookup; + unsigned int m_TypeTail; + unsigned int m_FreeTypes; + unsigned int m_HandleTail; + unsigned int m_FreeHandles; + unsigned int m_HSerial; + BaseStringTable *m_strtab; +}; + +extern HandleSystem g_HandleSys; + +#endif //_INCLUDE_SOURCEMOD_HANDLESYSTEM_H_