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