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