1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/core/systems/HandleSys.cpp Sun Jul 06 04:49:55 2008 +0000
1.3 @@ -0,0 +1,1067 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * =============================================================================
1.7 + * SourceMod
1.8 + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.9 + * =============================================================================
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify it under
1.12 + * the terms of the GNU General Public License, version 3.0, as published by the
1.13 + * Free Software Foundation.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 + * details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License along with
1.21 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 + *
1.23 + * As a special exception, AlliedModders LLC gives you permission to link the
1.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 + * by the Valve Corporation. You must obey the GNU General Public License in
1.27 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 + * this exception to all derivative works. AlliedModders LLC defines further
1.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 + * or <http://www.sourcemod.net/license.php>.
1.31 + *
1.32 + * Version: $Id$
1.33 + */
1.34 +
1.35 +#include "HandleSys.h"
1.36 +#include "ShareSys.h"
1.37 +#include "PluginSys.h"
1.38 +#include "ExtensionSys.h"
1.39 +#include "Logger.h"
1.40 +#include <assert.h>
1.41 +#include <string.h>
1.42 +#include "sm_stringutil.h"
1.43 +
1.44 +HandleSystem g_HandleSys;
1.45 +
1.46 +QHandle *ignore_handle;
1.47 +
1.48 +inline HandleType_t TypeParent(HandleType_t type)
1.49 +{
1.50 + return (type & ~HANDLESYS_SUBTYPE_MASK);
1.51 +}
1.52 +
1.53 +inline HandleError IdentityHandle(IdentityToken_t *token, unsigned int *index)
1.54 +{
1.55 + return g_HandleSys.GetHandle(token->ident, g_ShareSys.GetIdentRoot(), &ignore_handle, index);
1.56 +}
1.57 +
1.58 +HandleSystem::HandleSystem()
1.59 +{
1.60 + m_Handles = new QHandle[HANDLESYS_MAX_HANDLES + 1];
1.61 + memset(m_Handles, 0, sizeof(QHandle) * (HANDLESYS_MAX_HANDLES + 1));
1.62 +
1.63 + m_Types = new QHandleType[HANDLESYS_TYPEARRAY_SIZE];
1.64 + memset(m_Types, 0, sizeof(QHandleType) * HANDLESYS_TYPEARRAY_SIZE);
1.65 +
1.66 + m_TypeLookup = sm_trie_create();
1.67 + m_strtab = new BaseStringTable(512);
1.68 +
1.69 + m_TypeTail = 0;
1.70 +}
1.71 +
1.72 +HandleSystem::~HandleSystem()
1.73 +{
1.74 + delete [] m_Handles;
1.75 + delete [] m_Types;
1.76 + sm_trie_destroy(m_TypeLookup);
1.77 + delete m_strtab;
1.78 +}
1.79 +
1.80 +
1.81 +HandleType_t HandleSystem::CreateType(const char *name,
1.82 + IHandleTypeDispatch *dispatch,
1.83 + HandleType_t parent,
1.84 + const TypeAccess *typeAccess,
1.85 + const HandleAccess *hndlAccess,
1.86 + IdentityToken_t *ident,
1.87 + HandleError *err)
1.88 +{
1.89 + if (!dispatch)
1.90 + {
1.91 + if (err)
1.92 + {
1.93 + *err = HandleError_Parameter;
1.94 + }
1.95 + return 0;
1.96 + }
1.97 +
1.98 + if (typeAccess && typeAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
1.99 + {
1.100 + if (err)
1.101 + {
1.102 + *err = HandleError_Version;
1.103 + }
1.104 + return 0;
1.105 + }
1.106 +
1.107 + if (hndlAccess && hndlAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
1.108 + {
1.109 + if (err)
1.110 + {
1.111 + *err = HandleError_Version;
1.112 + }
1.113 + return 0;
1.114 + }
1.115 +
1.116 + bool isChild = false;
1.117 +
1.118 + if (parent != 0)
1.119 + {
1.120 + isChild = true;
1.121 + if (parent & HANDLESYS_SUBTYPE_MASK)
1.122 + {
1.123 + if (err)
1.124 + {
1.125 + *err = HandleError_NoInherit;
1.126 + }
1.127 + return 0;
1.128 + }
1.129 + if (parent >= HANDLESYS_TYPEARRAY_SIZE
1.130 + || m_Types[parent].dispatch == NULL)
1.131 + {
1.132 + if (err)
1.133 + {
1.134 + *err = HandleError_Parameter;
1.135 + }
1.136 + return 0;
1.137 + }
1.138 + if (m_Types[parent].typeSec.access[HTypeAccess_Inherit] == false
1.139 + && (m_Types[parent].typeSec.ident != ident))
1.140 + {
1.141 + if (err)
1.142 + {
1.143 + *err = HandleError_Access;
1.144 + }
1.145 + return 0;
1.146 + }
1.147 + }
1.148 +
1.149 + if (name && name[0] != '\0')
1.150 + {
1.151 + if (sm_trie_retrieve(m_TypeLookup, name, NULL))
1.152 + {
1.153 + if (err)
1.154 + {
1.155 + *err = HandleError_Parameter;
1.156 + }
1.157 + return 0;
1.158 + }
1.159 + }
1.160 +
1.161 + unsigned int index;
1.162 +
1.163 + if (isChild)
1.164 + {
1.165 + QHandleType *pParent = &m_Types[parent];
1.166 + if (pParent->children >= HANDLESYS_MAX_SUBTYPES)
1.167 + {
1.168 + if (err)
1.169 + {
1.170 + *err = HandleError_Limit;
1.171 + }
1.172 + return 0;
1.173 + }
1.174 + index = 0;
1.175 + for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++)
1.176 + {
1.177 + if (m_Types[parent + i].dispatch == NULL)
1.178 + {
1.179 + index = parent + i;
1.180 + break;
1.181 + }
1.182 + }
1.183 + if (!index)
1.184 + {
1.185 + if (err)
1.186 + {
1.187 + *err = HandleError_Limit;
1.188 + }
1.189 + return 0;
1.190 + }
1.191 + pParent->children++;
1.192 + } else {
1.193 + if (m_FreeTypes == 0)
1.194 + {
1.195 + /* Reserve another index */
1.196 + if (m_TypeTail >= HANDLESYS_TYPEARRAY_SIZE)
1.197 + {
1.198 + if (err)
1.199 + {
1.200 + *err = HandleError_Limit;
1.201 + }
1.202 + return 0;
1.203 + } else {
1.204 + m_TypeTail += (HANDLESYS_MAX_SUBTYPES + 1);
1.205 + index = m_TypeTail;
1.206 + }
1.207 + } else {
1.208 + /* The "free array" is compacted into the normal array for easiness */
1.209 + index = m_Types[m_FreeTypes--].freeID;
1.210 + }
1.211 + }
1.212 +
1.213 + QHandleType *pType = &m_Types[index];
1.214 +
1.215 + pType->dispatch = dispatch;
1.216 + if (name && name[0] != '\0')
1.217 + {
1.218 + pType->nameIdx = m_strtab->AddString(name);
1.219 + sm_trie_insert(m_TypeLookup, name, (void *)pType);
1.220 + } else {
1.221 + pType->nameIdx = -1;
1.222 + }
1.223 +
1.224 + pType->opened = 0;
1.225 +
1.226 + if (typeAccess)
1.227 + {
1.228 + pType->typeSec = *typeAccess;
1.229 + } else {
1.230 + InitAccessDefaults(&pType->typeSec, NULL);
1.231 + pType->typeSec.ident = ident;
1.232 + }
1.233 +
1.234 + if (hndlAccess)
1.235 + {
1.236 + pType->hndlSec = *hndlAccess;
1.237 + } else {
1.238 + InitAccessDefaults(NULL, &pType->hndlSec);
1.239 + }
1.240 +
1.241 + if (!isChild)
1.242 + {
1.243 + pType->children = 0;
1.244 + }
1.245 +
1.246 + return index;
1.247 +}
1.248 +
1.249 +bool HandleSystem::FindHandleType(const char *name, HandleType_t *type)
1.250 +{
1.251 + QHandleType *_type;
1.252 +
1.253 + if (!sm_trie_retrieve(m_TypeLookup, name, (void **)&_type))
1.254 + {
1.255 + return false;
1.256 + }
1.257 +
1.258 + unsigned int offset = _type - m_Types;
1.259 +
1.260 + if (type)
1.261 + {
1.262 + *type = offset;
1.263 + }
1.264 +
1.265 + return true;
1.266 +}
1.267 +
1.268 +HandleError HandleSystem::TryAllocHandle(unsigned int *handle)
1.269 +{
1.270 + if (m_FreeHandles == 0)
1.271 + {
1.272 + if (m_HandleTail >= HANDLESYS_MAX_HANDLES)
1.273 + {
1.274 + return HandleError_Limit;;
1.275 + }
1.276 + *handle = ++m_HandleTail;
1.277 + }
1.278 + else
1.279 + {
1.280 + *handle = m_Handles[m_FreeHandles--].freeID;
1.281 + }
1.282 +
1.283 + return HandleError_None;
1.284 +}
1.285 +
1.286 +HandleError HandleSystem::MakePrimHandle(HandleType_t type,
1.287 + QHandle **in_pHandle,
1.288 + unsigned int *in_index,
1.289 + Handle_t *in_handle,
1.290 + IdentityToken_t *owner,
1.291 + bool identity)
1.292 +{
1.293 + HandleError err;
1.294 + unsigned int owner_index = 0;
1.295 +
1.296 + if (owner && (IdentityHandle(owner, &owner_index) != HandleError_None))
1.297 + {
1.298 + return HandleError_Identity;
1.299 + }
1.300 +
1.301 + unsigned int handle;
1.302 + if ((err = TryAllocHandle(&handle)) != HandleError_None)
1.303 + {
1.304 + if (!TryAndFreeSomeHandles()
1.305 + || (err = TryAllocHandle(&handle)) != HandleError_None)
1.306 + {
1.307 + return err;
1.308 + }
1.309 + }
1.310 +
1.311 + QHandle *pHandle = &m_Handles[handle];
1.312 +
1.313 + assert(pHandle->set == false);
1.314 +
1.315 + if (++m_HSerial >= HANDLESYS_MAX_SERIALS)
1.316 + {
1.317 + m_HSerial = 1;
1.318 + }
1.319 +
1.320 + /* Set essential information */
1.321 + pHandle->set = identity ? HandleSet_Identity : HandleSet_Used;
1.322 + pHandle->refcount = 1;
1.323 + pHandle->type = type;
1.324 + pHandle->serial = m_HSerial;
1.325 + pHandle->owner = owner;
1.326 + pHandle->ch_next = 0;
1.327 + pHandle->access_special = false;
1.328 + pHandle->is_destroying = false;
1.329 +
1.330 + /* Create the hash value */
1.331 + Handle_t hash = pHandle->serial;
1.332 + hash <<= 16;
1.333 + hash |= handle;
1.334 +
1.335 + /* Add a reference count to the type */
1.336 + m_Types[type].opened++;
1.337 +
1.338 + /* Output */
1.339 + *in_pHandle = pHandle;
1.340 + *in_index = handle;
1.341 + *in_handle = hash;
1.342 +
1.343 + /* Decode the identity token
1.344 + * For now, we don't allow nested ownership
1.345 + */
1.346 + if (owner && !identity)
1.347 + {
1.348 + QHandle *pIdentity = &m_Handles[owner_index];
1.349 + if (pIdentity->ch_prev == 0)
1.350 + {
1.351 + pIdentity->ch_prev = handle;
1.352 + pIdentity->ch_next = handle;
1.353 + pHandle->ch_prev = 0;
1.354 + }
1.355 + else
1.356 + {
1.357 + /* Link previous node to us (forward) */
1.358 + m_Handles[pIdentity->ch_next].ch_next = handle;
1.359 + /* Link us to previous node (backwards) */
1.360 + pHandle->ch_prev = pIdentity->ch_next;
1.361 + /* Set new tail */
1.362 + pIdentity->ch_next = handle;
1.363 + }
1.364 + pIdentity->refcount++;
1.365 + }
1.366 + else
1.367 + {
1.368 + pHandle->ch_prev = 0;
1.369 + }
1.370 +
1.371 + return HandleError_None;
1.372 +}
1.373 +
1.374 +void HandleSystem::SetTypeSecurityOwner(HandleType_t type, IdentityToken_t *pToken)
1.375 +{
1.376 + if (!type
1.377 + || type >= HANDLESYS_TYPEARRAY_SIZE
1.378 + || m_Types[type].dispatch == NULL)
1.379 + {
1.380 + return;
1.381 + }
1.382 +
1.383 + m_Types[type].typeSec.ident = pToken;
1.384 +}
1.385 +
1.386 +Handle_t HandleSystem::CreateHandleInt(HandleType_t type,
1.387 + void *object,
1.388 + const HandleSecurity *pSec,
1.389 + HandleError *err,
1.390 + const HandleAccess *pAccess,
1.391 + bool identity)
1.392 +{
1.393 + IdentityToken_t *ident;
1.394 + IdentityToken_t *owner;
1.395 +
1.396 + if (pSec)
1.397 + {
1.398 + ident = pSec->pIdentity;
1.399 + owner = pSec->pOwner;
1.400 + } else {
1.401 + ident = NULL;
1.402 + owner = NULL;
1.403 + }
1.404 +
1.405 + if (!type
1.406 + || type >= HANDLESYS_TYPEARRAY_SIZE
1.407 + || m_Types[type].dispatch == NULL)
1.408 + {
1.409 + if (err)
1.410 + {
1.411 + *err = HandleError_Parameter;
1.412 + }
1.413 + return 0;
1.414 + }
1.415 +
1.416 + /* Check to see if we're allowed to create this handle type */
1.417 + QHandleType *pType = &m_Types[type];
1.418 + if (!pType->typeSec.access[HTypeAccess_Create]
1.419 + && (!pType->typeSec.ident
1.420 + || pType->typeSec.ident != ident))
1.421 + {
1.422 + if (err)
1.423 + {
1.424 + *err = HandleError_Access;
1.425 + }
1.426 + return 0;
1.427 + }
1.428 +
1.429 + unsigned int index;
1.430 + Handle_t handle;
1.431 + QHandle *pHandle;
1.432 + HandleError _err;
1.433 +
1.434 + if ((_err=MakePrimHandle(type, &pHandle, &index, &handle, owner, identity)) != HandleError_None)
1.435 + {
1.436 + if (err)
1.437 + {
1.438 + *err = _err;
1.439 + }
1.440 + return 0;
1.441 + }
1.442 +
1.443 + if (pAccess)
1.444 + {
1.445 + pHandle->access_special = true;
1.446 + pHandle->sec = *pAccess;
1.447 + }
1.448 +
1.449 + pHandle->object = object;
1.450 + pHandle->clone = 0;
1.451 +
1.452 + return handle;
1.453 +}
1.454 +
1.455 +Handle_t HandleSystem::CreateHandleEx(HandleType_t type, void *object, const HandleSecurity *pSec, const HandleAccess *pAccess, HandleError *err)
1.456 +{
1.457 + return CreateHandleInt(type, object, pSec, err, pAccess, false);
1.458 +}
1.459 +
1.460 +Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityToken_t *owner, IdentityToken_t *ident, HandleError *err)
1.461 +{
1.462 + HandleSecurity sec;
1.463 +
1.464 + sec.pIdentity = ident;
1.465 + sec.pOwner = owner;
1.466 +
1.467 + return CreateHandleEx(type, object, &sec, NULL, err);
1.468 +}
1.469 +
1.470 +bool HandleSystem::TypeCheck(HandleType_t intype, HandleType_t outtype)
1.471 +{
1.472 + /* Check the type inheritance */
1.473 + if (intype & HANDLESYS_SUBTYPE_MASK)
1.474 + {
1.475 + if (intype != outtype
1.476 + && (TypeParent(intype) != TypeParent(outtype)))
1.477 + {
1.478 + return false;
1.479 + }
1.480 + } else {
1.481 + if (intype != outtype)
1.482 + {
1.483 + return false;
1.484 + }
1.485 + }
1.486 +
1.487 + return true;
1.488 +}
1.489 +
1.490 +HandleError HandleSystem::GetHandle(Handle_t handle,
1.491 + IdentityToken_t *ident,
1.492 + QHandle **in_pHandle,
1.493 + unsigned int *in_index,
1.494 + bool ignoreFree)
1.495 +{
1.496 + unsigned int serial = (handle >> 16);
1.497 + unsigned int index = (handle & HANDLESYS_HANDLE_MASK);
1.498 +
1.499 + if (index == 0 || index == 0xFFFF)
1.500 + {
1.501 + return HandleError_Index;
1.502 + }
1.503 +
1.504 + QHandle *pHandle = &m_Handles[index];
1.505 +
1.506 + if (!pHandle->set
1.507 + || (pHandle->set == HandleSet_Freed && !ignoreFree))
1.508 + {
1.509 + return HandleError_Freed;
1.510 + } else if (pHandle->set == HandleSet_Identity
1.511 + && ident != g_ShareSys.GetIdentRoot())
1.512 + {
1.513 + /* Only IdentityHandle() can read this! */
1.514 + return HandleError_Identity;
1.515 + }
1.516 + if (pHandle->serial != serial)
1.517 + {
1.518 + return HandleError_Changed;
1.519 + }
1.520 +
1.521 + *in_pHandle = pHandle;
1.522 + *in_index = index;
1.523 +
1.524 + return HandleError_None;
1.525 +}
1.526 +
1.527 +bool HandleSystem::CheckAccess(QHandle *pHandle, HandleAccessRight right, const HandleSecurity *pSecurity)
1.528 +{
1.529 + QHandleType *pType = &m_Types[pHandle->type];
1.530 + unsigned int access;
1.531 +
1.532 + if (pHandle->access_special)
1.533 + {
1.534 + access = pHandle->sec.access[right];
1.535 + } else {
1.536 + access = pType->hndlSec.access[right];
1.537 + }
1.538 +
1.539 + /* Check if the type's identity matches */
1.540 + if (access & HANDLE_RESTRICT_IDENTITY)
1.541 + {
1.542 + IdentityToken_t *owner = pType->typeSec.ident;
1.543 + if (!owner
1.544 + || (!pSecurity || pSecurity->pIdentity != owner))
1.545 + {
1.546 + return false;
1.547 + }
1.548 + }
1.549 +
1.550 + /* Check if the owner is allowed */
1.551 + if (access & HANDLE_RESTRICT_OWNER)
1.552 + {
1.553 + IdentityToken_t *owner = pHandle->owner;
1.554 + if (owner
1.555 + && (!pSecurity || pSecurity->pOwner != owner))
1.556 + {
1.557 + return false;
1.558 + }
1.559 + }
1.560 +
1.561 + return true;
1.562 +}
1.563 +
1.564 +HandleError HandleSystem::CloneHandle(QHandle *pHandle, unsigned int index, Handle_t *newhandle, IdentityToken_t *newOwner)
1.565 +{
1.566 + /* Get a new Handle ID */
1.567 + unsigned int new_index;
1.568 + QHandle *pNewHandle;
1.569 + Handle_t new_handle;
1.570 + HandleError err;
1.571 +
1.572 + if ((err=MakePrimHandle(pHandle->type, &pNewHandle, &new_index, &new_handle, newOwner)) != HandleError_None)
1.573 + {
1.574 + return err;
1.575 + }
1.576 +
1.577 + /* Assign permissions from parent */
1.578 + if (pHandle->access_special)
1.579 + {
1.580 + pNewHandle->access_special = true;
1.581 + pNewHandle->sec = pHandle->sec;
1.582 + }
1.583 +
1.584 + pNewHandle->clone = index;
1.585 + pHandle->refcount++;
1.586 +
1.587 + *newhandle = new_handle;
1.588 +
1.589 + return HandleError_None;
1.590 +}
1.591 +
1.592 +HandleError HandleSystem::CloneHandle(Handle_t handle, Handle_t *newhandle, IdentityToken_t *newOwner, const HandleSecurity *pSecurity)
1.593 +{
1.594 + HandleError err;
1.595 + QHandle *pHandle;
1.596 + unsigned int index;
1.597 + IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
1.598 +
1.599 + if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
1.600 + {
1.601 + return err;
1.602 + }
1.603 +
1.604 + /* Identities cannot be cloned */
1.605 + if (pHandle->set == HandleSet_Identity)
1.606 + {
1.607 + return HandleError_Identity;
1.608 + }
1.609 +
1.610 + /* Check if the handle can be cloned */
1.611 + if (!CheckAccess(pHandle, HandleAccess_Clone, pSecurity))
1.612 + {
1.613 + return HandleError_Access;
1.614 + }
1.615 +
1.616 + /* Make sure we're not cloning a clone */
1.617 + if (pHandle->clone)
1.618 + {
1.619 + QHandle *pParent = &m_Handles[pHandle->clone];
1.620 + return CloneHandle(pParent, pHandle->clone, newhandle, newOwner);
1.621 + }
1.622 +
1.623 + return CloneHandle(pHandle, index, newhandle, newOwner);
1.624 +}
1.625 +
1.626 +HandleError HandleSystem::FreeHandle(QHandle *pHandle, unsigned int index)
1.627 +{
1.628 + QHandleType *pType = &m_Types[pHandle->type];
1.629 +
1.630 + if (pHandle->clone)
1.631 + {
1.632 + /* If we're a clone, decrease the parent reference count */
1.633 + QHandle *pMaster;
1.634 + unsigned int master;
1.635 +
1.636 + /* Note that if we ever have per-handle security, we would need to re-check
1.637 + * the access on this Handle. */
1.638 + master = pHandle->clone;
1.639 + pMaster = &m_Handles[master];
1.640 +
1.641 + /* Release the clone now */
1.642 + ReleasePrimHandle(index);
1.643 +
1.644 + /* Decrement the master's reference count */
1.645 + if (--pMaster->refcount == 0)
1.646 + {
1.647 + /* Type should be the same but do this anyway... */
1.648 + pType = &m_Types[pMaster->type];
1.649 + pMaster->is_destroying = true;
1.650 + pType->dispatch->OnHandleDestroy(pMaster->type, pMaster->object);
1.651 + ReleasePrimHandle(master);
1.652 + }
1.653 + } else if (pHandle->set == HandleSet_Identity) {
1.654 + /* If we're an identity, skip all this stuff!
1.655 + * NOTE: SHARESYS DOES NOT CARE ABOUT THE DESTRUCTOR
1.656 + */
1.657 + ReleasePrimHandle(index);
1.658 + } else {
1.659 + /* Decrement, free if necessary */
1.660 + if (--pHandle->refcount == 0)
1.661 + {
1.662 + pHandle->is_destroying = true;
1.663 + pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object);
1.664 + ReleasePrimHandle(index);
1.665 + } else {
1.666 + /* We must be cloned, so mark ourselves as freed */
1.667 + pHandle->set = HandleSet_Freed;
1.668 + /* Now, unlink us, so we're not being tracked by the owner */
1.669 + if (pHandle->owner)
1.670 + {
1.671 + UnlinkHandleFromOwner(pHandle, index);
1.672 + }
1.673 + }
1.674 + }
1.675 +
1.676 + return HandleError_None;
1.677 +}
1.678 +
1.679 +HandleError HandleSystem::FreeHandle(Handle_t handle, const HandleSecurity *pSecurity)
1.680 +{
1.681 + unsigned int index;
1.682 + QHandle *pHandle;
1.683 + HandleError err;
1.684 + IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
1.685 +
1.686 + if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
1.687 + {
1.688 + return err;
1.689 + }
1.690 +
1.691 + if (!CheckAccess(pHandle, HandleAccess_Delete, pSecurity))
1.692 + {
1.693 + return HandleError_Access;
1.694 + }
1.695 +
1.696 + if (pHandle->is_destroying)
1.697 + {
1.698 + /* Someone tried to free this recursively.
1.699 + * We'll just ignore this safely.
1.700 + */
1.701 + return HandleError_None;
1.702 + }
1.703 +
1.704 + return FreeHandle(pHandle, index);
1.705 +}
1.706 +
1.707 +HandleError HandleSystem::ReadHandle(Handle_t handle, HandleType_t type, const HandleSecurity *pSecurity, void **object)
1.708 +{
1.709 + unsigned int index;
1.710 + QHandle *pHandle;
1.711 + HandleError err;
1.712 + IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
1.713 +
1.714 + if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
1.715 + {
1.716 + return err;
1.717 + }
1.718 +
1.719 + if (!CheckAccess(pHandle, HandleAccess_Read, pSecurity))
1.720 + {
1.721 + return HandleError_Access;
1.722 + }
1.723 +
1.724 + /* Check the type inheritance */
1.725 + if (pHandle->type & HANDLESYS_SUBTYPE_MASK)
1.726 + {
1.727 + if (pHandle->type != type
1.728 + && (TypeParent(pHandle->type) != TypeParent(type)))
1.729 + {
1.730 + return HandleError_Type;
1.731 + }
1.732 + } else if (type) {
1.733 + if (pHandle->type != type)
1.734 + {
1.735 + return HandleError_Type;
1.736 + }
1.737 + }
1.738 +
1.739 + if (object)
1.740 + {
1.741 + /* if we're a clone, the rules change - object is ONLY in our reference */
1.742 + if (pHandle->clone)
1.743 + {
1.744 + pHandle = &m_Handles[pHandle->clone];
1.745 + }
1.746 + *object = pHandle->object;
1.747 + }
1.748 +
1.749 + return HandleError_None;
1.750 +}
1.751 +
1.752 +void HandleSystem::UnlinkHandleFromOwner(QHandle *pHandle, unsigned int index)
1.753 +{
1.754 + /* Unlink us if necessary */
1.755 + unsigned int ident_index;
1.756 + if (IdentityHandle(pHandle->owner, &ident_index) != HandleError_None)
1.757 + {
1.758 + /* Uh-oh! */
1.759 + assert(pHandle->owner == 0);
1.760 + return;
1.761 + }
1.762 + /* Note that since 0 is an invalid handle, if any of these links are 0,
1.763 + * the data can still be set.
1.764 + */
1.765 + QHandle *pIdentity = &m_Handles[ident_index];
1.766 +
1.767 + /* Unlink case: We're the head AND tail node */
1.768 + if (index == pIdentity->ch_prev && index == pIdentity->ch_next)
1.769 + {
1.770 + pIdentity->ch_prev = 0;
1.771 + pIdentity->ch_next = 0;
1.772 + }
1.773 + /* Unlink case: We're the head node */
1.774 + else if (index == pIdentity->ch_prev) {
1.775 + /* Link us to the next in the chain */
1.776 + pIdentity->ch_prev = pHandle->ch_next;
1.777 + /* Patch up the previous link */
1.778 + m_Handles[pHandle->ch_next].ch_prev = 0;
1.779 + }
1.780 + /* Unlink case: We're the tail node */
1.781 + else if (index == pIdentity->ch_next) {
1.782 + /* Link us to the previous in the chain */
1.783 + pIdentity->ch_next = pHandle->ch_prev;
1.784 + /* Patch up the next link */
1.785 + m_Handles[pHandle->ch_prev].ch_next = 0;
1.786 + }
1.787 + /* Unlink case: We're in the middle! */
1.788 + else {
1.789 + /* Patch the forward reference */
1.790 + m_Handles[pHandle->ch_next].ch_prev = pHandle->ch_prev;
1.791 + /* Patch the backward reference */
1.792 + m_Handles[pHandle->ch_prev].ch_next = pHandle->ch_next;
1.793 + }
1.794 +
1.795 + /* Lastly, decrease the reference count */
1.796 + pIdentity->refcount--;
1.797 +}
1.798 +
1.799 +void HandleSystem::ReleasePrimHandle(unsigned int index)
1.800 +{
1.801 + QHandle *pHandle = &m_Handles[index];
1.802 + HandleSet set = pHandle->set;
1.803 +
1.804 + if (pHandle->owner && (set != HandleSet_Identity))
1.805 + {
1.806 + UnlinkHandleFromOwner(pHandle, index);
1.807 + }
1.808 +
1.809 + /* Were we an identity ourself? */
1.810 + QHandle *pLocal;
1.811 + if (set == HandleSet_Identity)
1.812 + {
1.813 + /* Extra work to do. We need to find everything connected to this identity and release it. */
1.814 + unsigned int ch_index;
1.815 +#if defined _DEBUG
1.816 + unsigned int old_index = 0;
1.817 +#endif
1.818 + while ((ch_index = pHandle->ch_next) != 0)
1.819 + {
1.820 + pLocal = &m_Handles[ch_index];
1.821 +#if defined _DEBUG
1.822 + assert(old_index != ch_index);
1.823 + assert(pLocal->set == HandleSet_Used);
1.824 + old_index = ch_index;
1.825 +#endif
1.826 + FreeHandle(pLocal, ch_index);
1.827 + }
1.828 + }
1.829 +
1.830 + pHandle->set = HandleSet_None;
1.831 + m_Types[pHandle->type].opened--;
1.832 + m_Handles[++m_FreeHandles].freeID = index;
1.833 +}
1.834 +
1.835 +bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t *ident)
1.836 +{
1.837 + if (type == 0 || type >= HANDLESYS_TYPEARRAY_SIZE)
1.838 + {
1.839 + return false;
1.840 + }
1.841 +
1.842 + QHandleType *pType = &m_Types[type];
1.843 +
1.844 + if (pType->typeSec.ident
1.845 + && pType->typeSec.ident != ident)
1.846 + {
1.847 + return false;
1.848 + }
1.849 +
1.850 + if (pType->dispatch == NULL)
1.851 + {
1.852 + return false;
1.853 + }
1.854 +
1.855 + /* Remove children if we have to */
1.856 + if (!(type & HANDLESYS_SUBTYPE_MASK))
1.857 + {
1.858 + QHandleType *childType;
1.859 + for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++)
1.860 + {
1.861 + childType = &m_Types[type + i];
1.862 + if (childType->dispatch)
1.863 + {
1.864 + RemoveType(type + i, childType->typeSec.ident);
1.865 + }
1.866 + }
1.867 + /* Link us into the free chain */
1.868 + m_Types[++m_FreeTypes].freeID = type;
1.869 + }
1.870 +
1.871 + /* Invalidate the type now */
1.872 + IHandleTypeDispatch *dispatch = pType->dispatch;
1.873 + pType->dispatch = NULL;
1.874 +
1.875 + /* Make sure nothing is using this type. */
1.876 + if (pType->opened)
1.877 + {
1.878 + QHandle *pHandle;
1.879 + for (unsigned int i=1; i<=m_HandleTail; i++)
1.880 + {
1.881 + pHandle = &m_Handles[i];
1.882 + if (!pHandle->set || pHandle->type != type)
1.883 + {
1.884 + continue;
1.885 + }
1.886 + if (pHandle->clone)
1.887 + {
1.888 + /* Get parent */
1.889 + QHandle *pOther = &m_Handles[pHandle->clone];
1.890 + if (--pOther->refcount == 0)
1.891 + {
1.892 + /* Free! */
1.893 + dispatch->OnHandleDestroy(type, pOther->object);
1.894 + ReleasePrimHandle(pHandle->clone);
1.895 + }
1.896 + /* Unlink ourselves since we don't have a reference count */
1.897 + ReleasePrimHandle(i);
1.898 + } else {
1.899 + /* If it's not a clone, we still have to check the reference count.
1.900 + * Either way, we'll be destroyed eventually because the handle types do not change.
1.901 + */
1.902 + if (--pHandle->refcount == 0)
1.903 + {
1.904 + /* Free! */
1.905 + dispatch->OnHandleDestroy(type, pHandle->object);
1.906 + ReleasePrimHandle(i);
1.907 + }
1.908 + }
1.909 + if (pType->opened == 0)
1.910 + {
1.911 + break;
1.912 + }
1.913 + }
1.914 + }
1.915 +
1.916 + return true;
1.917 +}
1.918 +
1.919 +bool HandleSystem::InitAccessDefaults(TypeAccess *pTypeAccess, HandleAccess *pHandleAccess)
1.920 +{
1.921 + if (pTypeAccess)
1.922 + {
1.923 + if (pTypeAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
1.924 + {
1.925 + return false;
1.926 + }
1.927 + pTypeAccess->access[HTypeAccess_Create] = false;
1.928 + pTypeAccess->access[HTypeAccess_Inherit] = false;
1.929 + pTypeAccess->ident = NULL;
1.930 + }
1.931 +
1.932 + if (pHandleAccess)
1.933 + {
1.934 + if (pHandleAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
1.935 + {
1.936 + return false;
1.937 + }
1.938 +
1.939 + pHandleAccess->access[HandleAccess_Clone] = 0;
1.940 + pHandleAccess->access[HandleAccess_Delete] = HANDLE_RESTRICT_OWNER;
1.941 + pHandleAccess->access[HandleAccess_Read] = HANDLE_RESTRICT_IDENTITY;
1.942 + }
1.943 +
1.944 + return true;
1.945 +}
1.946 +
1.947 +bool HandleSystem::TryAndFreeSomeHandles()
1.948 +{
1.949 + IPluginIterator *pl_iter = g_PluginSys.GetPluginIterator();
1.950 + IPlugin *highest_owner = NULL;
1.951 + unsigned int highest_handle_count = 0;
1.952 +
1.953 + /* Search all plugins */
1.954 + while (pl_iter->MorePlugins())
1.955 + {
1.956 + IPlugin *plugin = pl_iter->GetPlugin();
1.957 + IdentityToken_t *identity = plugin->GetIdentity();
1.958 + unsigned int handle_count = 0;
1.959 +
1.960 + if (identity == NULL)
1.961 + {
1.962 + continue;
1.963 + }
1.964 +
1.965 + /* Search all handles */
1.966 + for (unsigned int i = 1; i <= m_HandleTail; i++)
1.967 + {
1.968 + if (m_Handles[i].set != HandleSet_Used)
1.969 + {
1.970 + continue;
1.971 + }
1.972 + if (m_Handles[i].owner == identity)
1.973 + {
1.974 + handle_count++;
1.975 + }
1.976 + }
1.977 +
1.978 + if (handle_count > highest_handle_count)
1.979 + {
1.980 + highest_owner = plugin;
1.981 + highest_handle_count = handle_count;
1.982 + }
1.983 +
1.984 + pl_iter->NextPlugin();
1.985 + }
1.986 +
1.987 + if (highest_owner == NULL || highest_handle_count == 0)
1.988 + {
1.989 + return false;
1.990 + }
1.991 +
1.992 + g_Logger.LogFatal("[SM] MEMORY LEAK DETECTED IN PLUGIN (file \"%s\")", highest_owner->GetFilename());
1.993 + g_Logger.LogFatal("[SM] Reloading plugin to free %d handles.", highest_handle_count);
1.994 + g_Logger.LogFatal("[SM] Contact the author(s) of this plugin to correct this error.", highest_handle_count);
1.995 +
1.996 + highest_owner->GetContext()->n_err = SP_ERROR_MEMACCESS;
1.997 +
1.998 + return g_PluginSys.UnloadPlugin(highest_owner);
1.999 +}
1.1000 +
1.1001 +void HandleSystem::Dump(HANDLE_REPORTER rep)
1.1002 +{
1.1003 + unsigned int total_size = 0;
1.1004 + rep("%-10.10s\t%-20.20s\t%-20.20s\t%-10.10s", "Handle", "Owner", "Type", "Memory");
1.1005 + rep("--------------------------------------------------------------------------");
1.1006 + for (unsigned int i = 1; i <= m_HandleTail; i++)
1.1007 + {
1.1008 + if (m_Handles[i].set != HandleSet_Used)
1.1009 + {
1.1010 + continue;
1.1011 + }
1.1012 + /* Get the index */
1.1013 + unsigned int index = (m_Handles[i].serial << 16) | i;
1.1014 + /* Determine the owner */
1.1015 + const char *owner = "UNKNOWN";
1.1016 + if (m_Handles[i].owner)
1.1017 + {
1.1018 + IdentityToken_t *pOwner = m_Handles[i].owner;
1.1019 + if (pOwner == g_pCoreIdent)
1.1020 + {
1.1021 + owner = "CORE";
1.1022 + }
1.1023 + else if (pOwner == g_PluginSys.GetIdentity())
1.1024 + {
1.1025 + owner = "PLUGINSYS";
1.1026 + }
1.1027 + else
1.1028 + {
1.1029 + CExtension *ext = g_Extensions.GetExtensionFromIdent(pOwner);
1.1030 + if (ext)
1.1031 + {
1.1032 + owner = ext->GetFilename();
1.1033 + }
1.1034 + else
1.1035 + {
1.1036 + CPlugin *pPlugin = g_PluginSys.GetPluginFromIdentity(pOwner);
1.1037 + if (pPlugin)
1.1038 + {
1.1039 + owner = pPlugin->GetFilename();
1.1040 + }
1.1041 + }
1.1042 + }
1.1043 + }
1.1044 + else
1.1045 + {
1.1046 + owner = "NONE";
1.1047 + }
1.1048 + const char *type = "ANON";
1.1049 + QHandleType *pType = &m_Types[m_Handles[i].type];
1.1050 + unsigned int size = 0;
1.1051 + if (pType->nameIdx != -1)
1.1052 + {
1.1053 + type = m_strtab->GetString(pType->nameIdx);
1.1054 + }
1.1055 + if (pType->dispatch->GetDispatchVersion() < HANDLESYS_MEMUSAGE_MIN_VERSION
1.1056 + || !pType->dispatch->GetHandleApproxSize(m_Handles[i].type, m_Handles[i].object, &size))
1.1057 + {
1.1058 + rep("0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, "-1");
1.1059 + }
1.1060 + else
1.1061 + {
1.1062 + char buffer[32];
1.1063 + UTIL_Format(buffer, sizeof(buffer), "%d", size);
1.1064 + rep("0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, buffer);
1.1065 + total_size += size;
1.1066 + }
1.1067 + }
1.1068 + rep("-- Approximately %d bytes of memory are in use by Handles.\n", total_size);
1.1069 +}
1.1070 +