Added Handle cloning and reference counting in preparation for IShareSys
Made the internal code a bit more flexible and improved access security structures --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40227
This commit is contained in:
parent
1e573fe0d0
commit
352b15c1b7
@ -36,29 +36,39 @@ namespace SourceMod
|
||||
HandleError_Freed, /* The handle has been freed */
|
||||
HandleError_Index, /* generic internal indexing error */
|
||||
HandleError_Access, /* No access permitted to free this handle */
|
||||
HandleError_Limit, /* The limited number of handles has been reached */
|
||||
};
|
||||
|
||||
struct HandleAccess
|
||||
enum HandleAccessRight
|
||||
{
|
||||
HandleAccess() : canRead(true), canDelete(true), canInherit(true), canCreate(true)
|
||||
{
|
||||
}
|
||||
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 */
|
||||
HandleAccess_Create, /* TYPE: Instances can be created by other objects (this makes it searchable) */
|
||||
HandleAccess_Read, /* HANDLES: Can be read by other objects */
|
||||
HandleAccess_Delete, /* HANDLES: Can be deleted by other objects */
|
||||
HandleAccess_Inherit, /* TYPE: Can be inherited by new types */
|
||||
HandleAccess_Clone, /* HANDLES: Can be cloned */
|
||||
/* ------------- */
|
||||
HandleAccess_TOTAL, /* Total number of access rights */
|
||||
};
|
||||
|
||||
struct HandleSecurity
|
||||
{
|
||||
IdentityToken_t owner; /* Owner of the handle */
|
||||
HandleAccess all; /* Access permissions of everyone */
|
||||
HandleSecurity()
|
||||
{
|
||||
owner = 0;
|
||||
access[HandleAccess_Create] = true;
|
||||
access[HandleAccess_Read] = true;
|
||||
access[HandleAccess_Delete] = true;
|
||||
access[HandleAccess_Inherit] = true;
|
||||
access[HandleAccess_Clone] = true;
|
||||
}
|
||||
IdentityToken_t owner; /* Owner of the handle */
|
||||
bool access[HandleAccess_TOTAL]; /* World access rights */
|
||||
};
|
||||
|
||||
class IHandleTypeDispatch
|
||||
{
|
||||
public:
|
||||
virtual unsigned int GetInterfaceVersion()
|
||||
virtual unsigned int GetDispatchVersion()
|
||||
{
|
||||
return SMINTERFACE_HANDLESYSTEM_VERSION;
|
||||
}
|
||||
@ -174,13 +184,27 @@ namespace SourceMod
|
||||
IdentityToken_t ident) =0;
|
||||
|
||||
/**
|
||||
* @brief Destroys a handle.
|
||||
* @brief Frees the memory associated with a handle and calls any destructors.
|
||||
* NOTE: This function will decrement the internal reference counter. It will
|
||||
* only perform any further action if the counter hits 0.
|
||||
*
|
||||
* @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, IdentityToken_t ident) =0;
|
||||
virtual HandleError FreeHandle(Handle_t handle, IdentityToken_t ident) =0;
|
||||
|
||||
/**
|
||||
* @brief Clones a handle by adding to its internal reference count. Its data,
|
||||
* type, and security permissions remain the same.
|
||||
*
|
||||
* @param handle Handle to duplicate. Any non-free handle target is valid.
|
||||
* @param newhandle If non-NULL, stores the duplicated handle in the pointer.
|
||||
* @param source New source of cloned handle.
|
||||
* @param ident Security token, if needed.
|
||||
* @return A HandleError error code.
|
||||
*/
|
||||
virtual HandleError CloneHandle(Handle_t handle, Handle_t *newhandle, IdentityToken_t source, IdentityToken_t ident) =0;
|
||||
|
||||
/**
|
||||
* @brief Retrieves the contents of a handle.
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "HandleSys.h"
|
||||
#include "PluginSys.h"
|
||||
#include <assert.h>
|
||||
|
||||
HandleSystem g_HandleSys;
|
||||
|
||||
@ -56,7 +57,7 @@ HandleType_t HandleSystem::CreateTypeEx(const char *name,
|
||||
}
|
||||
if (parent >= HANDLESYS_TYPEARRAY_SIZE
|
||||
|| m_Types[parent].dispatch != NULL
|
||||
|| m_Types[parent].sec.all.canInherit == false)
|
||||
|| m_Types[parent].sec.access[HandleAccess_Inherit] == false)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -68,12 +69,13 @@ HandleType_t HandleSystem::CreateTypeEx(const char *name,
|
||||
{
|
||||
security = &m_Types[parent].sec;
|
||||
} else {
|
||||
static HandleSecurity def_h = {0, HandleAccess() };
|
||||
static HandleSecurity def_h;
|
||||
security = &def_h;
|
||||
}
|
||||
}
|
||||
|
||||
if (security->all.canCreate && sm_trie_retrieve(m_TypeLookup, name, NULL))
|
||||
if (security->access[HandleAccess_Create]
|
||||
&& sm_trie_retrieve(m_TypeLookup, name, NULL))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -158,6 +160,54 @@ bool HandleSystem::FindHandleType(const char *name, HandleType_t *type)
|
||||
return true;
|
||||
}
|
||||
|
||||
HandleError HandleSystem::MakePrimHandle(HandleType_t type,
|
||||
QHandle **in_pHandle,
|
||||
unsigned int *in_index,
|
||||
Handle_t *in_handle)
|
||||
{
|
||||
unsigned int handle;
|
||||
if (m_FreeHandles == 0)
|
||||
{
|
||||
if (m_HandleTail >= HANDLESYS_MAX_HANDLES)
|
||||
{
|
||||
return HandleError_Limit;;
|
||||
}
|
||||
handle = ++m_HandleTail;
|
||||
} else {
|
||||
handle = m_Handles[m_FreeHandles--].freeID;
|
||||
}
|
||||
|
||||
QHandle *pHandle = &m_Handles[handle];
|
||||
|
||||
assert(pHandle->set == false);
|
||||
|
||||
if (++m_HSerial >= HANDLESYS_MAX_SERIALS)
|
||||
{
|
||||
m_HSerial = 1;
|
||||
}
|
||||
|
||||
/* Set essential information */
|
||||
pHandle->set = true;
|
||||
pHandle->refcount = 1;
|
||||
pHandle->type = type;
|
||||
pHandle->serial = m_HSerial;
|
||||
|
||||
/* Create the hash value */
|
||||
Handle_t hash = pHandle->serial;
|
||||
hash <<= 16;
|
||||
hash |= handle;
|
||||
|
||||
/* Add a reference count to the type */
|
||||
m_Types[type].opened++;
|
||||
|
||||
/* Output */
|
||||
*in_pHandle = pHandle;
|
||||
*in_index = handle;
|
||||
*in_handle = hash;
|
||||
|
||||
return HandleError_None;
|
||||
}
|
||||
|
||||
Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityToken_t source, IdentityToken_t ident)
|
||||
{
|
||||
if (!type
|
||||
@ -169,44 +219,27 @@ Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityTok
|
||||
|
||||
/* Check the security of this handle */
|
||||
QHandleType *pType = &m_Types[type];
|
||||
if (!pType->sec.all.canCreate
|
||||
if (!pType->sec.access[HandleAccess_Create]
|
||||
&& pType->sec.owner != ident)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int handle = 0;
|
||||
if (m_FreeHandles == 0)
|
||||
unsigned int index;
|
||||
Handle_t handle;
|
||||
QHandle *pHandle;
|
||||
HandleError err;
|
||||
|
||||
if ((err=MakePrimHandle(type, &pHandle, &index, &handle)) != HandleError_None)
|
||||
{
|
||||
if (m_HandleTail >= HANDLESYS_MAX_HANDLES)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
handle = ++m_HandleTail;
|
||||
} else {
|
||||
handle = m_Handles[m_FreeHandles--].freeID;
|
||||
return 0;
|
||||
}
|
||||
|
||||
QHandle *pHandle = &m_Handles[handle];
|
||||
|
||||
pHandle->type = type;
|
||||
pHandle->source = source;
|
||||
pHandle->set = true;
|
||||
pHandle->object = object;
|
||||
pHandle->clone = 0;
|
||||
|
||||
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;
|
||||
return handle;
|
||||
}
|
||||
|
||||
Handle_t HandleSystem::CreateScriptHandle(HandleType_t type,
|
||||
@ -219,7 +252,11 @@ Handle_t HandleSystem::CreateScriptHandle(HandleType_t type,
|
||||
return CreateHandle(type, object, pPlugin->GetIdentity(), ident);
|
||||
}
|
||||
|
||||
HandleError HandleSystem::DestroyHandle(Handle_t handle, IdentityToken_t ident)
|
||||
HandleError HandleSystem::GetHandle(Handle_t handle,
|
||||
IdentityToken_t ident,
|
||||
QHandle **in_pHandle,
|
||||
unsigned int *in_index,
|
||||
HandleAccessRight access)
|
||||
{
|
||||
unsigned int serial = (handle >> 16);
|
||||
unsigned int index = (handle & HANDLESYS_HANDLE_MASK);
|
||||
@ -232,8 +269,8 @@ HandleError HandleSystem::DestroyHandle(Handle_t handle, IdentityToken_t ident)
|
||||
QHandle *pHandle = &m_Handles[index];
|
||||
QHandleType *pType = &m_Types[pHandle->type];
|
||||
|
||||
if (!pType->sec.all.canDelete
|
||||
&& pType->sec.owner != ident)
|
||||
if ((access != HandleAccess_TOTAL)
|
||||
&& (!pType->sec.access[access] && pType->sec.owner != ident))
|
||||
{
|
||||
return HandleError_Access;
|
||||
}
|
||||
@ -246,10 +283,80 @@ HandleError HandleSystem::DestroyHandle(Handle_t handle, IdentityToken_t ident)
|
||||
return HandleError_Changed;
|
||||
}
|
||||
|
||||
pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object);
|
||||
pType->opened--;
|
||||
pHandle->set = false;
|
||||
m_Handles[++m_FreeTypes].freeID = index;
|
||||
*in_pHandle = pHandle;
|
||||
*in_index = index;
|
||||
|
||||
return HandleError_None;
|
||||
}
|
||||
|
||||
HandleError HandleSystem::CloneHandle(Handle_t handle, Handle_t *out_newhandle, IdentityToken_t source, IdentityToken_t ident)
|
||||
{
|
||||
HandleError err;
|
||||
QHandle *pHandle;
|
||||
unsigned int index;
|
||||
|
||||
if ((err=GetHandle(handle, ident, &pHandle, &index, HandleAccess_Clone)) != HandleError_None)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
QHandleType *pType = &m_Types[pHandle->type];
|
||||
|
||||
/* Get a new Handle ID */
|
||||
unsigned int new_index;
|
||||
QHandle *pNewHandle;
|
||||
Handle_t new_handle;
|
||||
|
||||
if ((err=MakePrimHandle(pHandle->type, &pNewHandle, &new_index, &new_handle)) != HandleError_None)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
pNewHandle->clone = index;
|
||||
pNewHandle->source = source;
|
||||
|
||||
if (out_newhandle)
|
||||
{
|
||||
*out_newhandle = new_handle;
|
||||
}
|
||||
|
||||
return HandleError_None;
|
||||
}
|
||||
|
||||
HandleError HandleSystem::FreeHandle(Handle_t handle, IdentityToken_t ident)
|
||||
{
|
||||
unsigned int index;
|
||||
QHandle *pHandle;
|
||||
HandleError err;
|
||||
|
||||
if ((err=GetHandle(handle, ident, &pHandle, &index, HandleAccess_Delete)) != HandleError_None)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
QHandleType *pType = &m_Types[pHandle->type];
|
||||
|
||||
bool dofree = false;
|
||||
if (pHandle->clone)
|
||||
{
|
||||
/* If we're a clone, there's not a lot to do. */
|
||||
if (FreeHandle(pHandle->clone, ident) != HandleError_None)
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
dofree = true;
|
||||
} else {
|
||||
if (--pHandle->refcount == 0)
|
||||
{
|
||||
dofree = true;
|
||||
pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object);
|
||||
}
|
||||
}
|
||||
|
||||
if (dofree)
|
||||
{
|
||||
ReleasePrimHandle(index);
|
||||
}
|
||||
|
||||
return HandleError_None;
|
||||
}
|
||||
@ -264,24 +371,13 @@ HandleError HandleSystem::ReadHandle(Handle_t handle,
|
||||
IdentityToken_t ident,
|
||||
void **object)
|
||||
{
|
||||
unsigned int serial = (handle >> 16);
|
||||
unsigned int index = (handle & HANDLESYS_HANDLE_MASK);
|
||||
unsigned int index;
|
||||
QHandle *pHandle;
|
||||
HandleError err;
|
||||
|
||||
if (index == 0 || index == 0xFFFF)
|
||||
if ((err=GetHandle(handle, ident, &pHandle, &index, HandleAccess_Read)) != HandleError_None)
|
||||
{
|
||||
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;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Check the type inheritance */
|
||||
@ -299,22 +395,28 @@ HandleError HandleSystem::ReadHandle(Handle_t handle,
|
||||
}
|
||||
}
|
||||
|
||||
/* Now do security checks */
|
||||
QHandleType *pType = &m_Types[pHandle->type];
|
||||
if (!pType->sec.all.canRead
|
||||
&& pType->sec.owner != ident)
|
||||
{
|
||||
return HandleError_Access;
|
||||
}
|
||||
|
||||
if (object)
|
||||
{
|
||||
/* if we're a clone, the rules change - object is ONLY in our reference */
|
||||
if (pHandle->clone)
|
||||
{
|
||||
pHandle = &m_Handles[pHandle->clone];
|
||||
}
|
||||
*object = pHandle->object;
|
||||
}
|
||||
|
||||
return HandleError_None;
|
||||
}
|
||||
|
||||
void HandleSystem::ReleasePrimHandle(unsigned int index)
|
||||
{
|
||||
QHandle *pHandle = &m_Handles[index];
|
||||
|
||||
pHandle->set = false;
|
||||
m_Types[pHandle->type].opened--;
|
||||
m_Handles[++m_FreeHandles].freeID = index;
|
||||
}
|
||||
|
||||
bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t ident)
|
||||
{
|
||||
if (type == 0 || type >= HANDLESYS_TYPEARRAY_SIZE)
|
||||
@ -358,17 +460,37 @@ bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t ident)
|
||||
if (pType->opened)
|
||||
{
|
||||
QHandle *pHandle;
|
||||
for (unsigned int i=1; i<=HANDLESYS_MAX_HANDLES; i++)
|
||||
for (unsigned int i=1; i<m_HandleTail; 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)
|
||||
if (pHandle->clone)
|
||||
{
|
||||
/* Get parent */
|
||||
QHandle *pOther = &m_Handles[pHandle->clone];
|
||||
if (--pOther->refcount == 0)
|
||||
{
|
||||
/* Free! */
|
||||
dispatch->OnHandleDestroy(type, pOther->object);
|
||||
ReleasePrimHandle(pHandle->clone);
|
||||
}
|
||||
/* Unlink ourselves since we don't have a reference count */
|
||||
ReleasePrimHandle(i);
|
||||
} else {
|
||||
/* If it's not a clone, we still have to check the reference count.
|
||||
* Either way, we'll be destroyed eventually because the handle types do not change.
|
||||
*/
|
||||
if (--pHandle->refcount == 0)
|
||||
{
|
||||
/* Free! */
|
||||
dispatch->OnHandleDestroy(type, pHandle->object);
|
||||
ReleasePrimHandle(i);
|
||||
}
|
||||
}
|
||||
if (pType->opened == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "sourcemod.h"
|
||||
#include "sm_memtable.h"
|
||||
|
||||
#define HANDLESYS_MAX_HANDLES (1<<16)
|
||||
#define HANDLESYS_MAX_HANDLES (1<<14)
|
||||
#define HANDLESYS_MAX_TYPES (1<<9)
|
||||
#define HANDLESYS_MAX_SUBTYPES 0xF
|
||||
#define HANDLESYS_SUBTYPE_MASK 0xF
|
||||
@ -18,12 +18,14 @@
|
||||
|
||||
struct QHandle
|
||||
{
|
||||
HandleType_t type;
|
||||
void *object;
|
||||
unsigned int freeID;
|
||||
IdentityToken_t source;
|
||||
bool set;
|
||||
unsigned int serial;
|
||||
HandleType_t type; /* Handle type */
|
||||
void *object; /* Unmaintained object pointer */
|
||||
unsigned int freeID; /* ID of a free handle in the free handle chain */
|
||||
IdentityToken_t source; /* Identity of object which owns this */
|
||||
unsigned int serial; /* Serial no. for sanity checking */
|
||||
unsigned int refcount; /* Reference count for safe destruction */
|
||||
Handle_t clone; /* If non-zero, this is our cloned parent */
|
||||
bool set; /* Whether or not this handle is set */
|
||||
};
|
||||
|
||||
struct QHandleType
|
||||
@ -56,8 +58,29 @@ public: //IHandleSystem
|
||||
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 FreeHandle(Handle_t handle, IdentityToken_t ident);
|
||||
HandleError CloneHandle(Handle_t handle, Handle_t *newhandle, IdentityToken_t source, IdentityToken_t ident);
|
||||
HandleError ReadHandle(Handle_t handle, HandleType_t type, IdentityToken_t ident, void **object);
|
||||
private:
|
||||
/**
|
||||
* Decodes a handle with sanity and security checking.
|
||||
*/
|
||||
HandleError GetHandle(Handle_t handle,
|
||||
IdentityToken_t ident,
|
||||
QHandle **pHandle,
|
||||
unsigned int *index,
|
||||
HandleAccessRight access);
|
||||
|
||||
/**
|
||||
* Creates a basic handle and sets its reference count to 1.
|
||||
* Does not do any type or security checking.
|
||||
*/
|
||||
HandleError MakePrimHandle(HandleType_t type, QHandle **pHandle, unsigned int *index, HandleType_t *handle);
|
||||
|
||||
/**
|
||||
* Frees a primitive handle. Does no object freeing, only reference count and bookkeepping.
|
||||
*/
|
||||
void ReleasePrimHandle(unsigned int index);
|
||||
private:
|
||||
QHandle *m_Handles;
|
||||
QHandleType *m_Types;
|
||||
|
@ -54,12 +54,6 @@ CPlugin::~CPlugin()
|
||||
m_pub_funcs = NULL;
|
||||
}
|
||||
|
||||
if (m_plugin)
|
||||
{
|
||||
g_pSourcePawn->FreeFromMemory(m_plugin);
|
||||
m_plugin = NULL;
|
||||
}
|
||||
|
||||
if (m_priv_funcs)
|
||||
{
|
||||
for (unsigned int i=0; i<m_funcsnum; i++)
|
||||
@ -69,6 +63,14 @@ CPlugin::~CPlugin()
|
||||
delete [] m_priv_funcs;
|
||||
m_priv_funcs = NULL;
|
||||
}
|
||||
|
||||
if (m_plugin)
|
||||
{
|
||||
g_pSourcePawn->FreeFromMemory(m_plugin);
|
||||
m_plugin = NULL;
|
||||
}
|
||||
|
||||
g_HandleSys.FreeHandle(m_handle, g_PluginType);
|
||||
}
|
||||
|
||||
CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength)
|
||||
@ -328,7 +330,7 @@ void CPlugin::Call_OnPluginInit()
|
||||
}
|
||||
|
||||
/* :TODO: push our own handle */
|
||||
pFunction->PushCell(0);
|
||||
pFunction->PushCell(m_handle);
|
||||
if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
|
||||
{
|
||||
/* :TODO: log into debugger instead */
|
||||
@ -360,7 +362,7 @@ bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength)
|
||||
return true;
|
||||
}
|
||||
|
||||
pFunction->PushCell(0); //:TODO: handle to ourself
|
||||
pFunction->PushCell(m_handle);
|
||||
pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0);
|
||||
pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK);
|
||||
pFunction->PushCell(maxlength);
|
||||
@ -1093,9 +1095,10 @@ void CPluginManager::OnSourceModAllInitialized()
|
||||
HandleSecurity sec;
|
||||
|
||||
sec.owner = 1; /* :TODO: implement ShareSys */
|
||||
sec.all.canCreate = false;
|
||||
sec.all.canDelete = false;
|
||||
sec.all.canInherit = false;
|
||||
sec.access[HandleAccess_Create] = false;
|
||||
sec.access[HandleAccess_Delete] = false;
|
||||
sec.access[HandleAccess_Inherit] = false;
|
||||
sec.access[HandleAccess_Clone] = false;
|
||||
|
||||
g_PluginType = g_HandleSys.CreateTypeEx("IPlugin", this, 0, &sec);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user