3 * ===============================================================
4 * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
5 * ===============================================================
7 * This file is not open source and may not be copied without explicit
8 * written permission of AlliedModders LLC. This file may not be redistributed
9 * in whole or significant part.
10 * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
15 #include "HandleSys.h"
17 #include "PluginSys.h"
18 #include "ExtensionSys.h"
22 HandleSystem g_HandleSys;
24 QHandle *ignore_handle;
26 inline HandleType_t TypeParent(HandleType_t type)
28 return (type & ~HANDLESYS_SUBTYPE_MASK);
31 inline HandleError IdentityHandle(IdentityToken_t *token, unsigned int *index)
33 return g_HandleSys.GetHandle(token->ident, g_ShareSys.GetIdentRoot(), &ignore_handle, index);
36 HandleSystem::HandleSystem()
38 m_Handles = new QHandle[HANDLESYS_MAX_HANDLES + 1];
39 memset(m_Handles, 0, sizeof(QHandle) * (HANDLESYS_MAX_HANDLES + 1));
41 m_Types = new QHandleType[HANDLESYS_TYPEARRAY_SIZE];
42 memset(m_Types, 0, sizeof(QHandleType) * HANDLESYS_TYPEARRAY_SIZE);
44 m_TypeLookup = sm_trie_create();
45 m_strtab = new BaseStringTable(512);
50 HandleSystem::~HandleSystem()
54 sm_trie_destroy(m_TypeLookup);
59 HandleType_t HandleSystem::CreateType(const char *name,
60 IHandleTypeDispatch *dispatch,
62 const TypeAccess *typeAccess,
63 const HandleAccess *hndlAccess,
64 IdentityToken_t *ident,
71 *err = HandleError_Parameter;
76 if (typeAccess && typeAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
80 *err = HandleError_Version;
85 if (hndlAccess && hndlAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
89 *err = HandleError_Version;
99 if (parent & HANDLESYS_SUBTYPE_MASK)
103 *err = HandleError_NoInherit;
107 if (parent >= HANDLESYS_TYPEARRAY_SIZE
108 || m_Types[parent].dispatch == NULL)
112 *err = HandleError_Parameter;
116 if (m_Types[parent].typeSec.access[HTypeAccess_Inherit] == false
117 && (m_Types[parent].typeSec.ident != ident))
121 *err = HandleError_Access;
127 if (name && name[0] != '\0')
129 if (sm_trie_retrieve(m_TypeLookup, name, NULL))
133 *err = HandleError_Parameter;
143 QHandleType *pParent = &m_Types[parent];
144 if (pParent->children >= HANDLESYS_MAX_SUBTYPES)
148 *err = HandleError_Limit;
153 for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++)
155 if (m_Types[parent + i].dispatch == NULL)
165 *err = HandleError_Limit;
171 if (m_FreeTypes == 0)
173 /* Reserve another index */
174 if (m_TypeTail >= HANDLESYS_TYPEARRAY_SIZE)
178 *err = HandleError_Limit;
182 m_TypeTail += (HANDLESYS_MAX_SUBTYPES + 1);
186 /* The "free array" is compacted into the normal array for easiness */
187 index = m_Types[m_FreeTypes--].freeID;
191 QHandleType *pType = &m_Types[index];
193 pType->dispatch = dispatch;
194 if (name && name[0] != '\0')
196 pType->nameIdx = m_strtab->AddString(name);
197 sm_trie_insert(m_TypeLookup, name, (void *)pType);
206 pType->typeSec = *typeAccess;
208 InitAccessDefaults(&pType->typeSec, NULL);
209 pType->typeSec.ident = ident;
214 pType->hndlSec = *hndlAccess;
216 InitAccessDefaults(NULL, &pType->hndlSec);
227 bool HandleSystem::FindHandleType(const char *name, HandleType_t *type)
231 if (!sm_trie_retrieve(m_TypeLookup, name, (void **)&_type))
236 unsigned int offset = _type - m_Types;
246 HandleError HandleSystem::MakePrimHandle(HandleType_t type,
247 QHandle **in_pHandle,
248 unsigned int *in_index,
250 IdentityToken_t *owner,
253 unsigned int owner_index = 0;
255 if (owner && (IdentityHandle(owner, &owner_index) != HandleError_None))
257 return HandleError_Identity;
261 if (m_FreeHandles == 0)
263 if (m_HandleTail >= HANDLESYS_MAX_HANDLES)
265 return HandleError_Limit;;
267 handle = ++m_HandleTail;
269 handle = m_Handles[m_FreeHandles--].freeID;
272 QHandle *pHandle = &m_Handles[handle];
274 assert(pHandle->set == false);
276 if (++m_HSerial >= HANDLESYS_MAX_SERIALS)
281 /* Set essential information */
282 pHandle->set = identity ? HandleSet_Identity : HandleSet_Used;
283 pHandle->refcount = 1;
284 pHandle->type = type;
285 pHandle->serial = m_HSerial;
286 pHandle->owner = owner;
287 pHandle->ch_next = 0;
288 pHandle->access_special = false;
289 pHandle->is_destroying = false;
291 /* Create the hash value */
292 Handle_t hash = pHandle->serial;
296 /* Add a reference count to the type */
297 m_Types[type].opened++;
300 *in_pHandle = pHandle;
304 /* Decode the identity token
305 * For now, we don't allow nested ownership
307 if (owner && !identity)
309 QHandle *pIdentity = &m_Handles[owner_index];
310 if (pIdentity->ch_prev == 0)
312 pIdentity->ch_prev = handle;
313 pIdentity->ch_next = handle;
314 pHandle->ch_prev = 0;
316 /* Link previous node to us (forward) */
317 m_Handles[pIdentity->ch_next].ch_next = handle;
318 /* Link us to previous node (backwards) */
319 pHandle->ch_prev = pIdentity->ch_next;
321 pIdentity->ch_next = handle;
323 pIdentity->refcount++;
325 pHandle->ch_prev = 0;
328 return HandleError_None;
331 void HandleSystem::SetTypeSecurityOwner(HandleType_t type, IdentityToken_t *pToken)
334 || type >= HANDLESYS_TYPEARRAY_SIZE
335 || m_Types[type].dispatch == NULL)
340 m_Types[type].typeSec.ident = pToken;
343 Handle_t HandleSystem::CreateHandleInt(HandleType_t type,
345 const HandleSecurity *pSec,
347 const HandleAccess *pAccess,
350 IdentityToken_t *ident;
351 IdentityToken_t *owner;
355 ident = pSec->pIdentity;
356 owner = pSec->pOwner;
363 || type >= HANDLESYS_TYPEARRAY_SIZE
364 || m_Types[type].dispatch == NULL)
368 *err = HandleError_Parameter;
373 /* Check to see if we're allowed to create this handle type */
374 QHandleType *pType = &m_Types[type];
375 if (!pType->typeSec.access[HTypeAccess_Create]
376 && (!pType->typeSec.ident
377 || pType->typeSec.ident != ident))
381 *err = HandleError_Access;
391 if ((_err=MakePrimHandle(type, &pHandle, &index, &handle, owner, identity)) != HandleError_None)
402 pHandle->access_special = true;
403 pHandle->sec = *pAccess;
406 pHandle->object = object;
412 Handle_t HandleSystem::CreateHandleEx(HandleType_t type, void *object, const HandleSecurity *pSec, const HandleAccess *pAccess, HandleError *err)
414 return CreateHandleInt(type, object, pSec, err, pAccess, false);
417 Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityToken_t *owner, IdentityToken_t *ident, HandleError *err)
421 sec.pIdentity = ident;
424 return CreateHandleEx(type, object, &sec, NULL, err);
427 bool HandleSystem::TypeCheck(HandleType_t intype, HandleType_t outtype)
429 /* Check the type inheritance */
430 if (intype & HANDLESYS_SUBTYPE_MASK)
432 if (intype != outtype
433 && (TypeParent(intype) != TypeParent(outtype)))
438 if (intype != outtype)
447 HandleError HandleSystem::GetHandle(Handle_t handle,
448 IdentityToken_t *ident,
449 QHandle **in_pHandle,
450 unsigned int *in_index,
453 unsigned int serial = (handle >> 16);
454 unsigned int index = (handle & HANDLESYS_HANDLE_MASK);
456 if (index == 0 || index == 0xFFFF)
458 return HandleError_Index;
461 QHandle *pHandle = &m_Handles[index];
464 || (pHandle->set == HandleSet_Freed && !ignoreFree))
466 return HandleError_Freed;
467 } else if (pHandle->set == HandleSet_Identity
468 && ident != g_ShareSys.GetIdentRoot())
470 /* Only IdentityHandle() can read this! */
471 return HandleError_Identity;
473 if (pHandle->serial != serial)
475 return HandleError_Changed;
478 *in_pHandle = pHandle;
481 return HandleError_None;
484 bool HandleSystem::CheckAccess(QHandle *pHandle, HandleAccessRight right, const HandleSecurity *pSecurity)
486 QHandleType *pType = &m_Types[pHandle->type];
489 if (pHandle->access_special)
491 access = pHandle->sec.access[right];
493 access = pType->hndlSec.access[right];
496 /* Check if the type's identity matches */
497 if (access & HANDLE_RESTRICT_IDENTITY)
499 IdentityToken_t *owner = pType->typeSec.ident;
501 || (!pSecurity || pSecurity->pIdentity != owner))
507 /* Check if the owner is allowed */
508 if (access & HANDLE_RESTRICT_OWNER)
510 IdentityToken_t *owner = pHandle->owner;
512 && (!pSecurity || pSecurity->pOwner != owner))
521 HandleError HandleSystem::CloneHandle(QHandle *pHandle, unsigned int index, Handle_t *newhandle, IdentityToken_t *newOwner)
523 /* Get a new Handle ID */
524 unsigned int new_index;
529 if ((err=MakePrimHandle(pHandle->type, &pNewHandle, &new_index, &new_handle, newOwner)) != HandleError_None)
534 /* Assign permissions from parent */
535 if (pHandle->access_special)
537 pNewHandle->access_special = true;
538 pNewHandle->sec = pHandle->sec;
541 pNewHandle->clone = index;
544 *newhandle = new_handle;
546 return HandleError_None;
549 HandleError HandleSystem::CloneHandle(Handle_t handle, Handle_t *newhandle, IdentityToken_t *newOwner, const HandleSecurity *pSecurity)
554 IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
556 if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
561 /* Identities cannot be cloned */
562 if (pHandle->set == HandleSet_Identity)
564 return HandleError_Identity;
567 /* Check if the handle can be cloned */
568 if (!CheckAccess(pHandle, HandleAccess_Clone, pSecurity))
570 return HandleError_Access;
573 /* Make sure we're not cloning a clone */
576 QHandle *pParent = &m_Handles[pHandle->clone];
577 return CloneHandle(pParent, pHandle->clone, newhandle, newOwner);
580 return CloneHandle(pHandle, index, newhandle, newOwner);
583 HandleError HandleSystem::FreeHandle(QHandle *pHandle, unsigned int index)
585 QHandleType *pType = &m_Types[pHandle->type];
589 /* If we're a clone, decrease the parent reference count */
593 /* Note that if we ever have per-handle security, we would need to re-check
594 * the access on this Handle. */
595 master = pHandle->clone;
596 pMaster = &m_Handles[master];
598 /* Release the clone now */
599 ReleasePrimHandle(index);
601 /* Decrement the master's reference count */
602 if (--pMaster->refcount == 0)
604 /* Type should be the same but do this anyway... */
605 pType = &m_Types[pMaster->type];
606 pMaster->is_destroying = true;
607 pType->dispatch->OnHandleDestroy(pMaster->type, pMaster->object);
608 ReleasePrimHandle(master);
610 } else if (pHandle->set == HandleSet_Identity) {
611 /* If we're an identity, skip all this stuff!
612 * NOTE: SHARESYS DOES NOT CARE ABOUT THE DESTRUCTOR
614 ReleasePrimHandle(index);
616 /* Decrement, free if necessary */
617 if (--pHandle->refcount == 0)
619 pHandle->is_destroying = true;
620 pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object);
621 ReleasePrimHandle(index);
623 /* We must be cloned, so mark ourselves as freed */
624 pHandle->set = HandleSet_Freed;
625 /* Now, unlink us, so we're not being tracked by the owner */
628 UnlinkHandleFromOwner(pHandle, index);
633 return HandleError_None;
636 HandleError HandleSystem::FreeHandle(Handle_t handle, const HandleSecurity *pSecurity)
641 IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
643 if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
648 if (!CheckAccess(pHandle, HandleAccess_Delete, pSecurity))
650 return HandleError_Access;
653 if (pHandle->is_destroying)
655 /* Someone tried to free this recursively.
656 * We'll just ignore this safely.
658 return HandleError_None;
661 return FreeHandle(pHandle, index);
664 HandleError HandleSystem::ReadHandle(Handle_t handle, HandleType_t type, const HandleSecurity *pSecurity, void **object)
669 IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
671 if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
676 if (!CheckAccess(pHandle, HandleAccess_Read, pSecurity))
678 return HandleError_Access;
681 /* Check the type inheritance */
682 if (pHandle->type & HANDLESYS_SUBTYPE_MASK)
684 if (pHandle->type != type
685 && (TypeParent(pHandle->type) != TypeParent(type)))
687 return HandleError_Type;
690 if (pHandle->type != type)
692 return HandleError_Type;
698 /* if we're a clone, the rules change - object is ONLY in our reference */
701 pHandle = &m_Handles[pHandle->clone];
703 *object = pHandle->object;
706 return HandleError_None;
709 void HandleSystem::UnlinkHandleFromOwner(QHandle *pHandle, unsigned int index)
711 /* Unlink us if necessary */
712 unsigned int ident_index;
713 if (IdentityHandle(pHandle->owner, &ident_index) != HandleError_None)
716 assert(pHandle->owner == 0);
719 /* Note that since 0 is an invalid handle, if any of these links are 0,
720 * the data can still be set.
722 QHandle *pIdentity = &m_Handles[ident_index];
724 /* Unlink case: We're the head AND tail node */
725 if (index == pIdentity->ch_prev && index == pIdentity->ch_next)
727 pIdentity->ch_prev = 0;
728 pIdentity->ch_next = 0;
730 /* Unlink case: We're the head node */
731 else if (index == pIdentity->ch_prev) {
732 /* Link us to the next in the chain */
733 pIdentity->ch_prev = pHandle->ch_next;
734 /* Patch up the previous link */
735 m_Handles[pHandle->ch_next].ch_prev = 0;
737 /* Unlink case: We're the tail node */
738 else if (index == pIdentity->ch_next) {
739 /* Link us to the previous in the chain */
740 pIdentity->ch_next = pHandle->ch_prev;
741 /* Patch up the next link */
742 m_Handles[pHandle->ch_prev].ch_next = 0;
744 /* Unlink case: We're in the middle! */
746 /* Patch the forward reference */
747 m_Handles[pHandle->ch_next].ch_prev = pHandle->ch_prev;
748 /* Patch the backward reference */
749 m_Handles[pHandle->ch_prev].ch_next = pHandle->ch_next;
752 /* Lastly, decrease the reference count */
753 pIdentity->refcount--;
756 void HandleSystem::ReleasePrimHandle(unsigned int index)
758 QHandle *pHandle = &m_Handles[index];
759 HandleSet set = pHandle->set;
761 if (pHandle->owner && (set != HandleSet_Identity))
763 UnlinkHandleFromOwner(pHandle, index);
766 /* Were we an identity ourself? */
768 if (set == HandleSet_Identity)
770 /* Extra work to do. We need to find everything connected to this identity and release it. */
771 unsigned int ch_index;
773 unsigned int old_index = 0;
775 while ((ch_index = pHandle->ch_next) != 0)
777 pLocal = &m_Handles[ch_index];
779 assert(old_index != ch_index);
780 assert(pLocal->set == HandleSet_Used);
781 old_index = ch_index;
783 FreeHandle(pLocal, ch_index);
787 pHandle->set = HandleSet_None;
788 m_Types[pHandle->type].opened--;
789 m_Handles[++m_FreeHandles].freeID = index;
792 bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t *ident)
794 if (type == 0 || type >= HANDLESYS_TYPEARRAY_SIZE)
799 QHandleType *pType = &m_Types[type];
801 if (pType->typeSec.ident
802 && pType->typeSec.ident != ident)
807 if (pType->dispatch == NULL)
812 /* Remove children if we have to */
813 if (!(type & HANDLESYS_SUBTYPE_MASK))
815 QHandleType *childType;
816 for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++)
818 childType = &m_Types[type + i];
819 if (childType->dispatch)
821 RemoveType(type + i, childType->typeSec.ident);
824 /* Link us into the free chain */
825 m_Types[++m_FreeTypes].freeID = type;
828 /* Invalidate the type now */
829 IHandleTypeDispatch *dispatch = pType->dispatch;
830 pType->dispatch = NULL;
832 /* Make sure nothing is using this type. */
836 for (unsigned int i=1; i<=m_HandleTail; i++)
838 pHandle = &m_Handles[i];
839 if (!pHandle->set || pHandle->type != type)
846 QHandle *pOther = &m_Handles[pHandle->clone];
847 if (--pOther->refcount == 0)
850 dispatch->OnHandleDestroy(type, pOther->object);
851 ReleasePrimHandle(pHandle->clone);
853 /* Unlink ourselves since we don't have a reference count */
854 ReleasePrimHandle(i);
856 /* If it's not a clone, we still have to check the reference count.
857 * Either way, we'll be destroyed eventually because the handle types do not change.
859 if (--pHandle->refcount == 0)
862 dispatch->OnHandleDestroy(type, pHandle->object);
863 ReleasePrimHandle(i);
866 if (pType->opened == 0)
876 bool HandleSystem::InitAccessDefaults(TypeAccess *pTypeAccess, HandleAccess *pHandleAccess)
880 if (pTypeAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
884 pTypeAccess->access[HTypeAccess_Create] = false;
885 pTypeAccess->access[HTypeAccess_Inherit] = false;
886 pTypeAccess->ident = NULL;
891 if (pHandleAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
896 pHandleAccess->access[HandleAccess_Clone] = 0;
897 pHandleAccess->access[HandleAccess_Delete] = HANDLE_RESTRICT_OWNER;
898 pHandleAccess->access[HandleAccess_Read] = HANDLE_RESTRICT_IDENTITY;
904 void HandleSystem::Dump(FILE *fp)
906 fprintf(fp, "%-10.10s\t%-20.20s\t%-20.20s\n", "Handle", "Owner", "Type");
907 fprintf(fp, "---------------------------------------------\n");
908 for (unsigned int i=1; i<=m_HandleTail; i++)
910 if (m_Handles[i].set == HandleSet_Freed
911 || m_Handles[i].set == HandleSet_Identity)
916 unsigned int index = (m_Handles[i].serial << 16) | i;
917 /* Determine the owner */
918 const char *owner = "UNKNOWN";
919 if (m_Handles[i].owner)
921 IdentityToken_t *pOwner = m_Handles[i].owner;
922 if (pOwner == g_pCoreIdent)
925 } else if (pOwner == g_PluginSys.GetIdentity()) {
928 CExtension *ext = g_Extensions.GetExtensionFromIdent(pOwner);
931 owner = ext->GetFilename();
933 CPlugin *pPlugin = g_PluginSys.GetPluginFromIdentity(pOwner);
936 owner = pPlugin->GetFilename();
943 const char *type = "ANON";
944 QHandleType *pType = &m_Types[m_Handles[i].type];
945 if (pType->nameIdx != -1)
947 type = m_strtab->GetString(pType->nameIdx);
949 fprintf(fp, "0x%08x\t%-20.20s\t%-20.20s\n", index, owner, type);