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