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