core/logic/HandleSys.cpp
author David Anderson <dvander@alliedmods.net>
Fri Mar 29 11:37:29 2013 -0700 (2013-03-29)
changeset 3872 4c93f74cae51
parent 3838 core/HandleSys.cpp@aa2d7aa5322f
child 3996 4a0daf289347
permissions -rw-r--r--
Move scared guts of core into logic (bug 5680, r=fyren).
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "HandleSys.h"
    33 #include <assert.h>
    34 #include <string.h>
    35 #include "common_logic.h"
    36 #include "ShareSys.h"
    37 #include "ExtensionSys.h"
    38 
    39 HandleSystem g_HandleSys;
    40 
    41 QHandle *ignore_handle;
    42 
    43 inline HandleType_t TypeParent(HandleType_t type)
    44 {
    45 	return (type & ~HANDLESYS_SUBTYPE_MASK);
    46 }
    47 
    48 inline HandleError IdentityHandle(IdentityToken_t *token, unsigned int *index)
    49 {
    50 	return g_HandleSys.GetHandle(token->ident, g_ShareSys.GetIdentRoot(), &ignore_handle, index);
    51 }
    52 
    53 HandleSystem::HandleSystem()
    54 {
    55 	m_Handles = new QHandle[HANDLESYS_MAX_HANDLES + 1];
    56 	memset(m_Handles, 0, sizeof(QHandle) * (HANDLESYS_MAX_HANDLES + 1));
    57 
    58 	m_Types = new QHandleType[HANDLESYS_TYPEARRAY_SIZE];
    59 	memset(m_Types, 0, sizeof(QHandleType) * HANDLESYS_TYPEARRAY_SIZE);
    60 
    61 	m_strtab = new BaseStringTable(512);
    62 
    63 	m_TypeTail = 0;
    64 }
    65 
    66 HandleSystem::~HandleSystem()
    67 {
    68 	delete [] m_Handles;
    69 	delete [] m_Types;
    70 	delete m_strtab;
    71 }
    72 
    73 
    74 HandleType_t HandleSystem::CreateType(const char *name, 
    75 									  IHandleTypeDispatch *dispatch, 
    76 									  HandleType_t parent, 
    77 									  const TypeAccess *typeAccess, 
    78 									  const HandleAccess *hndlAccess, 
    79 									  IdentityToken_t *ident,
    80 									  HandleError *err)
    81 {
    82 	if (!dispatch)
    83 	{
    84 		if (err)
    85 		{
    86 			*err = HandleError_Parameter;
    87 		}
    88 		return 0;
    89 	}
    90 
    91 	if (typeAccess && typeAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
    92 	{
    93 		if (err)
    94 		{
    95 			*err = HandleError_Version;
    96 		}
    97 		return 0;
    98 	}
    99 
   100 	if (hndlAccess && hndlAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
   101 	{
   102 		if (err)
   103 		{
   104 			*err = HandleError_Version;
   105 		}
   106 		return 0;
   107 	}
   108 
   109 	bool isChild = false;
   110 
   111 	if (parent != 0)
   112 	{
   113 		isChild = true;
   114 		if (parent & HANDLESYS_SUBTYPE_MASK)
   115 		{
   116 			if (err)
   117 			{
   118 				*err = HandleError_NoInherit;
   119 			}
   120 			return 0;
   121 		}
   122 		if (parent >= HANDLESYS_TYPEARRAY_SIZE
   123 			|| m_Types[parent].dispatch == NULL)
   124 		{
   125 			if (err)
   126 			{
   127 				*err = HandleError_Parameter;
   128 			}
   129 			return 0;
   130 		}
   131 		if (m_Types[parent].typeSec.access[HTypeAccess_Inherit] == false
   132 			&& (m_Types[parent].typeSec.ident != ident))
   133 		{
   134 			if (err)
   135 			{
   136 				*err = HandleError_Access;
   137 			}
   138 			return 0;
   139 		}
   140 	}
   141 
   142 	if (name && name[0] != '\0')
   143 	{
   144 		if (m_TypeLookup.retrieve(name))
   145 		{
   146 			if (err)
   147 			{
   148 				*err = HandleError_Parameter;
   149 			}
   150 			return 0;
   151 		}
   152 	}
   153 
   154 	unsigned int index;
   155 
   156 	if (isChild)
   157 	{
   158 		QHandleType *pParent = &m_Types[parent];
   159 		if (pParent->children >= HANDLESYS_MAX_SUBTYPES)
   160 		{
   161 			if (err)
   162 			{
   163 				*err = HandleError_Limit;
   164 			}
   165 			return 0;
   166 		}
   167 		index = 0;
   168 		for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++)
   169 		{
   170 			if (m_Types[parent + i].dispatch == NULL)
   171 			{
   172 				index = parent + i;
   173 				break;
   174 			}
   175 		}
   176 		if (!index)
   177 		{
   178 			if (err)
   179 			{
   180 				*err = HandleError_Limit;
   181 			}
   182 			return 0;
   183 		}
   184 		pParent->children++;
   185 	} else {
   186 		if (m_FreeTypes == 0)
   187 		{
   188 			/* Reserve another index */
   189 			if (m_TypeTail >= HANDLESYS_TYPEARRAY_SIZE)
   190 			{
   191 				if (err)
   192 				{
   193 					*err = HandleError_Limit;
   194 				}
   195 				return 0;
   196 			} else {
   197 				m_TypeTail += (HANDLESYS_MAX_SUBTYPES + 1);
   198 				index = m_TypeTail;
   199 			}
   200 		} else {
   201 			/* The "free array" is compacted into the normal array for easiness */
   202 			index = m_Types[m_FreeTypes--].freeID;
   203 		}
   204 	}
   205 
   206 	QHandleType *pType = &m_Types[index];
   207 	
   208 	pType->dispatch = dispatch;
   209 	if (name && name[0] != '\0')
   210 	{
   211 		pType->nameIdx = m_strtab->AddString(name);
   212 		m_TypeLookup.insert(name, pType);
   213 	} else {
   214 		pType->nameIdx = -1;
   215 	}
   216 
   217 	pType->opened = 0;
   218 
   219 	if (typeAccess)
   220 	{
   221 		pType->typeSec = *typeAccess;
   222 	} else {
   223 		InitAccessDefaults(&pType->typeSec, NULL);
   224 		pType->typeSec.ident = ident;
   225 	}
   226 
   227 	if (hndlAccess)
   228 	{
   229 		pType->hndlSec = *hndlAccess;
   230 	} else {
   231 		InitAccessDefaults(NULL, &pType->hndlSec);
   232 	}
   233 
   234 	if (!isChild)
   235 	{
   236 		pType->children = 0;
   237 	}
   238 
   239 	return index;
   240 }
   241 
   242 bool HandleSystem::FindHandleType(const char *name, HandleType_t *type)
   243 {
   244 	QHandleType **typepp = m_TypeLookup.retrieve(name);
   245 
   246 	if (!typepp)
   247 	{
   248 		return false;
   249 	}
   250 
   251 	unsigned int offset = *typepp - m_Types;
   252 
   253 	if (type)
   254 	{
   255 		*type = offset;
   256 	}
   257 
   258 	return true;
   259 }
   260 
   261 HandleError HandleSystem::TryAllocHandle(unsigned int *handle)
   262 {
   263 	if (m_FreeHandles == 0)
   264 	{
   265 		if (m_HandleTail >= HANDLESYS_MAX_HANDLES)
   266 		{
   267 			return HandleError_Limit;;
   268 		}
   269 		*handle = ++m_HandleTail;
   270 	}
   271 	else
   272 	{
   273 		*handle = m_Handles[m_FreeHandles--].freeID;
   274 	}
   275 
   276 	return HandleError_None;
   277 }
   278 
   279 HandleError HandleSystem::MakePrimHandle(HandleType_t type, 
   280 						   QHandle **in_pHandle, 
   281 						   unsigned int *in_index, 
   282 						   Handle_t *in_handle,
   283 						   IdentityToken_t *owner,
   284 						   bool identity)
   285 {
   286 	HandleError err;
   287 	unsigned int owner_index = 0;
   288 
   289 	if (owner && (IdentityHandle(owner, &owner_index) != HandleError_None))
   290 	{
   291 		return HandleError_Identity;
   292 	}
   293 
   294 	unsigned int handle;
   295 	if ((err = TryAllocHandle(&handle)) != HandleError_None)
   296 	{
   297 		if (!TryAndFreeSomeHandles()
   298 			|| (err = TryAllocHandle(&handle)) != HandleError_None)
   299 		{
   300 			return err;
   301 		}
   302 	}
   303 
   304 	QHandle *pHandle = &m_Handles[handle];
   305 	
   306 	assert(pHandle->set == false);
   307 
   308 	if (++m_HSerial >= HANDLESYS_MAX_SERIALS)
   309 	{
   310 		m_HSerial = 1;
   311 	}
   312 
   313 	/* Set essential information */
   314 	pHandle->set = identity ? HandleSet_Identity : HandleSet_Used;
   315 	pHandle->refcount = 1;
   316 	pHandle->type = type;
   317 	pHandle->serial = m_HSerial;
   318 	pHandle->owner = owner;
   319 	pHandle->ch_next = 0;
   320 	pHandle->access_special = false;
   321 	pHandle->is_destroying = false;
   322 
   323 	/* Create the hash value */
   324 	Handle_t hash = pHandle->serial;
   325 	hash <<= 16;
   326 	hash |= handle;
   327 
   328 	/* Add a reference count to the type */
   329 	m_Types[type].opened++;
   330 
   331 	/* Output */
   332 	*in_pHandle = pHandle;
   333 	*in_index = handle;
   334 	*in_handle = hash;
   335 
   336 	/* Decode the identity token 
   337 	 * For now, we don't allow nested ownership
   338 	 */
   339 	if (owner && !identity)
   340 	{
   341 		QHandle *pIdentity = &m_Handles[owner_index];
   342 		if (pIdentity->ch_prev == 0)
   343 		{
   344 			pIdentity->ch_prev = handle;
   345 			pIdentity->ch_next = handle;
   346 			pHandle->ch_prev = 0;
   347 		}
   348 		else
   349 		{
   350 			/* Link previous node to us (forward) */
   351 			m_Handles[pIdentity->ch_next].ch_next = handle;
   352 			/* Link us to previous node (backwards) */
   353 			pHandle->ch_prev = pIdentity->ch_next;
   354 			/* Set new tail */
   355 			pIdentity->ch_next = handle;
   356 		}
   357 		pIdentity->refcount++;
   358 	}
   359 	else
   360 	{
   361 		pHandle->ch_prev = 0;
   362 	}
   363 
   364 	return HandleError_None;
   365 }
   366 
   367 void HandleSystem::SetTypeSecurityOwner(HandleType_t type, IdentityToken_t *pToken)
   368 {
   369 	if (!type
   370 		|| type >= HANDLESYS_TYPEARRAY_SIZE
   371 		|| m_Types[type].dispatch == NULL)
   372 	{
   373 		return;
   374 	}
   375 
   376 	m_Types[type].typeSec.ident = pToken;
   377 }
   378 
   379 Handle_t HandleSystem::CreateHandleInt(HandleType_t type, 
   380 									   void *object, 
   381 									   const HandleSecurity *pSec,
   382 									   HandleError *err, 
   383 									   const HandleAccess *pAccess,
   384 									   bool identity)
   385 {
   386 	IdentityToken_t *ident;
   387 	IdentityToken_t *owner;
   388 
   389 	if (pSec)
   390 	{
   391 		ident = pSec->pIdentity;
   392 		owner = pSec->pOwner;
   393 	} else {
   394 		ident = NULL;
   395 		owner = NULL;
   396 	}
   397 
   398 	if (!type 
   399 		|| type >= HANDLESYS_TYPEARRAY_SIZE
   400 		|| m_Types[type].dispatch == NULL)
   401 	{
   402 		if (err)
   403 		{
   404 			*err = HandleError_Parameter;
   405 		}
   406 		return 0;
   407 	}
   408 
   409 	/* Check to see if we're allowed to create this handle type */
   410 	QHandleType *pType = &m_Types[type];
   411 	if (!pType->typeSec.access[HTypeAccess_Create]
   412 	&& (!pType->typeSec.ident
   413 		|| pType->typeSec.ident != ident))
   414 	{
   415 		if (err)
   416 		{
   417 			*err = HandleError_Access;
   418 		}
   419 		return 0;
   420 	}
   421 
   422 	unsigned int index;
   423 	Handle_t handle;
   424 	QHandle *pHandle;
   425 	HandleError _err;
   426 
   427 	if ((_err=MakePrimHandle(type, &pHandle, &index, &handle, owner, identity)) != HandleError_None)
   428 	{
   429 		if (err)
   430 		{
   431 			*err = _err;
   432 		}
   433 		return 0;
   434 	}
   435 
   436 	if (pAccess)
   437 	{
   438 		pHandle->access_special = true;
   439 		pHandle->sec = *pAccess;
   440 	}
   441 
   442 	pHandle->object = object;
   443 	pHandle->clone = 0;
   444 
   445 	return handle;
   446 }
   447 
   448 Handle_t HandleSystem::CreateHandleEx(HandleType_t type, void *object, const HandleSecurity *pSec, const HandleAccess *pAccess, HandleError *err)
   449 {
   450 	return CreateHandleInt(type, object, pSec, err, pAccess, false);
   451 }
   452 
   453 Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityToken_t *owner, IdentityToken_t *ident, HandleError *err)
   454 {
   455 	HandleSecurity sec;
   456 
   457 	sec.pIdentity = ident;
   458 	sec.pOwner = owner;
   459 
   460 	return CreateHandleEx(type, object, &sec, NULL, err);
   461 }
   462 
   463 bool HandleSystem::TypeCheck(HandleType_t intype, HandleType_t outtype)
   464 {
   465 	/* Check the type inheritance */
   466 	if (intype & HANDLESYS_SUBTYPE_MASK)
   467 	{
   468 		if (intype != outtype
   469 			&& (TypeParent(intype) != TypeParent(outtype)))
   470 		{
   471 			return false;
   472 		}
   473 	} else {
   474 		if (intype != outtype)
   475 		{
   476 			return false;
   477 		}
   478 	}
   479 
   480 	return true;
   481 }
   482 
   483 HandleError HandleSystem::GetHandle(Handle_t handle,
   484 									IdentityToken_t *ident, 
   485 									QHandle **in_pHandle, 
   486 									unsigned int *in_index,
   487 									bool ignoreFree)
   488 {
   489 	unsigned int serial = (handle >> 16);
   490 	unsigned int index = (handle & HANDLESYS_HANDLE_MASK);
   491 
   492 	if (index == 0 || index > m_HandleTail || index > HANDLESYS_MAX_HANDLES)
   493 	{
   494 		return HandleError_Index;
   495 	}
   496 
   497 	QHandle *pHandle = &m_Handles[index];
   498 
   499 	if (!pHandle->set
   500 		|| (pHandle->set == HandleSet_Freed && !ignoreFree))
   501 	{
   502 		return HandleError_Freed;
   503 	} else if (pHandle->set == HandleSet_Identity
   504 			   && ident != g_ShareSys.GetIdentRoot())
   505 	{
   506 		/* Only IdentityHandle() can read this! */
   507 		return HandleError_Identity;
   508 	}
   509 	if (pHandle->serial != serial)
   510 	{
   511 		return HandleError_Changed;
   512 	}
   513 
   514 	*in_pHandle = pHandle;
   515 	*in_index = index;
   516 
   517 	return HandleError_None;
   518 }
   519 
   520 bool HandleSystem::CheckAccess(QHandle *pHandle, HandleAccessRight right, const HandleSecurity *pSecurity)
   521 {
   522 	QHandleType *pType = &m_Types[pHandle->type];
   523 	unsigned int access;
   524 
   525 	if (pHandle->access_special)
   526 	{
   527 		access = pHandle->sec.access[right];
   528 	} else {
   529 		access = pType->hndlSec.access[right];
   530 	}
   531 
   532 	/* Check if the type's identity matches */
   533 	if (access & HANDLE_RESTRICT_IDENTITY)
   534 	{
   535 		IdentityToken_t *owner = pType->typeSec.ident;
   536 		if (!owner
   537 			|| (!pSecurity || pSecurity->pIdentity != owner))
   538 		{
   539 			return false;
   540 		}
   541 	}
   542 
   543 	/* Check if the owner is allowed */
   544 	if (access & HANDLE_RESTRICT_OWNER)
   545 	{
   546 		IdentityToken_t *owner = pHandle->owner;
   547 		if (owner
   548 			&& (!pSecurity || pSecurity->pOwner != owner))
   549 		{
   550 			return false;
   551 		}
   552 	}
   553 
   554 	return true;
   555 }
   556 
   557 HandleError HandleSystem::CloneHandle(QHandle *pHandle, unsigned int index, Handle_t *newhandle, IdentityToken_t *newOwner)
   558 {
   559 	/* Get a new Handle ID */
   560 	unsigned int new_index;
   561 	QHandle *pNewHandle;
   562 	Handle_t new_handle;
   563 	HandleError err;
   564 
   565 	if ((err=MakePrimHandle(pHandle->type, &pNewHandle, &new_index, &new_handle, newOwner)) != HandleError_None)
   566 	{
   567 		return err;
   568 	}
   569 
   570 	/* Assign permissions from parent */
   571 	if (pHandle->access_special)
   572 	{
   573 		pNewHandle->access_special = true;
   574 		pNewHandle->sec = pHandle->sec;
   575 	}
   576 
   577 	pNewHandle->clone = index;
   578 	pNewHandle->object = NULL;
   579 	pHandle->refcount++;
   580 
   581 	*newhandle = new_handle;
   582 
   583 	return HandleError_None;
   584 }
   585 
   586 HandleError HandleSystem::CloneHandle(Handle_t handle, Handle_t *newhandle, IdentityToken_t *newOwner, const HandleSecurity *pSecurity)
   587 {
   588 	HandleError err;
   589 	QHandle *pHandle;
   590 	unsigned int index;
   591 	IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
   592 
   593 	if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
   594 	{
   595 		return err;
   596 	}
   597 
   598 	/* Identities cannot be cloned */
   599 	if (pHandle->set == HandleSet_Identity)
   600 	{
   601 		return HandleError_Identity;
   602 	}
   603 
   604 	/* Check if the handle can be cloned */
   605 	if (!CheckAccess(pHandle, HandleAccess_Clone, pSecurity))
   606 	{
   607 		return HandleError_Access;
   608 	}
   609 
   610 	/* Make sure we're not cloning a clone */
   611 	if (pHandle->clone)
   612 	{
   613 		QHandle *pParent = &m_Handles[pHandle->clone];
   614 		return CloneHandle(pParent, pHandle->clone, newhandle, newOwner);
   615 	}
   616 
   617 	return CloneHandle(pHandle, index, newhandle, newOwner);
   618 }
   619 
   620 Handle_t HandleSystem::FastCloneHandle(QHandle *pHandle, unsigned int index)
   621 {
   622 	if (pHandle->clone)
   623 		return FastCloneHandle(&m_Handles[pHandle->clone], pHandle->clone);
   624 
   625 	Handle_t hndl;
   626 	if (CloneHandle(pHandle, index, &hndl, g_pCoreIdent) != HandleError_None)
   627 		return BAD_HANDLE;
   628 
   629 	return hndl;
   630 }
   631 
   632 Handle_t HandleSystem::FastCloneHandle(Handle_t hndl)
   633 {
   634 	QHandle *pHandle;
   635 	unsigned int index;
   636 
   637 	GetHandleUnchecked(hndl, pHandle, index);
   638 
   639 	return FastCloneHandle(pHandle, index);
   640 }
   641 
   642 void HandleSystem::GetHandleUnchecked(Handle_t hndl, QHandle *& pHandle, unsigned int &index)
   643 {
   644 #ifndef NDEBUG
   645 	unsigned int serial = (hndl >> 16);
   646 #endif
   647 	index = (hndl & HANDLESYS_HANDLE_MASK);
   648 
   649 	assert(index != 0 && index <= m_HandleTail && index < HANDLESYS_MAX_HANDLES);
   650 
   651 	pHandle = &m_Handles[index];
   652 
   653 	assert(pHandle->set && pHandle->set != HandleSet_Freed);
   654 	assert(pHandle->serial == serial);
   655 }
   656 
   657 HandleError HandleSystem::FreeHandle(QHandle *pHandle, unsigned int index)
   658 {
   659 	if (pHandle->is_destroying)
   660 	{
   661 		/* Someone tried to free this recursively.  
   662 		 * We'll just ignore this safely.
   663 		 */
   664 		return HandleError_None;
   665 	}
   666 
   667 	QHandleType *pType = &m_Types[pHandle->type];
   668 
   669 	if (pHandle->clone)
   670 	{
   671 		/* If we're a clone, decrease the parent reference count */
   672 		QHandle *pMaster;
   673 		unsigned int master;
   674 
   675 		/* Note that if we ever have per-handle security, we would need to re-check
   676 		* the access on this Handle. */
   677 		master = pHandle->clone;
   678 		pMaster = &m_Handles[master];
   679 
   680 		/* Release the clone now */
   681 		pHandle->is_destroying = true;
   682 		ReleasePrimHandle(index);
   683 
   684 		/* Decrement the master's reference count */
   685 		if (--pMaster->refcount == 0)
   686 		{
   687 			/* Type should be the same but do this anyway... */
   688 			pType = &m_Types[pMaster->type];
   689 			pMaster->is_destroying = true;
   690 			if (pMaster->object)
   691 			{
   692 				pType->dispatch->OnHandleDestroy(pMaster->type, pMaster->object);
   693 			}
   694 			ReleasePrimHandle(master);
   695 		}
   696 	} else if (pHandle->set == HandleSet_Identity) {
   697 		/* If we're an identity, skip all this stuff!
   698 		 * NOTE: SHARESYS DOES NOT CARE ABOUT THE DESTRUCTOR
   699 		 */
   700 		pHandle->is_destroying = true;
   701 		ReleasePrimHandle(index);
   702 	} else {
   703 		/* Decrement, free if necessary */
   704 		if (--pHandle->refcount == 0)
   705 		{
   706 			pHandle->is_destroying = true;
   707 			if (pHandle->object)
   708 			{
   709 				pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object);
   710 			}
   711 			ReleasePrimHandle(index);
   712 		} else {
   713 			/* We must be cloned, so mark ourselves as freed */
   714 			pHandle->set = HandleSet_Freed;
   715 			/* Now, unlink us, so we're not being tracked by the owner */
   716 			if (pHandle->owner)
   717 			{
   718 				UnlinkHandleFromOwner(pHandle, index);
   719 			}
   720 		}
   721 	}
   722 
   723 	return HandleError_None;
   724 }
   725 
   726 HandleError HandleSystem::FreeHandle(Handle_t handle, const HandleSecurity *pSecurity)
   727 {
   728 	unsigned int index;
   729 	QHandle *pHandle;
   730 	HandleError err;
   731 	IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
   732 
   733 	if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
   734 	{
   735 		return err;
   736 	}
   737 
   738 	if (!CheckAccess(pHandle, HandleAccess_Delete, pSecurity))
   739 	{
   740 		return HandleError_Access;
   741 	}
   742 
   743 	return FreeHandle(pHandle, index);
   744 }
   745 
   746 HandleError HandleSystem::ReadHandle(Handle_t handle, HandleType_t type, const HandleSecurity *pSecurity, void **object)
   747 {
   748 	unsigned int index;
   749 	QHandle *pHandle;
   750 	HandleError err;
   751 	IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
   752 
   753 	if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
   754 	{
   755 		return err;
   756 	}
   757 
   758 	if (!CheckAccess(pHandle, HandleAccess_Read, pSecurity))
   759 	{
   760 		return HandleError_Access;
   761 	}
   762 
   763 	/* Check the type inheritance */
   764 	if (pHandle->type & HANDLESYS_SUBTYPE_MASK)
   765 	{
   766 		if (pHandle->type != type
   767 			&& (TypeParent(pHandle->type) != TypeParent(type)))
   768 		{
   769 			return HandleError_Type;
   770 		}
   771 	} else if (type) {
   772 		if (pHandle->type != type)
   773 		{
   774 			return HandleError_Type;
   775 		}
   776 	}
   777 
   778 	if (object)
   779 	{
   780 		/* if we're a clone, the rules change - object is ONLY in our reference */
   781 		if (pHandle->clone)
   782 		{
   783 			pHandle = &m_Handles[pHandle->clone];
   784 		}
   785 		*object = pHandle->object;
   786 	}
   787 
   788 	return HandleError_None;
   789 }
   790 
   791 void HandleSystem::UnlinkHandleFromOwner(QHandle *pHandle, unsigned int index)
   792 {
   793 	/* Unlink us if necessary */
   794 	unsigned int ident_index;
   795 	if (IdentityHandle(pHandle->owner, &ident_index) != HandleError_None)
   796 	{
   797 		/* Uh-oh! */
   798 		assert(pHandle->owner == 0);
   799 		return;
   800 	}
   801 
   802 	pHandle->owner = NULL;
   803 
   804 	/* Note that since 0 is an invalid handle, if any of these links are 0,
   805 	* the data can still be set.
   806 	*/
   807 	QHandle *pIdentity = &m_Handles[ident_index];
   808 
   809 	/* Unlink case: We're the head AND tail node */
   810 	if (index == pIdentity->ch_prev && index == pIdentity->ch_next)
   811 	{
   812 		pIdentity->ch_prev = 0;
   813 		pIdentity->ch_next = 0;
   814 	}
   815 	/* Unlink case: We're the head node */
   816 	else if (index == pIdentity->ch_prev) {
   817 		/* Link us to the next in the chain */
   818 		pIdentity->ch_prev = pHandle->ch_next;
   819 		/* Patch up the previous link */
   820 		m_Handles[pHandle->ch_next].ch_prev = 0;
   821 	}
   822 	/* Unlink case: We're the tail node */
   823 	else if (index == pIdentity->ch_next) {
   824 		/* Link us to the previous in the chain */
   825 		pIdentity->ch_next = pHandle->ch_prev;
   826 		/* Patch up the next link */
   827 		m_Handles[pHandle->ch_prev].ch_next = 0;
   828 	}
   829 	/* Unlink case: We're in the middle! */
   830 	else {
   831 		/* Patch the forward reference */
   832 		m_Handles[pHandle->ch_next].ch_prev = pHandle->ch_prev;
   833 		/* Patch the backward reference */
   834 		m_Handles[pHandle->ch_prev].ch_next = pHandle->ch_next;
   835 	}
   836 
   837 	/* Lastly, decrease the reference count */
   838 	pIdentity->refcount--;
   839 }
   840 
   841 void HandleSystem::ReleasePrimHandle(unsigned int index)
   842 {
   843 	QHandle *pHandle = &m_Handles[index];
   844 	HandleSet set = pHandle->set;
   845 
   846 	if (pHandle->owner && (set != HandleSet_Identity))
   847 	{
   848 		UnlinkHandleFromOwner(pHandle, index);
   849 	}
   850 
   851 	/* Were we an identity ourself? */
   852 	QHandle *pLocal;
   853 	if (set == HandleSet_Identity)
   854 	{
   855 		/* Extra work to do.  We need to find everything connected to this identity and release it. */
   856 		unsigned int ch_index;
   857 #if defined _DEBUG
   858 		unsigned int old_index = 0;
   859 #endif
   860 		while ((ch_index = pHandle->ch_next) != 0)
   861 		{
   862 			pLocal = &m_Handles[ch_index];
   863 #if defined _DEBUG
   864 			assert(old_index != ch_index);
   865 			assert(pLocal->set == HandleSet_Used);
   866 			old_index = ch_index;
   867 #endif
   868 			FreeHandle(pLocal, ch_index);
   869 		}
   870 	}
   871 
   872 	pHandle->set = HandleSet_None;
   873 	m_Types[pHandle->type].opened--;
   874 	m_Handles[++m_FreeHandles].freeID = index;
   875 }
   876 
   877 bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t *ident)
   878 {
   879 	if (type == 0 || type >= HANDLESYS_TYPEARRAY_SIZE)
   880 	{
   881 		return false;
   882 	}
   883 
   884 	QHandleType *pType = &m_Types[type];
   885 
   886 	if (pType->typeSec.ident
   887 		&& pType->typeSec.ident != ident)
   888 	{
   889 		return false;
   890 	}
   891 
   892 	if (pType->dispatch == NULL)
   893 	{
   894 		return false;
   895 	}
   896 
   897 	/* Remove children if we have to */
   898 	if (!(type & HANDLESYS_SUBTYPE_MASK))
   899 	{
   900 		QHandleType *childType;
   901 		for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++)
   902 		{
   903 			childType = &m_Types[type + i];
   904 			if (childType->dispatch)
   905 			{
   906 				RemoveType(type + i, childType->typeSec.ident);
   907 			}
   908 		}
   909 		/* Link us into the free chain */
   910 		m_Types[++m_FreeTypes].freeID = type;
   911 	}
   912 
   913 	/* Invalidate the type now */
   914 	IHandleTypeDispatch *dispatch = pType->dispatch;
   915 	pType->dispatch = NULL;
   916 
   917 	/* Make sure nothing is using this type. */
   918 	if (pType->opened)
   919 	{
   920 		QHandle *pHandle;
   921 		for (unsigned int i=1; i<=m_HandleTail; i++)
   922 		{
   923 			pHandle = &m_Handles[i];
   924 			if (!pHandle->set || pHandle->type != type)
   925 			{
   926 				continue;
   927 			}
   928 
   929 			FreeHandle(pHandle, i);
   930 
   931 			if (pType->opened == 0)
   932 			{
   933 				break;
   934 			}
   935 		}
   936 	}
   937 
   938 	/* Remove it from the type cache. */
   939 	if (pType->nameIdx != -1)
   940 	{
   941 		const char *typeName;
   942 
   943 		typeName = m_strtab->GetString(pType->nameIdx);
   944 		m_TypeLookup.remove(typeName);
   945 	}
   946 
   947 	return true;
   948 }
   949 
   950 bool HandleSystem::InitAccessDefaults(TypeAccess *pTypeAccess, HandleAccess *pHandleAccess)
   951 {
   952 	if (pTypeAccess)
   953 	{
   954 		if (pTypeAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
   955 		{
   956 			return false;
   957 		}
   958 		pTypeAccess->access[HTypeAccess_Create] = false;
   959 		pTypeAccess->access[HTypeAccess_Inherit] = false;
   960 		pTypeAccess->ident = NULL;
   961 	}
   962 
   963 	if (pHandleAccess)
   964 	{
   965 		if (pHandleAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
   966 		{
   967 			return false;
   968 		}
   969 
   970 		pHandleAccess->access[HandleAccess_Clone] = 0;
   971 		pHandleAccess->access[HandleAccess_Delete] = HANDLE_RESTRICT_OWNER;
   972 		pHandleAccess->access[HandleAccess_Read] = HANDLE_RESTRICT_IDENTITY;
   973 	}
   974 
   975 	return true;
   976 }
   977 
   978 #define HANDLE_LOG_VERY_BAD(message, ...) \
   979 	smcore.LogFatal(message, ##__VA_ARGS__); \
   980 	smcore.LogError(message, ##__VA_ARGS__);
   981 
   982 bool HandleSystem::TryAndFreeSomeHandles()
   983 {
   984 	IPluginIterator *pl_iter = scripts->GetPluginIterator();
   985 	IPlugin *highest_owner = NULL;
   986 	unsigned int highest_handle_count = 0;
   987 
   988 	/* Search all plugins */
   989 	while (pl_iter->MorePlugins())
   990 	{
   991 		IPlugin *plugin = pl_iter->GetPlugin();
   992 		IdentityToken_t *identity = plugin->GetIdentity();
   993 		unsigned int handle_count = 0;
   994 
   995 		if (identity == NULL)
   996 		{
   997 			continue;
   998 		}
   999 
  1000 		/* Search all handles */
  1001 		for (unsigned int i = 1; i <= m_HandleTail; i++)
  1002 		{
  1003 			if (m_Handles[i].set != HandleSet_Used)
  1004 			{
  1005 				continue;
  1006 			}
  1007 			if (m_Handles[i].owner == identity)
  1008 			{
  1009 				handle_count++;
  1010 			}
  1011 		}
  1012 
  1013 		if (handle_count > highest_handle_count)
  1014 		{
  1015 			highest_owner = plugin;
  1016 			highest_handle_count = handle_count;
  1017 		}
  1018 
  1019 		pl_iter->NextPlugin();
  1020 	}
  1021 
  1022 	if (highest_owner == NULL || highest_handle_count == 0)
  1023 	{
  1024 		return false;
  1025 	}
  1026 
  1027 	HANDLE_LOG_VERY_BAD("[SM] MEMORY LEAK DETECTED IN PLUGIN (file \"%s\")", highest_owner->GetFilename());
  1028 	HANDLE_LOG_VERY_BAD("[SM] Unloading plugin to free %d handles.", highest_handle_count);
  1029 	HANDLE_LOG_VERY_BAD("[SM] Contact the author(s) of this plugin to correct this error.", highest_handle_count);
  1030 
  1031 	highest_owner->GetBaseContext()->ThrowNativeErrorEx(SP_ERROR_MEMACCESS, "Memory leak");
  1032 
  1033 	return scripts->UnloadPlugin(highest_owner);
  1034 }
  1035 
  1036 void HandleSystem::Dump(HANDLE_REPORTER rep)
  1037 {
  1038 	unsigned int total_size = 0;
  1039 	rep("%-10.10s\t%-20.20s\t%-20.20s\t%-10.10s", "Handle", "Owner", "Type", "Memory");
  1040 	rep("--------------------------------------------------------------------------");
  1041 	for (unsigned int i = 1; i <= m_HandleTail; i++)
  1042 	{
  1043 		if (m_Handles[i].set != HandleSet_Used)
  1044 		{
  1045 			continue;
  1046 		}
  1047 		/* Get the index */
  1048 		unsigned int index = (m_Handles[i].serial << 16) | i;
  1049 		/* Determine the owner */
  1050 		const char *owner = "UNKNOWN";
  1051 		if (m_Handles[i].owner)
  1052 		{
  1053 			IdentityToken_t *pOwner = m_Handles[i].owner;
  1054 			if (pOwner == g_pCoreIdent)
  1055 			{
  1056 				owner = "CORE";
  1057 			}
  1058 			else if (pOwner == scripts->GetIdentity())
  1059 			{
  1060 				owner = "PLUGINSYS";
  1061 			}
  1062 			else
  1063 			{
  1064 				IExtension *ext = g_Extensions.GetExtensionFromIdent(pOwner);
  1065 				if (ext)
  1066 				{
  1067 					owner = ext->GetFilename();
  1068 				}
  1069 				else
  1070 				{
  1071 					SMPlugin *pPlugin = scripts->FindPluginByIdentity(pOwner);
  1072 					if (pPlugin)
  1073 					{
  1074 						owner = pPlugin->GetFilename();
  1075 					}
  1076 				}
  1077 			}
  1078 		}
  1079 		else
  1080 		{
  1081 			owner = "NONE";
  1082 		}
  1083 		const char *type = "ANON";
  1084 		QHandleType *pType = &m_Types[m_Handles[i].type];
  1085 		unsigned int size = 0;
  1086 		unsigned int parentIdx;
  1087 		bool bresult;
  1088 		if (pType->nameIdx != -1)
  1089 		{
  1090 			type = m_strtab->GetString(pType->nameIdx);
  1091 		}
  1092 
  1093 		if ((parentIdx = m_Handles[i].clone) != 0)
  1094 		{
  1095 			if (m_Handles[parentIdx].refcount > 0)
  1096 			{
  1097 				size = 0;
  1098 				bresult = true;
  1099 			}
  1100 			else
  1101 			{
  1102 				bresult = pType->dispatch->GetHandleApproxSize(m_Handles[parentIdx].type, m_Handles[parentIdx].object, &size);
  1103 			}
  1104 		}
  1105 		else
  1106 		{
  1107 			bresult = pType->dispatch->GetHandleApproxSize(m_Handles[i].type, m_Handles[i].object, &size);
  1108 		}
  1109 
  1110 		if (pType->dispatch->GetDispatchVersion() < HANDLESYS_MEMUSAGE_MIN_VERSION
  1111 			|| !bresult)
  1112 		{
  1113 			rep("0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, "-1");
  1114 		}
  1115 		else
  1116 		{
  1117 			char buffer[32];
  1118 			smcore.Format(buffer, sizeof(buffer), "%d", size);
  1119 			rep("0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, buffer);
  1120 			total_size += size;
  1121 		}
  1122 	}
  1123 	rep("-- Approximately %d bytes of memory are in use by Handles.\n", total_size);
  1124 }
  1125