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: