ds@1952: /** ds@1952: * vim: set ts=4 : ds@1952: * ============================================================================= ds@1952: * SourceMod ds@1952: * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. ds@1952: * ============================================================================= ds@1952: * ds@1952: * This program is free software; you can redistribute it and/or modify it under ds@1952: * the terms of the GNU General Public License, version 3.0, as published by the ds@1952: * Free Software Foundation. ds@1952: * ds@1952: * This program is distributed in the hope that it will be useful, but WITHOUT ds@1952: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ds@1952: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ds@1952: * details. ds@1952: * ds@1952: * You should have received a copy of the GNU General Public License along with ds@1952: * this program. If not, see . ds@1952: * ds@1952: * As a special exception, AlliedModders LLC gives you permission to link the ds@1952: * code of this program (as well as its derivative works) to "Half-Life 2," the ds@1952: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software ds@1952: * by the Valve Corporation. You must obey the GNU General Public License in ds@1952: * all respects for all other code used. Additionally, AlliedModders LLC grants ds@1952: * this exception to all derivative works. AlliedModders LLC defines further ds@1952: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), ds@1952: * or . ds@1952: * ds@1952: * Version: $Id$ ds@1952: */ ds@1952: ds@1952: #include "HandleSys.h" ds@1952: #include "ShareSys.h" ds@1952: #include "PluginSys.h" ds@1952: #include "ExtensionSys.h" ds@1952: #include "Logger.h" ds@1952: #include ds@1952: #include ds@1952: #include "sm_stringutil.h" ds@1952: ds@1952: HandleSystem g_HandleSys; ds@1952: ds@1952: QHandle *ignore_handle; ds@1952: ds@1952: inline HandleType_t TypeParent(HandleType_t type) ds@1952: { ds@1952: return (type & ~HANDLESYS_SUBTYPE_MASK); ds@1952: } ds@1952: ds@1952: inline HandleError IdentityHandle(IdentityToken_t *token, unsigned int *index) ds@1952: { ds@1952: return g_HandleSys.GetHandle(token->ident, g_ShareSys.GetIdentRoot(), &ignore_handle, index); ds@1952: } ds@1952: ds@1952: HandleSystem::HandleSystem() ds@1952: { ds@1952: m_Handles = new QHandle[HANDLESYS_MAX_HANDLES + 1]; ds@1952: memset(m_Handles, 0, sizeof(QHandle) * (HANDLESYS_MAX_HANDLES + 1)); ds@1952: ds@1952: m_Types = new QHandleType[HANDLESYS_TYPEARRAY_SIZE]; ds@1952: memset(m_Types, 0, sizeof(QHandleType) * HANDLESYS_TYPEARRAY_SIZE); ds@1952: ds@1952: m_TypeLookup = sm_trie_create(); ds@1952: m_strtab = new BaseStringTable(512); ds@1952: ds@1952: m_TypeTail = 0; ds@1952: } ds@1952: ds@1952: HandleSystem::~HandleSystem() ds@1952: { ds@1952: delete [] m_Handles; ds@1952: delete [] m_Types; ds@1952: sm_trie_destroy(m_TypeLookup); ds@1952: delete m_strtab; ds@1952: } ds@1952: ds@1952: ds@1952: HandleType_t HandleSystem::CreateType(const char *name, ds@1952: IHandleTypeDispatch *dispatch, ds@1952: HandleType_t parent, ds@1952: const TypeAccess *typeAccess, ds@1952: const HandleAccess *hndlAccess, ds@1952: IdentityToken_t *ident, ds@1952: HandleError *err) ds@1952: { ds@1952: if (!dispatch) ds@1952: { ds@1952: if (err) ds@1952: { ds@1952: *err = HandleError_Parameter; ds@1952: } ds@1952: return 0; ds@1952: } ds@1952: ds@1952: if (typeAccess && typeAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION) ds@1952: { ds@1952: if (err) ds@1952: { ds@1952: *err = HandleError_Version; ds@1952: } ds@1952: return 0; ds@1952: } ds@1952: ds@1952: if (hndlAccess && hndlAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION) ds@1952: { ds@1952: if (err) ds@1952: { ds@1952: *err = HandleError_Version; ds@1952: } ds@1952: return 0; ds@1952: } ds@1952: ds@1952: bool isChild = false; ds@1952: ds@1952: if (parent != 0) ds@1952: { ds@1952: isChild = true; ds@1952: if (parent & HANDLESYS_SUBTYPE_MASK) ds@1952: { ds@1952: if (err) ds@1952: { ds@1952: *err = HandleError_NoInherit; ds@1952: } ds@1952: return 0; ds@1952: } ds@1952: if (parent >= HANDLESYS_TYPEARRAY_SIZE ds@1952: || m_Types[parent].dispatch == NULL) ds@1952: { ds@1952: if (err) ds@1952: { ds@1952: *err = HandleError_Parameter; ds@1952: } ds@1952: return 0; ds@1952: } ds@1952: if (m_Types[parent].typeSec.access[HTypeAccess_Inherit] == false ds@1952: && (m_Types[parent].typeSec.ident != ident)) ds@1952: { ds@1952: if (err) ds@1952: { ds@1952: *err = HandleError_Access; ds@1952: } ds@1952: return 0; ds@1952: } ds@1952: } ds@1952: ds@1952: if (name && name[0] != '\0') ds@1952: { ds@1952: if (sm_trie_retrieve(m_TypeLookup, name, NULL)) ds@1952: { ds@1952: if (err) ds@1952: { ds@1952: *err = HandleError_Parameter; ds@1952: } ds@1952: return 0; ds@1952: } ds@1952: } ds@1952: ds@1952: unsigned int index; ds@1952: ds@1952: if (isChild) ds@1952: { ds@1952: QHandleType *pParent = &m_Types[parent]; ds@1952: if (pParent->children >= HANDLESYS_MAX_SUBTYPES) ds@1952: { ds@1952: if (err) ds@1952: { ds@1952: *err = HandleError_Limit; ds@1952: } ds@1952: return 0; ds@1952: } ds@1952: index = 0; ds@1952: for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++) ds@1952: { ds@1952: if (m_Types[parent + i].dispatch == NULL) ds@1952: { ds@1952: index = parent + i; ds@1952: break; ds@1952: } ds@1952: } ds@1952: if (!index) ds@1952: { ds@1952: if (err) ds@1952: { ds@1952: *err = HandleError_Limit; ds@1952: } ds@1952: return 0; ds@1952: } ds@1952: pParent->children++; ds@1952: } else { ds@1952: if (m_FreeTypes == 0) ds@1952: { ds@1952: /* Reserve another index */ ds@1952: if (m_TypeTail >= HANDLESYS_TYPEARRAY_SIZE) ds@1952: { ds@1952: if (err) ds@1952: { ds@1952: *err = HandleError_Limit; ds@1952: } ds@1952: return 0; ds@1952: } else { ds@1952: m_TypeTail += (HANDLESYS_MAX_SUBTYPES + 1); ds@1952: index = m_TypeTail; ds@1952: } ds@1952: } else { ds@1952: /* The "free array" is compacted into the normal array for easiness */ ds@1952: index = m_Types[m_FreeTypes--].freeID; ds@1952: } ds@1952: } ds@1952: ds@1952: QHandleType *pType = &m_Types[index]; ds@1952: ds@1952: pType->dispatch = dispatch; ds@1952: if (name && name[0] != '\0') ds@1952: { ds@1952: pType->nameIdx = m_strtab->AddString(name); ds@1952: sm_trie_insert(m_TypeLookup, name, (void *)pType); ds@1952: } else { ds@1952: pType->nameIdx = -1; ds@1952: } ds@1952: ds@1952: pType->opened = 0; ds@1952: ds@1952: if (typeAccess) ds@1952: { ds@1952: pType->typeSec = *typeAccess; ds@1952: } else { ds@1952: InitAccessDefaults(&pType->typeSec, NULL); ds@1952: pType->typeSec.ident = ident; ds@1952: } ds@1952: ds@1952: if (hndlAccess) ds@1952: { ds@1952: pType->hndlSec = *hndlAccess; ds@1952: } else { ds@1952: InitAccessDefaults(NULL, &pType->hndlSec); ds@1952: } ds@1952: ds@1952: if (!isChild) ds@1952: { ds@1952: pType->children = 0; ds@1952: } ds@1952: ds@1952: return index; ds@1952: } ds@1952: ds@1952: bool HandleSystem::FindHandleType(const char *name, HandleType_t *type) ds@1952: { ds@1952: QHandleType *_type; ds@1952: ds@1952: if (!sm_trie_retrieve(m_TypeLookup, name, (void **)&_type)) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: unsigned int offset = _type - m_Types; ds@1952: ds@1952: if (type) ds@1952: { ds@1952: *type = offset; ds@1952: } ds@1952: ds@1952: return true; ds@1952: } ds@1952: ds@1952: HandleError HandleSystem::TryAllocHandle(unsigned int *handle) ds@1952: { ds@1952: if (m_FreeHandles == 0) ds@1952: { ds@1952: if (m_HandleTail >= HANDLESYS_MAX_HANDLES) ds@1952: { ds@1952: return HandleError_Limit;; ds@1952: } ds@1952: *handle = ++m_HandleTail; ds@1952: } ds@1952: else ds@1952: { ds@1952: *handle = m_Handles[m_FreeHandles--].freeID; ds@1952: } ds@1952: ds@1952: return HandleError_None; ds@1952: } ds@1952: ds@1952: HandleError HandleSystem::MakePrimHandle(HandleType_t type, ds@1952: QHandle **in_pHandle, ds@1952: unsigned int *in_index, ds@1952: Handle_t *in_handle, ds@1952: IdentityToken_t *owner, ds@1952: bool identity) ds@1952: { ds@1952: HandleError err; ds@1952: unsigned int owner_index = 0; ds@1952: ds@1952: if (owner && (IdentityHandle(owner, &owner_index) != HandleError_None)) ds@1952: { ds@1952: return HandleError_Identity; ds@1952: } ds@1952: ds@1952: unsigned int handle; ds@1952: if ((err = TryAllocHandle(&handle)) != HandleError_None) ds@1952: { ds@1952: if (!TryAndFreeSomeHandles() ds@1952: || (err = TryAllocHandle(&handle)) != HandleError_None) ds@1952: { ds@1952: return err; ds@1952: } ds@1952: } ds@1952: ds@1952: QHandle *pHandle = &m_Handles[handle]; ds@1952: ds@1952: assert(pHandle->set == false); ds@1952: ds@1952: if (++m_HSerial >= HANDLESYS_MAX_SERIALS) ds@1952: { ds@1952: m_HSerial = 1; ds@1952: } ds@1952: ds@1952: /* Set essential information */ ds@1952: pHandle->set = identity ? HandleSet_Identity : HandleSet_Used; ds@1952: pHandle->refcount = 1; ds@1952: pHandle->type = type; ds@1952: pHandle->serial = m_HSerial; ds@1952: pHandle->owner = owner; ds@1952: pHandle->ch_next = 0; ds@1952: pHandle->access_special = false; ds@1952: pHandle->is_destroying = false; ds@1952: ds@1952: /* Create the hash value */ ds@1952: Handle_t hash = pHandle->serial; ds@1952: hash <<= 16; ds@1952: hash |= handle; ds@1952: ds@1952: /* Add a reference count to the type */ ds@1952: m_Types[type].opened++; ds@1952: ds@1952: /* Output */ ds@1952: *in_pHandle = pHandle; ds@1952: *in_index = handle; ds@1952: *in_handle = hash; ds@1952: ds@1952: /* Decode the identity token ds@1952: * For now, we don't allow nested ownership ds@1952: */ ds@1952: if (owner && !identity) ds@1952: { ds@1952: QHandle *pIdentity = &m_Handles[owner_index]; ds@1952: if (pIdentity->ch_prev == 0) ds@1952: { ds@1952: pIdentity->ch_prev = handle; ds@1952: pIdentity->ch_next = handle; ds@1952: pHandle->ch_prev = 0; ds@1952: } ds@1952: else ds@1952: { ds@1952: /* Link previous node to us (forward) */ ds@1952: m_Handles[pIdentity->ch_next].ch_next = handle; ds@1952: /* Link us to previous node (backwards) */ ds@1952: pHandle->ch_prev = pIdentity->ch_next; ds@1952: /* Set new tail */ ds@1952: pIdentity->ch_next = handle; ds@1952: } ds@1952: pIdentity->refcount++; ds@1952: } ds@1952: else ds@1952: { ds@1952: pHandle->ch_prev = 0; ds@1952: } ds@1952: ds@1952: return HandleError_None; ds@1952: } ds@1952: ds@1952: void HandleSystem::SetTypeSecurityOwner(HandleType_t type, IdentityToken_t *pToken) ds@1952: { ds@1952: if (!type ds@1952: || type >= HANDLESYS_TYPEARRAY_SIZE ds@1952: || m_Types[type].dispatch == NULL) ds@1952: { ds@1952: return; ds@1952: } ds@1952: ds@1952: m_Types[type].typeSec.ident = pToken; ds@1952: } ds@1952: ds@1952: Handle_t HandleSystem::CreateHandleInt(HandleType_t type, ds@1952: void *object, ds@1952: const HandleSecurity *pSec, ds@1952: HandleError *err, ds@1952: const HandleAccess *pAccess, ds@1952: bool identity) ds@1952: { ds@1952: IdentityToken_t *ident; ds@1952: IdentityToken_t *owner; ds@1952: ds@1952: if (pSec) ds@1952: { ds@1952: ident = pSec->pIdentity; ds@1952: owner = pSec->pOwner; ds@1952: } else { ds@1952: ident = NULL; ds@1952: owner = NULL; ds@1952: } ds@1952: ds@1952: if (!type ds@1952: || type >= HANDLESYS_TYPEARRAY_SIZE ds@1952: || m_Types[type].dispatch == NULL) ds@1952: { ds@1952: if (err) ds@1952: { ds@1952: *err = HandleError_Parameter; ds@1952: } ds@1952: return 0; ds@1952: } ds@1952: ds@1952: /* Check to see if we're allowed to create this handle type */ ds@1952: QHandleType *pType = &m_Types[type]; ds@1952: if (!pType->typeSec.access[HTypeAccess_Create] ds@1952: && (!pType->typeSec.ident ds@1952: || pType->typeSec.ident != ident)) ds@1952: { ds@1952: if (err) ds@1952: { ds@1952: *err = HandleError_Access; ds@1952: } ds@1952: return 0; ds@1952: } ds@1952: ds@1952: unsigned int index; ds@1952: Handle_t handle; ds@1952: QHandle *pHandle; ds@1952: HandleError _err; ds@1952: ds@1952: if ((_err=MakePrimHandle(type, &pHandle, &index, &handle, owner, identity)) != HandleError_None) ds@1952: { ds@1952: if (err) ds@1952: { ds@1952: *err = _err; ds@1952: } ds@1952: return 0; ds@1952: } ds@1952: ds@1952: if (pAccess) ds@1952: { ds@1952: pHandle->access_special = true; ds@1952: pHandle->sec = *pAccess; ds@1952: } ds@1952: ds@1952: pHandle->object = object; ds@1952: pHandle->clone = 0; ds@1952: ds@1952: return handle; ds@1952: } ds@1952: ds@1952: Handle_t HandleSystem::CreateHandleEx(HandleType_t type, void *object, const HandleSecurity *pSec, const HandleAccess *pAccess, HandleError *err) ds@1952: { ds@1952: return CreateHandleInt(type, object, pSec, err, pAccess, false); ds@1952: } ds@1952: ds@1952: Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityToken_t *owner, IdentityToken_t *ident, HandleError *err) ds@1952: { ds@1952: HandleSecurity sec; ds@1952: ds@1952: sec.pIdentity = ident; ds@1952: sec.pOwner = owner; ds@1952: ds@1952: return CreateHandleEx(type, object, &sec, NULL, err); ds@1952: } ds@1952: ds@1952: bool HandleSystem::TypeCheck(HandleType_t intype, HandleType_t outtype) ds@1952: { ds@1952: /* Check the type inheritance */ ds@1952: if (intype & HANDLESYS_SUBTYPE_MASK) ds@1952: { ds@1952: if (intype != outtype ds@1952: && (TypeParent(intype) != TypeParent(outtype))) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: } else { ds@1952: if (intype != outtype) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: } ds@1952: ds@1952: return true; ds@1952: } ds@1952: ds@1952: HandleError HandleSystem::GetHandle(Handle_t handle, ds@1952: IdentityToken_t *ident, ds@1952: QHandle **in_pHandle, ds@1952: unsigned int *in_index, ds@1952: bool ignoreFree) ds@1952: { ds@1952: unsigned int serial = (handle >> 16); ds@1952: unsigned int index = (handle & HANDLESYS_HANDLE_MASK); ds@1952: ds@1952: if (index == 0 || index == 0xFFFF) ds@1952: { ds@1952: return HandleError_Index; ds@1952: } ds@1952: ds@1952: QHandle *pHandle = &m_Handles[index]; ds@1952: ds@1952: if (!pHandle->set ds@1952: || (pHandle->set == HandleSet_Freed && !ignoreFree)) ds@1952: { ds@1952: return HandleError_Freed; ds@1952: } else if (pHandle->set == HandleSet_Identity ds@1952: && ident != g_ShareSys.GetIdentRoot()) ds@1952: { ds@1952: /* Only IdentityHandle() can read this! */ ds@1952: return HandleError_Identity; ds@1952: } ds@1952: if (pHandle->serial != serial) ds@1952: { ds@1952: return HandleError_Changed; ds@1952: } ds@1952: ds@1952: *in_pHandle = pHandle; ds@1952: *in_index = index; ds@1952: ds@1952: return HandleError_None; ds@1952: } ds@1952: ds@1952: bool HandleSystem::CheckAccess(QHandle *pHandle, HandleAccessRight right, const HandleSecurity *pSecurity) ds@1952: { ds@1952: QHandleType *pType = &m_Types[pHandle->type]; ds@1952: unsigned int access; ds@1952: ds@1952: if (pHandle->access_special) ds@1952: { ds@1952: access = pHandle->sec.access[right]; ds@1952: } else { ds@1952: access = pType->hndlSec.access[right]; ds@1952: } ds@1952: ds@1952: /* Check if the type's identity matches */ ds@1952: if (access & HANDLE_RESTRICT_IDENTITY) ds@1952: { ds@1952: IdentityToken_t *owner = pType->typeSec.ident; ds@1952: if (!owner ds@1952: || (!pSecurity || pSecurity->pIdentity != owner)) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: } ds@1952: ds@1952: /* Check if the owner is allowed */ ds@1952: if (access & HANDLE_RESTRICT_OWNER) ds@1952: { ds@1952: IdentityToken_t *owner = pHandle->owner; ds@1952: if (owner ds@1952: && (!pSecurity || pSecurity->pOwner != owner)) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: } ds@1952: ds@1952: return true; ds@1952: } ds@1952: ds@1952: HandleError HandleSystem::CloneHandle(QHandle *pHandle, unsigned int index, Handle_t *newhandle, IdentityToken_t *newOwner) ds@1952: { ds@1952: /* Get a new Handle ID */ ds@1952: unsigned int new_index; ds@1952: QHandle *pNewHandle; ds@1952: Handle_t new_handle; ds@1952: HandleError err; ds@1952: ds@1952: if ((err=MakePrimHandle(pHandle->type, &pNewHandle, &new_index, &new_handle, newOwner)) != HandleError_None) ds@1952: { ds@1952: return err; ds@1952: } ds@1952: ds@1952: /* Assign permissions from parent */ ds@1952: if (pHandle->access_special) ds@1952: { ds@1952: pNewHandle->access_special = true; ds@1952: pNewHandle->sec = pHandle->sec; ds@1952: } ds@1952: ds@1952: pNewHandle->clone = index; ds@1952: pHandle->refcount++; ds@1952: ds@1952: *newhandle = new_handle; ds@1952: ds@1952: return HandleError_None; ds@1952: } ds@1952: ds@1952: HandleError HandleSystem::CloneHandle(Handle_t handle, Handle_t *newhandle, IdentityToken_t *newOwner, const HandleSecurity *pSecurity) ds@1952: { ds@1952: HandleError err; ds@1952: QHandle *pHandle; ds@1952: unsigned int index; ds@1952: IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL; ds@1952: ds@1952: if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None) ds@1952: { ds@1952: return err; ds@1952: } ds@1952: ds@1952: /* Identities cannot be cloned */ ds@1952: if (pHandle->set == HandleSet_Identity) ds@1952: { ds@1952: return HandleError_Identity; ds@1952: } ds@1952: ds@1952: /* Check if the handle can be cloned */ ds@1952: if (!CheckAccess(pHandle, HandleAccess_Clone, pSecurity)) ds@1952: { ds@1952: return HandleError_Access; ds@1952: } ds@1952: ds@1952: /* Make sure we're not cloning a clone */ ds@1952: if (pHandle->clone) ds@1952: { ds@1952: QHandle *pParent = &m_Handles[pHandle->clone]; ds@1952: return CloneHandle(pParent, pHandle->clone, newhandle, newOwner); ds@1952: } ds@1952: ds@1952: return CloneHandle(pHandle, index, newhandle, newOwner); ds@1952: } ds@1952: ds@1952: HandleError HandleSystem::FreeHandle(QHandle *pHandle, unsigned int index) ds@1952: { ds@1952: QHandleType *pType = &m_Types[pHandle->type]; ds@1952: ds@1952: if (pHandle->clone) ds@1952: { ds@1952: /* If we're a clone, decrease the parent reference count */ ds@1952: QHandle *pMaster; ds@1952: unsigned int master; ds@1952: ds@1952: /* Note that if we ever have per-handle security, we would need to re-check ds@1952: * the access on this Handle. */ ds@1952: master = pHandle->clone; ds@1952: pMaster = &m_Handles[master]; ds@1952: ds@1952: /* Release the clone now */ ds@1952: ReleasePrimHandle(index); ds@1952: ds@1952: /* Decrement the master's reference count */ ds@1952: if (--pMaster->refcount == 0) ds@1952: { ds@1952: /* Type should be the same but do this anyway... */ ds@1952: pType = &m_Types[pMaster->type]; ds@1952: pMaster->is_destroying = true; ds@1952: pType->dispatch->OnHandleDestroy(pMaster->type, pMaster->object); ds@1952: ReleasePrimHandle(master); ds@1952: } ds@1952: } else if (pHandle->set == HandleSet_Identity) { ds@1952: /* If we're an identity, skip all this stuff! ds@1952: * NOTE: SHARESYS DOES NOT CARE ABOUT THE DESTRUCTOR ds@1952: */ ds@1952: ReleasePrimHandle(index); ds@1952: } else { ds@1952: /* Decrement, free if necessary */ ds@1952: if (--pHandle->refcount == 0) ds@1952: { ds@1952: pHandle->is_destroying = true; ds@1952: pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object); ds@1952: ReleasePrimHandle(index); ds@1952: } else { ds@1952: /* We must be cloned, so mark ourselves as freed */ ds@1952: pHandle->set = HandleSet_Freed; ds@1952: /* Now, unlink us, so we're not being tracked by the owner */ ds@1952: if (pHandle->owner) ds@1952: { ds@1952: UnlinkHandleFromOwner(pHandle, index); ds@1952: } ds@1952: } ds@1952: } ds@1952: ds@1952: return HandleError_None; ds@1952: } ds@1952: ds@1952: HandleError HandleSystem::FreeHandle(Handle_t handle, const HandleSecurity *pSecurity) ds@1952: { ds@1952: unsigned int index; ds@1952: QHandle *pHandle; ds@1952: HandleError err; ds@1952: IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL; ds@1952: ds@1952: if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None) ds@1952: { ds@1952: return err; ds@1952: } ds@1952: ds@1952: if (!CheckAccess(pHandle, HandleAccess_Delete, pSecurity)) ds@1952: { ds@1952: return HandleError_Access; ds@1952: } ds@1952: ds@1952: if (pHandle->is_destroying) ds@1952: { ds@1952: /* Someone tried to free this recursively. ds@1952: * We'll just ignore this safely. ds@1952: */ ds@1952: return HandleError_None; ds@1952: } ds@1952: ds@1952: return FreeHandle(pHandle, index); ds@1952: } ds@1952: ds@1952: HandleError HandleSystem::ReadHandle(Handle_t handle, HandleType_t type, const HandleSecurity *pSecurity, void **object) ds@1952: { ds@1952: unsigned int index; ds@1952: QHandle *pHandle; ds@1952: HandleError err; ds@1952: IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL; ds@1952: ds@1952: if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None) ds@1952: { ds@1952: return err; ds@1952: } ds@1952: ds@1952: if (!CheckAccess(pHandle, HandleAccess_Read, pSecurity)) ds@1952: { ds@1952: return HandleError_Access; ds@1952: } ds@1952: ds@1952: /* Check the type inheritance */ ds@1952: if (pHandle->type & HANDLESYS_SUBTYPE_MASK) ds@1952: { ds@1952: if (pHandle->type != type ds@1952: && (TypeParent(pHandle->type) != TypeParent(type))) ds@1952: { ds@1952: return HandleError_Type; ds@1952: } ds@1952: } else if (type) { ds@1952: if (pHandle->type != type) ds@1952: { ds@1952: return HandleError_Type; ds@1952: } ds@1952: } ds@1952: ds@1952: if (object) ds@1952: { ds@1952: /* if we're a clone, the rules change - object is ONLY in our reference */ ds@1952: if (pHandle->clone) ds@1952: { ds@1952: pHandle = &m_Handles[pHandle->clone]; ds@1952: } ds@1952: *object = pHandle->object; ds@1952: } ds@1952: ds@1952: return HandleError_None; ds@1952: } ds@1952: ds@1952: void HandleSystem::UnlinkHandleFromOwner(QHandle *pHandle, unsigned int index) ds@1952: { ds@1952: /* Unlink us if necessary */ ds@1952: unsigned int ident_index; ds@1952: if (IdentityHandle(pHandle->owner, &ident_index) != HandleError_None) ds@1952: { ds@1952: /* Uh-oh! */ ds@1952: assert(pHandle->owner == 0); ds@1952: return; ds@1952: } ds@1952: /* Note that since 0 is an invalid handle, if any of these links are 0, ds@1952: * the data can still be set. ds@1952: */ ds@1952: QHandle *pIdentity = &m_Handles[ident_index]; ds@1952: ds@1952: /* Unlink case: We're the head AND tail node */ ds@1952: if (index == pIdentity->ch_prev && index == pIdentity->ch_next) ds@1952: { ds@1952: pIdentity->ch_prev = 0; ds@1952: pIdentity->ch_next = 0; ds@1952: } ds@1952: /* Unlink case: We're the head node */ ds@1952: else if (index == pIdentity->ch_prev) { ds@1952: /* Link us to the next in the chain */ ds@1952: pIdentity->ch_prev = pHandle->ch_next; ds@1952: /* Patch up the previous link */ ds@1952: m_Handles[pHandle->ch_next].ch_prev = 0; ds@1952: } ds@1952: /* Unlink case: We're the tail node */ ds@1952: else if (index == pIdentity->ch_next) { ds@1952: /* Link us to the previous in the chain */ ds@1952: pIdentity->ch_next = pHandle->ch_prev; ds@1952: /* Patch up the next link */ ds@1952: m_Handles[pHandle->ch_prev].ch_next = 0; ds@1952: } ds@1952: /* Unlink case: We're in the middle! */ ds@1952: else { ds@1952: /* Patch the forward reference */ ds@1952: m_Handles[pHandle->ch_next].ch_prev = pHandle->ch_prev; ds@1952: /* Patch the backward reference */ ds@1952: m_Handles[pHandle->ch_prev].ch_next = pHandle->ch_next; ds@1952: } ds@1952: ds@1952: /* Lastly, decrease the reference count */ ds@1952: pIdentity->refcount--; ds@1952: } ds@1952: ds@1952: void HandleSystem::ReleasePrimHandle(unsigned int index) ds@1952: { ds@1952: QHandle *pHandle = &m_Handles[index]; ds@1952: HandleSet set = pHandle->set; ds@1952: ds@1952: if (pHandle->owner && (set != HandleSet_Identity)) ds@1952: { ds@1952: UnlinkHandleFromOwner(pHandle, index); ds@1952: } ds@1952: ds@1952: /* Were we an identity ourself? */ ds@1952: QHandle *pLocal; ds@1952: if (set == HandleSet_Identity) ds@1952: { ds@1952: /* Extra work to do. We need to find everything connected to this identity and release it. */ ds@1952: unsigned int ch_index; ds@1952: #if defined _DEBUG ds@1952: unsigned int old_index = 0; ds@1952: #endif ds@1952: while ((ch_index = pHandle->ch_next) != 0) ds@1952: { ds@1952: pLocal = &m_Handles[ch_index]; ds@1952: #if defined _DEBUG ds@1952: assert(old_index != ch_index); ds@1952: assert(pLocal->set == HandleSet_Used); ds@1952: old_index = ch_index; ds@1952: #endif ds@1952: FreeHandle(pLocal, ch_index); ds@1952: } ds@1952: } ds@1952: ds@1952: pHandle->set = HandleSet_None; ds@1952: m_Types[pHandle->type].opened--; ds@1952: m_Handles[++m_FreeHandles].freeID = index; ds@1952: } ds@1952: ds@1952: bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t *ident) ds@1952: { ds@1952: if (type == 0 || type >= HANDLESYS_TYPEARRAY_SIZE) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: QHandleType *pType = &m_Types[type]; ds@1952: ds@1952: if (pType->typeSec.ident ds@1952: && pType->typeSec.ident != ident) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: if (pType->dispatch == NULL) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: /* Remove children if we have to */ ds@1952: if (!(type & HANDLESYS_SUBTYPE_MASK)) ds@1952: { ds@1952: QHandleType *childType; ds@1952: for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++) ds@1952: { ds@1952: childType = &m_Types[type + i]; ds@1952: if (childType->dispatch) ds@1952: { ds@1952: RemoveType(type + i, childType->typeSec.ident); ds@1952: } ds@1952: } ds@1952: /* Link us into the free chain */ ds@1952: m_Types[++m_FreeTypes].freeID = type; ds@1952: } ds@1952: ds@1952: /* Invalidate the type now */ ds@1952: IHandleTypeDispatch *dispatch = pType->dispatch; ds@1952: pType->dispatch = NULL; ds@1952: ds@1952: /* Make sure nothing is using this type. */ ds@1952: if (pType->opened) ds@1952: { ds@1952: QHandle *pHandle; ds@1952: for (unsigned int i=1; i<=m_HandleTail; i++) ds@1952: { ds@1952: pHandle = &m_Handles[i]; ds@1952: if (!pHandle->set || pHandle->type != type) ds@1952: { ds@1952: continue; ds@1952: } ds@1952: if (pHandle->clone) ds@1952: { ds@1952: /* Get parent */ ds@1952: QHandle *pOther = &m_Handles[pHandle->clone]; ds@1952: if (--pOther->refcount == 0) ds@1952: { ds@1952: /* Free! */ ds@1952: dispatch->OnHandleDestroy(type, pOther->object); ds@1952: ReleasePrimHandle(pHandle->clone); ds@1952: } ds@1952: /* Unlink ourselves since we don't have a reference count */ ds@1952: ReleasePrimHandle(i); ds@1952: } else { ds@1952: /* If it's not a clone, we still have to check the reference count. ds@1952: * Either way, we'll be destroyed eventually because the handle types do not change. ds@1952: */ ds@1952: if (--pHandle->refcount == 0) ds@1952: { ds@1952: /* Free! */ ds@1952: dispatch->OnHandleDestroy(type, pHandle->object); ds@1952: ReleasePrimHandle(i); ds@1952: } ds@1952: } ds@1952: if (pType->opened == 0) ds@1952: { ds@1952: break; ds@1952: } ds@1952: } ds@1952: } ds@1952: ds@1952: return true; ds@1952: } ds@1952: ds@1952: bool HandleSystem::InitAccessDefaults(TypeAccess *pTypeAccess, HandleAccess *pHandleAccess) ds@1952: { ds@1952: if (pTypeAccess) ds@1952: { ds@1952: if (pTypeAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: pTypeAccess->access[HTypeAccess_Create] = false; ds@1952: pTypeAccess->access[HTypeAccess_Inherit] = false; ds@1952: pTypeAccess->ident = NULL; ds@1952: } ds@1952: ds@1952: if (pHandleAccess) ds@1952: { ds@1952: if (pHandleAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: pHandleAccess->access[HandleAccess_Clone] = 0; ds@1952: pHandleAccess->access[HandleAccess_Delete] = HANDLE_RESTRICT_OWNER; ds@1952: pHandleAccess->access[HandleAccess_Read] = HANDLE_RESTRICT_IDENTITY; ds@1952: } ds@1952: ds@1952: return true; ds@1952: } ds@1952: ds@1952: bool HandleSystem::TryAndFreeSomeHandles() ds@1952: { ds@1952: IPluginIterator *pl_iter = g_PluginSys.GetPluginIterator(); ds@1952: IPlugin *highest_owner = NULL; ds@1952: unsigned int highest_handle_count = 0; ds@1952: ds@1952: /* Search all plugins */ ds@1952: while (pl_iter->MorePlugins()) ds@1952: { ds@1952: IPlugin *plugin = pl_iter->GetPlugin(); ds@1952: IdentityToken_t *identity = plugin->GetIdentity(); ds@1952: unsigned int handle_count = 0; ds@1952: ds@1952: if (identity == NULL) ds@1952: { ds@1952: continue; ds@1952: } ds@1952: ds@1952: /* Search all handles */ ds@1952: for (unsigned int i = 1; i <= m_HandleTail; i++) ds@1952: { ds@1952: if (m_Handles[i].set != HandleSet_Used) ds@1952: { ds@1952: continue; ds@1952: } ds@1952: if (m_Handles[i].owner == identity) ds@1952: { ds@1952: handle_count++; ds@1952: } ds@1952: } ds@1952: ds@1952: if (handle_count > highest_handle_count) ds@1952: { ds@1952: highest_owner = plugin; ds@1952: highest_handle_count = handle_count; ds@1952: } ds@1952: ds@1952: pl_iter->NextPlugin(); ds@1952: } ds@1952: ds@1952: if (highest_owner == NULL || highest_handle_count == 0) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: g_Logger.LogFatal("[SM] MEMORY LEAK DETECTED IN PLUGIN (file \"%s\")", highest_owner->GetFilename()); ds@1952: g_Logger.LogFatal("[SM] Reloading plugin to free %d handles.", highest_handle_count); ds@1952: g_Logger.LogFatal("[SM] Contact the author(s) of this plugin to correct this error.", highest_handle_count); ds@1952: ds@1952: highest_owner->GetContext()->n_err = SP_ERROR_MEMACCESS; ds@1952: ds@1952: return g_PluginSys.UnloadPlugin(highest_owner); ds@1952: } ds@1952: ds@1952: void HandleSystem::Dump(HANDLE_REPORTER rep) ds@1952: { ds@1952: unsigned int total_size = 0; ds@1952: rep("%-10.10s\t%-20.20s\t%-20.20s\t%-10.10s", "Handle", "Owner", "Type", "Memory"); ds@1952: rep("--------------------------------------------------------------------------"); ds@1952: for (unsigned int i = 1; i <= m_HandleTail; i++) ds@1952: { ds@1952: if (m_Handles[i].set != HandleSet_Used) ds@1952: { ds@1952: continue; ds@1952: } ds@1952: /* Get the index */ ds@1952: unsigned int index = (m_Handles[i].serial << 16) | i; ds@1952: /* Determine the owner */ ds@1952: const char *owner = "UNKNOWN"; ds@1952: if (m_Handles[i].owner) ds@1952: { ds@1952: IdentityToken_t *pOwner = m_Handles[i].owner; ds@1952: if (pOwner == g_pCoreIdent) ds@1952: { ds@1952: owner = "CORE"; ds@1952: } ds@1952: else if (pOwner == g_PluginSys.GetIdentity()) ds@1952: { ds@1952: owner = "PLUGINSYS"; ds@1952: } ds@1952: else ds@1952: { ds@1952: CExtension *ext = g_Extensions.GetExtensionFromIdent(pOwner); ds@1952: if (ext) ds@1952: { ds@1952: owner = ext->GetFilename(); ds@1952: } ds@1952: else ds@1952: { ds@1952: CPlugin *pPlugin = g_PluginSys.GetPluginFromIdentity(pOwner); ds@1952: if (pPlugin) ds@1952: { ds@1952: owner = pPlugin->GetFilename(); ds@1952: } ds@1952: } ds@1952: } ds@1952: } ds@1952: else ds@1952: { ds@1952: owner = "NONE"; ds@1952: } ds@1952: const char *type = "ANON"; ds@1952: QHandleType *pType = &m_Types[m_Handles[i].type]; ds@1952: unsigned int size = 0; ds@1952: if (pType->nameIdx != -1) ds@1952: { ds@1952: type = m_strtab->GetString(pType->nameIdx); ds@1952: } ds@1952: if (pType->dispatch->GetDispatchVersion() < HANDLESYS_MEMUSAGE_MIN_VERSION ds@1952: || !pType->dispatch->GetHandleApproxSize(m_Handles[i].type, m_Handles[i].object, &size)) ds@1952: { ds@1952: rep("0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, "-1"); ds@1952: } ds@1952: else ds@1952: { ds@1952: char buffer[32]; ds@1952: UTIL_Format(buffer, sizeof(buffer), "%d", size); ds@1952: rep("0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, buffer); ds@1952: total_size += size; ds@1952: } ds@1952: } ds@1952: rep("-- Approximately %d bytes of memory are in use by Handles.\n", total_size); ds@1952: } ds@1952: