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:
David Anderson 2006-12-17 09:56:45 +00:00
parent 1e573fe0d0
commit 352b15c1b7
4 changed files with 270 additions and 98 deletions

View File

@ -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.

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}