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;
287 /* Create the hash value */
288 Handle_t hash = pHandle->serial;
292 /* Add a reference count to the type */
293 m_Types[type].opened++;
296 *in_pHandle = pHandle;
300 /* Decode the identity token
301 * For now, we don't allow nested ownership
303 if (owner && !identity)
305 QHandle *pIdentity = &m_Handles[owner_index];
306 if (pIdentity->ch_prev == 0)
308 pIdentity->ch_prev = handle;
309 pIdentity->ch_next = handle;
310 pHandle->ch_prev = 0;
312 /* Link previous node to us (forward) */
313 m_Handles[pIdentity->ch_next].ch_next = handle;
314 /* Link us to previous node (backwards) */
315 pHandle->ch_prev = pIdentity->ch_next;
317 pIdentity->ch_next = handle;
319 pIdentity->refcount++;
321 pHandle->ch_prev = 0;
324 return HandleError_None;
327 void HandleSystem::SetTypeSecurityOwner(HandleType_t type, IdentityToken_t *pToken)
330 || type >= HANDLESYS_TYPEARRAY_SIZE
331 || m_Types[type].dispatch == NULL)
336 m_Types[type].typeSec.ident = pToken;
339 Handle_t HandleSystem::CreateHandleEx(HandleType_t type, void *object, IdentityToken_t *owner, IdentityToken_t *ident, HandleError *err, bool identity)
342 || type >= HANDLESYS_TYPEARRAY_SIZE
343 || m_Types[type].dispatch == NULL)
347 *err = HandleError_Parameter;
352 /* Check to see if we're allowed to create this handle type */
353 QHandleType *pType = &m_Types[type];
354 if (!pType->typeSec.access[HTypeAccess_Create]
355 && (!pType->typeSec.ident
356 || pType->typeSec.ident != ident))
360 *err = HandleError_Access;
370 if ((_err=MakePrimHandle(type, &pHandle, &index, &handle, owner, identity)) != HandleError_None)
379 pHandle->object = object;
385 Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityToken_t *owner, IdentityToken_t *ident, HandleError *err)
387 return CreateHandleEx(type, object, owner, ident, err, false);
390 bool HandleSystem::TypeCheck(HandleType_t intype, HandleType_t outtype)
392 /* Check the type inheritance */
393 if (intype & HANDLESYS_SUBTYPE_MASK)
395 if (intype != outtype
396 && (TypeParent(intype) != TypeParent(outtype)))
401 if (intype != outtype)
410 HandleError HandleSystem::GetHandle(Handle_t handle,
411 IdentityToken_t *ident,
412 QHandle **in_pHandle,
413 unsigned int *in_index,
416 unsigned int serial = (handle >> 16);
417 unsigned int index = (handle & HANDLESYS_HANDLE_MASK);
419 if (index == 0 || index == 0xFFFF)
421 return HandleError_Index;
424 QHandle *pHandle = &m_Handles[index];
427 || (pHandle->set == HandleSet_Freed && !ignoreFree))
429 return HandleError_Freed;
430 } else if (pHandle->set == HandleSet_Identity
431 && ident != g_ShareSys.GetIdentRoot())
433 /* Only IdentityHandle() can read this! */
434 return HandleError_Identity;
436 if (pHandle->serial != serial)
438 return HandleError_Changed;
441 *in_pHandle = pHandle;
444 return HandleError_None;
447 bool HandleSystem::CheckAccess(QHandle *pHandle, HandleAccessRight right, const HandleSecurity *pSecurity)
449 QHandleType *pType = &m_Types[pHandle->type];
450 unsigned int access = pType->hndlSec.access[right];
452 /* Check if the type's identity matches */
453 if (access & HANDLE_RESTRICT_IDENTITY)
455 IdentityToken_t *owner = pType->typeSec.ident;
457 || (!pSecurity || pSecurity->pIdentity != owner))
463 /* Check if the owner is allowed */
464 if (access & HANDLE_RESTRICT_OWNER)
466 IdentityToken_t *owner = pHandle->owner;
468 && (!pSecurity || pSecurity->pOwner != owner))
477 HandleError HandleSystem::CloneHandle(QHandle *pHandle, unsigned int index, Handle_t *newhandle, IdentityToken_t *newOwner)
479 /* Get a new Handle ID */
480 unsigned int new_index;
485 if ((err=MakePrimHandle(pHandle->type, &pNewHandle, &new_index, &new_handle, newOwner)) != HandleError_None)
490 pNewHandle->clone = index;
493 *newhandle = new_handle;
495 return HandleError_None;
498 HandleError HandleSystem::CloneHandle(Handle_t handle, Handle_t *newhandle, IdentityToken_t *newOwner, const HandleSecurity *pSecurity)
503 IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
505 if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
510 /* Identities cannot be cloned */
511 if (pHandle->set == HandleSet_Identity)
513 return HandleError_Identity;
516 /* Check if the handle can be cloned */
517 if (!CheckAccess(pHandle, HandleAccess_Clone, pSecurity))
519 return HandleError_Access;
522 /* Make sure we're not cloning a clone */
525 QHandle *pParent = &m_Handles[pHandle->clone];
526 return CloneHandle(pParent, pHandle->clone, newhandle, newOwner);
529 return CloneHandle(pHandle, index, newhandle, newOwner);
532 HandleError HandleSystem::FreeHandle(QHandle *pHandle, unsigned int index)
534 QHandleType *pType = &m_Types[pHandle->type];
538 /* If we're a clone, decrease the parent reference count */
542 /* Note that if we ever have per-handle security, we would need to re-check
543 * the access on this Handle. */
544 master = pHandle->clone;
545 pMaster = &m_Handles[master];
547 /* Release the clone now */
548 ReleasePrimHandle(index);
550 /* Decrement the master's reference count */
551 if (--pMaster->refcount == 0)
553 /* Type should be the same but do this anyway... */
554 pType = &m_Types[pMaster->type];
555 pType->dispatch->OnHandleDestroy(pMaster->type, pMaster->object);
556 ReleasePrimHandle(master);
558 } else if (pHandle->set == HandleSet_Identity) {
559 /* If we're an identity, skip all this stuff!
560 * NOTE: SHARESYS DOES NOT CARE ABOUT THE DESTRUCTOR
562 ReleasePrimHandle(index);
564 /* Decrement, free if necessary */
565 if (--pHandle->refcount == 0)
567 pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object);
568 ReleasePrimHandle(index);
570 /* We must be cloned, so mark ourselves as freed */
571 pHandle->set = HandleSet_Freed;
572 /* Now, unlink us, so we're not being tracked by the owner */
575 UnlinkHandleFromOwner(pHandle, index);
580 return HandleError_None;
583 HandleError HandleSystem::FreeHandle(Handle_t handle, const HandleSecurity *pSecurity)
588 IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
590 if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
595 if (!CheckAccess(pHandle, HandleAccess_Delete, pSecurity))
597 return HandleError_Access;
600 return FreeHandle(pHandle, index);
603 HandleError HandleSystem::ReadHandle(Handle_t handle, HandleType_t type, const HandleSecurity *pSecurity, void **object)
608 IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
610 if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
615 if (!CheckAccess(pHandle, HandleAccess_Read, pSecurity))
617 return HandleError_Access;
620 /* Check the type inheritance */
621 if (pHandle->type & HANDLESYS_SUBTYPE_MASK)
623 if (pHandle->type != type
624 && (TypeParent(pHandle->type) != TypeParent(type)))
626 return HandleError_Type;
629 if (pHandle->type != type)
631 return HandleError_Type;
637 /* if we're a clone, the rules change - object is ONLY in our reference */
640 pHandle = &m_Handles[pHandle->clone];
642 *object = pHandle->object;
645 return HandleError_None;
648 void HandleSystem::UnlinkHandleFromOwner(QHandle *pHandle, unsigned int index)
650 /* Unlink us if necessary */
651 unsigned int ident_index;
652 if (IdentityHandle(pHandle->owner, &ident_index) != HandleError_None)
655 assert(pHandle->owner == 0);
658 /* Note that since 0 is an invalid handle, if any of these links are 0,
659 * the data can still be set.
661 QHandle *pIdentity = &m_Handles[ident_index];
663 /* Unlink case: We're the head AND tail node */
664 if (index == pIdentity->ch_prev && index == pIdentity->ch_next)
666 pIdentity->ch_prev = 0;
667 pIdentity->ch_next = 0;
669 /* Unlink case: We're the head node */
670 else if (index == pIdentity->ch_prev) {
671 /* Link us to the next in the chain */
672 pIdentity->ch_prev = pHandle->ch_next;
673 /* Patch up the previous link */
674 m_Handles[pHandle->ch_next].ch_prev = 0;
676 /* Unlink case: We're the tail node */
677 else if (index == pIdentity->ch_next) {
678 /* Link us to the previous in the chain */
679 pIdentity->ch_next = pHandle->ch_prev;
680 /* Patch up the next link */
681 m_Handles[pHandle->ch_prev].ch_next = 0;
683 /* Unlink case: We're in the middle! */
685 /* Patch the forward reference */
686 m_Handles[pHandle->ch_next].ch_prev = pHandle->ch_prev;
687 /* Patch the backward reference */
688 m_Handles[pHandle->ch_prev].ch_next = pHandle->ch_next;
691 /* Lastly, decrease the reference count */
692 pIdentity->refcount--;
695 void HandleSystem::ReleasePrimHandle(unsigned int index)
697 QHandle *pHandle = &m_Handles[index];
698 HandleSet set = pHandle->set;
700 if (pHandle->owner && (set != HandleSet_Identity))
702 UnlinkHandleFromOwner(pHandle, index);
705 /* Were we an identity ourself? */
707 if (set == HandleSet_Identity)
709 /* Extra work to do. We need to find everything connected to this identity and release it. */
710 unsigned int ch_index;
712 unsigned int old_index = 0;
714 while ((ch_index = pHandle->ch_next) != 0)
716 pLocal = &m_Handles[ch_index];
718 assert(old_index != ch_index);
719 assert(pLocal->set == HandleSet_Used);
720 old_index = ch_index;
722 FreeHandle(pLocal, ch_index);
726 pHandle->set = HandleSet_None;
727 m_Types[pHandle->type].opened--;
728 m_Handles[++m_FreeHandles].freeID = index;
731 bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t *ident)
733 if (type == 0 || type >= HANDLESYS_TYPEARRAY_SIZE)
738 QHandleType *pType = &m_Types[type];
740 if (pType->typeSec.ident
741 && pType->typeSec.ident != ident)
746 if (pType->dispatch == NULL)
751 /* Remove children if we have to */
752 if (!(type & HANDLESYS_SUBTYPE_MASK))
754 QHandleType *childType;
755 for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++)
757 childType = &m_Types[type + i];
758 if (childType->dispatch)
760 RemoveType(type + i, childType->typeSec.ident);
763 /* Link us into the free chain */
764 m_Types[++m_FreeTypes].freeID = type;
767 /* Invalidate the type now */
768 IHandleTypeDispatch *dispatch = pType->dispatch;
769 pType->dispatch = NULL;
771 /* Make sure nothing is using this type. */
775 for (unsigned int i=1; i<=m_HandleTail; i++)
777 pHandle = &m_Handles[i];
778 if (!pHandle->set || pHandle->type != type)
785 QHandle *pOther = &m_Handles[pHandle->clone];
786 if (--pOther->refcount == 0)
789 dispatch->OnHandleDestroy(type, pOther->object);
790 ReleasePrimHandle(pHandle->clone);
792 /* Unlink ourselves since we don't have a reference count */
793 ReleasePrimHandle(i);
795 /* If it's not a clone, we still have to check the reference count.
796 * Either way, we'll be destroyed eventually because the handle types do not change.
798 if (--pHandle->refcount == 0)
801 dispatch->OnHandleDestroy(type, pHandle->object);
802 ReleasePrimHandle(i);
805 if (pType->opened == 0)
815 bool HandleSystem::InitAccessDefaults(TypeAccess *pTypeAccess, HandleAccess *pHandleAccess)
819 if (pTypeAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
823 pTypeAccess->access[HTypeAccess_Create] = false;
824 pTypeAccess->access[HTypeAccess_Inherit] = false;
825 pTypeAccess->ident = NULL;
830 if (pHandleAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
835 pHandleAccess->access[HandleAccess_Clone] = 0;
836 pHandleAccess->access[HandleAccess_Delete] = HANDLE_RESTRICT_OWNER;
837 pHandleAccess->access[HandleAccess_Read] = HANDLE_RESTRICT_IDENTITY;