2 * ===============================================================
3 * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
4 * ===============================================================
6 * This file is not open source and may not be copied without explicit
7 * written permission of AlliedModders LLC. This file may not be redistributed
8 * in whole or significant part.
9 * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
14 #include "HandleSys.h"
16 #include "PluginSys.h"
20 HandleSystem g_HandleSys;
22 QHandle *ignore_handle;
24 inline HandleType_t TypeParent(HandleType_t type)
26 return (type & ~HANDLESYS_SUBTYPE_MASK);
29 inline HandleError IdentityHandle(IdentityToken_t *token, unsigned int *index)
31 return g_HandleSys.GetHandle(token->ident, g_ShareSys.GetIdentRoot(), &ignore_handle, index);
34 HandleSystem::HandleSystem()
36 m_Handles = new QHandle[HANDLESYS_MAX_HANDLES + 1];
37 memset(m_Handles, 0, sizeof(QHandle) * (HANDLESYS_MAX_HANDLES + 1));
39 m_Types = new QHandleType[HANDLESYS_TYPEARRAY_SIZE];
40 memset(m_Types, 0, sizeof(QHandleType) * HANDLESYS_TYPEARRAY_SIZE);
42 m_TypeLookup = sm_trie_create();
43 m_strtab = new BaseStringTable(512);
48 HandleSystem::~HandleSystem()
52 sm_trie_destroy(m_TypeLookup);
57 HandleType_t HandleSystem::CreateType(const char *name,
58 IHandleTypeDispatch *dispatch,
60 const TypeAccess *typeAccess,
61 const HandleAccess *hndlAccess,
62 IdentityToken_t *ident,
69 *err = HandleError_Parameter;
74 if (typeAccess && typeAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
78 *err = HandleError_Version;
83 if (hndlAccess && hndlAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
87 *err = HandleError_Version;
97 if (parent & HANDLESYS_SUBTYPE_MASK)
101 *err = HandleError_NoInherit;
105 if (parent >= HANDLESYS_TYPEARRAY_SIZE
106 || m_Types[parent].dispatch == NULL)
110 *err = HandleError_Parameter;
114 if (m_Types[parent].typeSec.access[HTypeAccess_Inherit] == false
115 && (m_Types[parent].typeSec.ident != ident))
119 *err = HandleError_Access;
125 if (name && name[0] != '\0')
127 if (sm_trie_retrieve(m_TypeLookup, name, NULL))
131 *err = HandleError_Parameter;
141 QHandleType *pParent = &m_Types[parent];
142 if (pParent->children >= HANDLESYS_MAX_SUBTYPES)
146 *err = HandleError_Limit;
151 for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++)
153 if (m_Types[parent + i].dispatch == NULL)
163 *err = HandleError_Limit;
169 if (m_FreeTypes == 0)
171 /* Reserve another index */
172 if (m_TypeTail >= HANDLESYS_TYPEARRAY_SIZE)
176 *err = HandleError_Limit;
180 m_TypeTail += (HANDLESYS_MAX_SUBTYPES + 1);
184 /* The "free array" is compacted into the normal array for easiness */
185 index = m_Types[m_FreeTypes--].freeID;
189 QHandleType *pType = &m_Types[index];
191 pType->dispatch = dispatch;
192 if (name && name[0] != '\0')
194 pType->nameIdx = m_strtab->AddString(name);
195 sm_trie_insert(m_TypeLookup, name, (void *)pType);
204 pType->typeSec = *typeAccess;
206 InitAccessDefaults(&pType->typeSec, NULL);
207 pType->typeSec.ident = ident;
212 pType->hndlSec = *hndlAccess;
214 InitAccessDefaults(NULL, &pType->hndlSec);
225 bool HandleSystem::FindHandleType(const char *name, HandleType_t *type)
229 if (!sm_trie_retrieve(m_TypeLookup, name, (void **)&_type))
234 unsigned int offset = _type - m_Types;
244 HandleError HandleSystem::MakePrimHandle(HandleType_t type,
245 QHandle **in_pHandle,
246 unsigned int *in_index,
248 IdentityToken_t *owner,
251 unsigned int owner_index = 0;
253 if (owner && (IdentityHandle(owner, &owner_index) != HandleError_None))
255 return HandleError_Identity;
259 if (m_FreeHandles == 0)
261 if (m_HandleTail >= HANDLESYS_MAX_HANDLES)
263 return HandleError_Limit;;
265 handle = ++m_HandleTail;
267 handle = m_Handles[m_FreeHandles--].freeID;
270 QHandle *pHandle = &m_Handles[handle];
272 assert(pHandle->set == false);
274 if (++m_HSerial >= HANDLESYS_MAX_SERIALS)
279 /* Set essential information */
280 pHandle->set = identity ? HandleSet_Identity : HandleSet_Used;
281 pHandle->refcount = 1;
282 pHandle->type = type;
283 pHandle->serial = m_HSerial;
284 pHandle->owner = owner;
285 pHandle->ch_next = 0;
286 pHandle->access_special = false;
288 /* Create the hash value */
289 Handle_t hash = pHandle->serial;
293 /* Add a reference count to the type */
294 m_Types[type].opened++;
297 *in_pHandle = pHandle;
301 /* Decode the identity token
302 * For now, we don't allow nested ownership
304 if (owner && !identity)
306 QHandle *pIdentity = &m_Handles[owner_index];
307 if (pIdentity->ch_prev == 0)
309 pIdentity->ch_prev = handle;
310 pIdentity->ch_next = handle;
311 pHandle->ch_prev = 0;
313 /* Link previous node to us (forward) */
314 m_Handles[pIdentity->ch_next].ch_next = handle;
315 /* Link us to previous node (backwards) */
316 pHandle->ch_prev = pIdentity->ch_next;
318 pIdentity->ch_next = handle;
320 pIdentity->refcount++;
322 pHandle->ch_prev = 0;
325 return HandleError_None;
328 void HandleSystem::SetTypeSecurityOwner(HandleType_t type, IdentityToken_t *pToken)
331 || type >= HANDLESYS_TYPEARRAY_SIZE
332 || m_Types[type].dispatch == NULL)
337 m_Types[type].typeSec.ident = pToken;
340 Handle_t HandleSystem::CreateHandleInt(HandleType_t type,
342 const HandleSecurity *pSec,
344 const HandleAccess *pAccess,
347 IdentityToken_t *ident;
348 IdentityToken_t *owner;
352 ident = pSec->pIdentity;
353 owner = pSec->pOwner;
360 || type >= HANDLESYS_TYPEARRAY_SIZE
361 || m_Types[type].dispatch == NULL)
365 *err = HandleError_Parameter;
370 /* Check to see if we're allowed to create this handle type */
371 QHandleType *pType = &m_Types[type];
372 if (!pType->typeSec.access[HTypeAccess_Create]
373 && (!pType->typeSec.ident
374 || pType->typeSec.ident != ident))
378 *err = HandleError_Access;
388 if ((_err=MakePrimHandle(type, &pHandle, &index, &handle, owner, identity)) != HandleError_None)
399 pHandle->access_special = true;
400 pHandle->sec = *pAccess;
403 pHandle->object = object;
409 Handle_t HandleSystem::CreateHandleEx(HandleType_t type, void *object, const HandleSecurity *pSec, const HandleAccess *pAccess, HandleError *err)
411 return CreateHandleInt(type, object, pSec, err, pAccess, false);
414 Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityToken_t *owner, IdentityToken_t *ident, HandleError *err)
418 sec.pIdentity = ident;
421 return CreateHandleEx(type, object, &sec, NULL, err);
424 bool HandleSystem::TypeCheck(HandleType_t intype, HandleType_t outtype)
426 /* Check the type inheritance */
427 if (intype & HANDLESYS_SUBTYPE_MASK)
429 if (intype != outtype
430 && (TypeParent(intype) != TypeParent(outtype)))
435 if (intype != outtype)
444 HandleError HandleSystem::GetHandle(Handle_t handle,
445 IdentityToken_t *ident,
446 QHandle **in_pHandle,
447 unsigned int *in_index,
450 unsigned int serial = (handle >> 16);
451 unsigned int index = (handle & HANDLESYS_HANDLE_MASK);
453 if (index == 0 || index == 0xFFFF)
455 return HandleError_Index;
458 QHandle *pHandle = &m_Handles[index];
461 || (pHandle->set == HandleSet_Freed && !ignoreFree))
463 return HandleError_Freed;
464 } else if (pHandle->set == HandleSet_Identity
465 && ident != g_ShareSys.GetIdentRoot())
467 /* Only IdentityHandle() can read this! */
468 return HandleError_Identity;
470 if (pHandle->serial != serial)
472 return HandleError_Changed;
475 *in_pHandle = pHandle;
478 return HandleError_None;
481 bool HandleSystem::CheckAccess(QHandle *pHandle, HandleAccessRight right, const HandleSecurity *pSecurity)
483 QHandleType *pType = &m_Types[pHandle->type];
486 if (pHandle->access_special)
488 access = pHandle->sec.access[right];
490 access = pType->hndlSec.access[right];
493 /* Check if the type's identity matches */
494 if (access & HANDLE_RESTRICT_IDENTITY)
496 IdentityToken_t *owner = pType->typeSec.ident;
498 || (!pSecurity || pSecurity->pIdentity != owner))
504 /* Check if the owner is allowed */
505 if (access & HANDLE_RESTRICT_OWNER)
507 IdentityToken_t *owner = pHandle->owner;
509 && (!pSecurity || pSecurity->pOwner != owner))
518 HandleError HandleSystem::CloneHandle(QHandle *pHandle, unsigned int index, Handle_t *newhandle, IdentityToken_t *newOwner)
520 /* Get a new Handle ID */
521 unsigned int new_index;
526 if ((err=MakePrimHandle(pHandle->type, &pNewHandle, &new_index, &new_handle, newOwner)) != HandleError_None)
531 /* Assign permissions from parent */
532 if (pHandle->access_special)
534 pNewHandle->access_special = true;
535 pNewHandle->sec = pHandle->sec;
538 pNewHandle->clone = index;
541 *newhandle = new_handle;
543 return HandleError_None;
546 HandleError HandleSystem::CloneHandle(Handle_t handle, Handle_t *newhandle, IdentityToken_t *newOwner, const HandleSecurity *pSecurity)
551 IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
553 if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
558 /* Identities cannot be cloned */
559 if (pHandle->set == HandleSet_Identity)
561 return HandleError_Identity;
564 /* Check if the handle can be cloned */
565 if (!CheckAccess(pHandle, HandleAccess_Clone, pSecurity))
567 return HandleError_Access;
570 /* Make sure we're not cloning a clone */
573 QHandle *pParent = &m_Handles[pHandle->clone];
574 return CloneHandle(pParent, pHandle->clone, newhandle, newOwner);
577 return CloneHandle(pHandle, index, newhandle, newOwner);
580 HandleError HandleSystem::FreeHandle(QHandle *pHandle, unsigned int index)
582 QHandleType *pType = &m_Types[pHandle->type];
586 /* If we're a clone, decrease the parent reference count */
590 /* Note that if we ever have per-handle security, we would need to re-check
591 * the access on this Handle. */
592 master = pHandle->clone;
593 pMaster = &m_Handles[master];
595 /* Release the clone now */
596 ReleasePrimHandle(index);
598 /* Decrement the master's reference count */
599 if (--pMaster->refcount == 0)
601 /* Type should be the same but do this anyway... */
602 pType = &m_Types[pMaster->type];
603 pType->dispatch->OnHandleDestroy(pMaster->type, pMaster->object);
604 ReleasePrimHandle(master);
606 } else if (pHandle->set == HandleSet_Identity) {
607 /* If we're an identity, skip all this stuff!
608 * NOTE: SHARESYS DOES NOT CARE ABOUT THE DESTRUCTOR
610 ReleasePrimHandle(index);
612 /* Decrement, free if necessary */
613 if (--pHandle->refcount == 0)
615 pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object);
616 ReleasePrimHandle(index);
618 /* We must be cloned, so mark ourselves as freed */
619 pHandle->set = HandleSet_Freed;
620 /* Now, unlink us, so we're not being tracked by the owner */
623 UnlinkHandleFromOwner(pHandle, index);
628 return HandleError_None;
631 HandleError HandleSystem::FreeHandle(Handle_t handle, const HandleSecurity *pSecurity)
636 IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
638 if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
643 if (!CheckAccess(pHandle, HandleAccess_Delete, pSecurity))
645 return HandleError_Access;
648 return FreeHandle(pHandle, index);
651 HandleError HandleSystem::ReadHandle(Handle_t handle, HandleType_t type, const HandleSecurity *pSecurity, void **object)
656 IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
658 if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
663 if (!CheckAccess(pHandle, HandleAccess_Read, pSecurity))
665 return HandleError_Access;
668 /* Check the type inheritance */
669 if (pHandle->type & HANDLESYS_SUBTYPE_MASK)
671 if (pHandle->type != type
672 && (TypeParent(pHandle->type) != TypeParent(type)))
674 return HandleError_Type;
677 if (pHandle->type != type)
679 return HandleError_Type;
685 /* if we're a clone, the rules change - object is ONLY in our reference */
688 pHandle = &m_Handles[pHandle->clone];
690 *object = pHandle->object;
693 return HandleError_None;
696 void HandleSystem::UnlinkHandleFromOwner(QHandle *pHandle, unsigned int index)
698 /* Unlink us if necessary */
699 unsigned int ident_index;
700 if (IdentityHandle(pHandle->owner, &ident_index) != HandleError_None)
703 assert(pHandle->owner == 0);
706 /* Note that since 0 is an invalid handle, if any of these links are 0,
707 * the data can still be set.
709 QHandle *pIdentity = &m_Handles[ident_index];
711 /* Unlink case: We're the head AND tail node */
712 if (index == pIdentity->ch_prev && index == pIdentity->ch_next)
714 pIdentity->ch_prev = 0;
715 pIdentity->ch_next = 0;
717 /* Unlink case: We're the head node */
718 else if (index == pIdentity->ch_prev) {
719 /* Link us to the next in the chain */
720 pIdentity->ch_prev = pHandle->ch_next;
721 /* Patch up the previous link */
722 m_Handles[pHandle->ch_next].ch_prev = 0;
724 /* Unlink case: We're the tail node */
725 else if (index == pIdentity->ch_next) {
726 /* Link us to the previous in the chain */
727 pIdentity->ch_next = pHandle->ch_prev;
728 /* Patch up the next link */
729 m_Handles[pHandle->ch_prev].ch_next = 0;
731 /* Unlink case: We're in the middle! */
733 /* Patch the forward reference */
734 m_Handles[pHandle->ch_next].ch_prev = pHandle->ch_prev;
735 /* Patch the backward reference */
736 m_Handles[pHandle->ch_prev].ch_next = pHandle->ch_next;
739 /* Lastly, decrease the reference count */
740 pIdentity->refcount--;
743 void HandleSystem::ReleasePrimHandle(unsigned int index)
745 QHandle *pHandle = &m_Handles[index];
746 HandleSet set = pHandle->set;
748 if (pHandle->owner && (set != HandleSet_Identity))
750 UnlinkHandleFromOwner(pHandle, index);
753 /* Were we an identity ourself? */
755 if (set == HandleSet_Identity)
757 /* Extra work to do. We need to find everything connected to this identity and release it. */
758 unsigned int ch_index;
760 unsigned int old_index = 0;
762 while ((ch_index = pHandle->ch_next) != 0)
764 pLocal = &m_Handles[ch_index];
766 assert(old_index != ch_index);
767 assert(pLocal->set == HandleSet_Used);
768 old_index = ch_index;
770 FreeHandle(pLocal, ch_index);
774 pHandle->set = HandleSet_None;
775 m_Types[pHandle->type].opened--;
776 m_Handles[++m_FreeHandles].freeID = index;
779 bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t *ident)
781 if (type == 0 || type >= HANDLESYS_TYPEARRAY_SIZE)
786 QHandleType *pType = &m_Types[type];
788 if (pType->typeSec.ident
789 && pType->typeSec.ident != ident)
794 if (pType->dispatch == NULL)
799 /* Remove children if we have to */
800 if (!(type & HANDLESYS_SUBTYPE_MASK))
802 QHandleType *childType;
803 for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++)
805 childType = &m_Types[type + i];
806 if (childType->dispatch)
808 RemoveType(type + i, childType->typeSec.ident);
811 /* Link us into the free chain */
812 m_Types[++m_FreeTypes].freeID = type;
815 /* Invalidate the type now */
816 IHandleTypeDispatch *dispatch = pType->dispatch;
817 pType->dispatch = NULL;
819 /* Make sure nothing is using this type. */
823 for (unsigned int i=1; i<=m_HandleTail; i++)
825 pHandle = &m_Handles[i];
826 if (!pHandle->set || pHandle->type != type)
833 QHandle *pOther = &m_Handles[pHandle->clone];
834 if (--pOther->refcount == 0)
837 dispatch->OnHandleDestroy(type, pOther->object);
838 ReleasePrimHandle(pHandle->clone);
840 /* Unlink ourselves since we don't have a reference count */
841 ReleasePrimHandle(i);
843 /* If it's not a clone, we still have to check the reference count.
844 * Either way, we'll be destroyed eventually because the handle types do not change.
846 if (--pHandle->refcount == 0)
849 dispatch->OnHandleDestroy(type, pHandle->object);
850 ReleasePrimHandle(i);
853 if (pType->opened == 0)
863 bool HandleSystem::InitAccessDefaults(TypeAccess *pTypeAccess, HandleAccess *pHandleAccess)
867 if (pTypeAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
871 pTypeAccess->access[HTypeAccess_Create] = false;
872 pTypeAccess->access[HTypeAccess_Inherit] = false;
873 pTypeAccess->ident = NULL;
878 if (pHandleAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
883 pHandleAccess->access[HandleAccess_Clone] = 0;
884 pHandleAccess->access[HandleAccess_Delete] = HANDLE_RESTRICT_OWNER;
885 pHandleAccess->access[HandleAccess_Read] = HANDLE_RESTRICT_IDENTITY;