| /* | 
 |  * dlls/rsaenh/handle.c | 
 |  * Support code to manage HANDLE tables. | 
 |  * | 
 |  * Copyright 1998 Alexandre Julliard | 
 |  * Copyright 2002-2004 Mike McCormack for CodeWeavers | 
 |  * Copyright 2004 Michael Jung | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  * | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |  * Lesser General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU Lesser General Public | 
 |  * License along with this library; if not, write to the Free Software | 
 |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
 |  */ | 
 |  | 
 | #include <string.h> | 
 | #include <stdarg.h> | 
 |  | 
 | #include "windef.h" | 
 | #include "winbase.h" | 
 | #include "handle.h" | 
 |  | 
 | #include "wine/debug.h" | 
 |  | 
 | WINE_DEFAULT_DEBUG_CHANNEL(handle); | 
 |  | 
 | #define HANDLE2INDEX(h) ((h)-1) | 
 | #define INDEX2HANDLE(i) ((i)+1) | 
 |  | 
 | /****************************************************************************** | 
 |  *  init_handle_table | 
 |  * | 
 |  * Initializes the HANDLETABLE structure pointed to by lpTable | 
 |  * | 
 |  * PARAMS | 
 |  *  lpTable [I] Pointer to the HANDLETABLE structure, which is to be initalized. | 
 |  * | 
 |  * NOTES | 
 |  *  Note that alloc_handle_table calls init_handle_table on it's own, which  | 
 |  *  means that you only have to call init_handle_table, if you use a global | 
 |  *  variable of type HANDLETABLE for your handle table. However, in this | 
 |  *  case you have to call destroy_handle_table when you don't need the table | 
 |  *  any more. | 
 |  */ | 
 | void init_handle_table(HANDLETABLE *lpTable) | 
 | { | 
 |     TRACE("(lpTable=%p)\n", lpTable); | 
 |          | 
 |     lpTable->paEntries = NULL; | 
 |     lpTable->iEntries = 0; | 
 |     lpTable->iFirstFree = 0; | 
 |     InitializeCriticalSection(&lpTable->mutex); | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *  destroy_handle_table | 
 |  * | 
 |  * Destroys the handle table. | 
 |  *  | 
 |  * PARAMS | 
 |  *  lpTable [I] Pointer to the handle table, which is to be destroyed. | 
 |  * | 
 |  * NOTES | 
 |  *  Note that release_handle_table takes care of this. | 
 |  */ | 
 | void destroy_handle_table(HANDLETABLE *lpTable) | 
 | { | 
 |     TRACE("(lpTable=%p)\n", lpTable); | 
 |          | 
 |     HeapFree(GetProcessHeap(), 0, lpTable->paEntries); | 
 |     DeleteCriticalSection(&lpTable->mutex); | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *  is_valid_handle | 
 |  * | 
 |  * Tests if handle is valid given the specified handle table | 
 |  *  | 
 |  * PARAMS | 
 |  *  lpTable [I] Pointer to the handle table, with respect to which the handle's  | 
 |  *              validness is tested. | 
 |  *  handle  [I] The handle tested for validness. | 
 |  *  dwType  [I] A magic value that identifies the referenced object's type. | 
 |  * | 
 |  * RETURNS | 
 |  *  non zero,  if handle is valid. | 
 |  *  zero,      if handle is not valid. | 
 |  */ | 
 | int is_valid_handle(HANDLETABLE *lpTable, unsigned int handle, DWORD dwType) | 
 | { | 
 |     unsigned int index = HANDLE2INDEX(handle); | 
 |     int ret = 0; | 
 |  | 
 |     TRACE("(lpTable=%p, handle=%d)\n", lpTable, handle); | 
 |      | 
 |     EnterCriticalSection(&lpTable->mutex); | 
 |          | 
 |     /* We don't use zero handle values */ | 
 |     if (!handle) goto exit; | 
 |   | 
 |     /* Check for index out of table bounds */     | 
 |     if (index >= lpTable->iEntries) goto exit; | 
 |      | 
 |     /* Check if this handle is currently allocated */ | 
 |     if (!lpTable->paEntries[index].pObject) goto exit; | 
 |      | 
 |     /* Check if this handle references an object of the correct type. */ | 
 |     if (lpTable->paEntries[index].pObject->dwType != dwType) goto exit; | 
 |      | 
 |     ret = 1; | 
 | exit: | 
 |     LeaveCriticalSection(&lpTable->mutex); | 
 |     return ret; | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *  alloc_handle_table | 
 |  * | 
 |  * Allocates a new handle table | 
 |  *  | 
 |  * PARAMS | 
 |  *  lplpTable [O] Pointer to the variable, to which the pointer to the newly | 
 |  *                allocated handle table is written. | 
 |  * RETURNS | 
 |  *  non zero,  if successful | 
 |  *  zero,      if not successful (out of process heap memory) | 
 |  * | 
 |  * NOTES | 
 |  *  If all you need is a single handle table, you may as well declare a global  | 
 |  *  variable of type HANDLETABLE and call init_handle_table on your own.  | 
 |  */ | 
 | int alloc_handle_table(HANDLETABLE **lplpTable) | 
 | { | 
 |     TRACE("(lplpTable=%p)\n", lplpTable); | 
 |          | 
 |     *lplpTable = HeapAlloc(GetProcessHeap(), 0, sizeof(HANDLETABLE)); | 
 |     if (*lplpTable)  | 
 |     { | 
 |         init_handle_table(*lplpTable); | 
 |         return 1; | 
 |     } | 
 |     else | 
 |         return 0; | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *  release_handle_table | 
 |  * | 
 |  * Releases a handle table and frees the resources it used. | 
 |  * | 
 |  * PARAMS | 
 |  *  lpTable [I] Pointer to the handle table, which is to be released. | 
 |  * | 
 |  * RETURNS | 
 |  *  non zero,  if successful | 
 |  *  zero,      if not successful | 
 |  * | 
 |  * NOTES | 
 |  *   All valid handles still in the table are released also. | 
 |  */ | 
 | int release_handle_table(HANDLETABLE *lpTable)  | 
 | { | 
 |     TRACE("(lpTable=%p)\n", lpTable); | 
 |          | 
 |     release_all_handles(lpTable); | 
 |     destroy_handle_table(lpTable); | 
 |     return (int)HeapFree(GetProcessHeap(), 0, lpTable); | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *  grow_handle_table [Internal] | 
 |  * | 
 |  * Grows the number of entries in the given table by TABLE_SIZE_INCREMENT | 
 |  * | 
 |  * PARAMS  | 
 |  *  lpTable [I] Pointer to the table, which is to be grown | 
 |  * | 
 |  * RETURNS | 
 |  *  non zero,  if successful | 
 |  *  zero,      if not successful (out of memory on process heap) | 
 |  * | 
 |  * NOTES | 
 |  *  This is a support function for alloc_handle. Do not call! | 
 |  */ | 
 | static int grow_handle_table(HANDLETABLE *lpTable)  | 
 | { | 
 |     HANDLETABLEENTRY *newEntries; | 
 |     unsigned int i, newIEntries; | 
 |  | 
 |     newIEntries = lpTable->iEntries + TABLE_SIZE_INCREMENT; | 
 |  | 
 |     newEntries = (HANDLETABLEENTRY*)HeapAlloc(GetProcessHeap(), 0,  | 
 |                                               sizeof(HANDLETABLEENTRY)*newIEntries); | 
 |     if (!newEntries)  | 
 |         return 0; | 
 |  | 
 |     if (lpTable->paEntries) | 
 |     { | 
 |         memcpy(newEntries, lpTable->paEntries, sizeof(HANDLETABLEENTRY)*lpTable->iEntries); | 
 |         HeapFree(GetProcessHeap(), 0, lpTable->paEntries); | 
 |     } | 
 |  | 
 |     for (i=lpTable->iEntries; i<newIEntries; i++) | 
 |     { | 
 |         newEntries[i].pObject = NULL; | 
 |         newEntries[i].iNextFree = i+1; | 
 |     } | 
 |  | 
 |     lpTable->paEntries = newEntries; | 
 |     lpTable->iEntries = newIEntries; | 
 |  | 
 |     return 1; | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *  alloc_handle | 
 |  * | 
 |  * Allocates a new handle to the specified object in a given handle table. | 
 |  * | 
 |  * PARAMS | 
 |  *  lpTable  [I] Pointer to the handle table, from which the new handle is  | 
 |  *               allocated. | 
 |  *  lpObject [I] Pointer to the object, for which a handle shall be allocated. | 
 |  *  lpHandle [O] Pointer to a handle variable, into which the handle value will | 
 |  *               be stored. If not successful, this will be  | 
 |  *               INVALID_HANDLE_VALUE | 
 |  * RETURNS | 
 |  *  non zero,  if successful | 
 |  *  zero,      if not successful (no free handle) | 
 |  */ | 
 | int alloc_handle(HANDLETABLE *lpTable, OBJECTHDR *lpObject, unsigned int *lpHandle) | 
 | { | 
 |     int ret = 0; | 
 |          | 
 |     TRACE("(lpTable=%p, lpObject=%p, lpHandle=%p)\n", lpTable, lpObject, lpHandle); | 
 |          | 
 |     EnterCriticalSection(&lpTable->mutex); | 
 |     if (lpTable->iFirstFree >= lpTable->iEntries)  | 
 |         if (!grow_handle_table(lpTable)) | 
 |         { | 
 |             *lpHandle = (unsigned int)INVALID_HANDLE_VALUE; | 
 |             goto exit; | 
 |         } | 
 |  | 
 |     *lpHandle = INDEX2HANDLE(lpTable->iFirstFree); | 
 |      | 
 |     lpTable->paEntries[lpTable->iFirstFree].pObject = lpObject; | 
 |     lpTable->iFirstFree = lpTable->paEntries[lpTable->iFirstFree].iNextFree; | 
 |     lpObject->refcount++; | 
 |  | 
 |     ret = 1; | 
 | exit: | 
 |     LeaveCriticalSection(&lpTable->mutex); | 
 |     return ret; | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *  release_handle | 
 |  * | 
 |  * Releases resources occupied by the specified handle in the given table. | 
 |  * The reference count of the handled object is decremented. If it becomes | 
 |  * zero and if the 'destructor' function pointer member is non NULL, the | 
 |  * destructor function will be called. Note that release_handle does not  | 
 |  * release resources other than the handle itself. If this is wanted, do it | 
 |  * in the destructor function. | 
 |  * | 
 |  * PARAMS | 
 |  *  lpTable [I] Pointer to the handle table, from which a handle is to be  | 
 |  *              released. | 
 |  *  handle  [I] The handle, which is to be released | 
 |  *  dwType  [I] Identifier for the type of the object, for which a handle is | 
 |  *              to be released. | 
 |  * | 
 |  * RETURNS | 
 |  *  non zero,  if successful | 
 |  *  zero,      if not successful (invalid handle) | 
 |  */ | 
 | int release_handle(HANDLETABLE *lpTable, unsigned int handle, DWORD dwType) | 
 | { | 
 |     unsigned int index = HANDLE2INDEX(handle); | 
 |     OBJECTHDR *pObject; | 
 |     int ret = 0; | 
 |  | 
 |     TRACE("(lpTable=%p, hande=%d)\n", lpTable, handle); | 
 |      | 
 |     EnterCriticalSection(&lpTable->mutex); | 
 |      | 
 |     if (!is_valid_handle(lpTable, handle, dwType)) | 
 |         goto exit; | 
 |  | 
 |     pObject = lpTable->paEntries[index].pObject; | 
 |     pObject->refcount--; | 
 |     if (pObject->refcount == 0) | 
 |         if (pObject->destructor) | 
 |             pObject->destructor(pObject); | 
 |  | 
 |     lpTable->paEntries[index].pObject = NULL; | 
 |     lpTable->paEntries[index].iNextFree = lpTable->iFirstFree; | 
 |     lpTable->iFirstFree = index; | 
 |     | 
 |     ret = 1; | 
 | exit: | 
 |     LeaveCriticalSection(&lpTable->mutex); | 
 |     return ret; | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *  release_all_handles | 
 |  * | 
 |  * Releases all valid handles in the given handle table and shrinks the table  | 
 |  * to zero size. | 
 |  * | 
 |  * PARAMS | 
 |  *  lpTable [I] The table of which all valid handles shall be released. | 
 |  */ | 
 | void release_all_handles(HANDLETABLE *lpTable)  | 
 | { | 
 |     unsigned int i; | 
 |  | 
 |     TRACE("(lpTable=%p)\n", lpTable); | 
 |          | 
 |     EnterCriticalSection(&lpTable->mutex); | 
 |     for (i=0; i<lpTable->iEntries; i++)  | 
 |         if (lpTable->paEntries[i].pObject) | 
 |             release_handle(lpTable, lpTable->paEntries[i].pObject->dwType, INDEX2HANDLE(i)); | 
 |     LeaveCriticalSection(&lpTable->mutex); | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *  lookup_handle | 
 |  * | 
 |  * Returns the object identified by the handle in the given handle table | 
 |  * | 
 |  * PARAMS | 
 |  *  lpTable    [I] Pointer to the handle table, in which the handle is looked up. | 
 |  *  handle     [I] The handle, which is to be looked up | 
 |  *    lplpObject [O] Pointer to the variable, into which the pointer to the  | 
 |  *                   object looked up is copied. | 
 |  * RETURNS | 
 |  *  non zero,  if successful | 
 |  *  zero,      if not successful (invalid handle) | 
 |  */ | 
 | int lookup_handle(HANDLETABLE *lpTable, unsigned int handle, DWORD dwType, OBJECTHDR **lplpObject) | 
 | { | 
 |     int ret = 0; | 
 |      | 
 |     TRACE("(lpTable=%p, handle=%d, lplpObject=%p)\n", lpTable, handle, lplpObject); | 
 |      | 
 |     EnterCriticalSection(&lpTable->mutex); | 
 |     if (!is_valid_handle(lpTable, handle, dwType))  | 
 |     { | 
 |         *lplpObject = NULL; | 
 |         goto exit; | 
 |     } | 
 |     *lplpObject = lpTable->paEntries[HANDLE2INDEX(handle)].pObject; | 
 |  | 
 |     ret = 1; | 
 | exit: | 
 |     LeaveCriticalSection(&lpTable->mutex); | 
 |     return ret; | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *  copy_handle | 
 |  * | 
 |  * Copies a handle. Increments the reference count of the object referenced | 
 |  * by the handle. | 
 |  * | 
 |  * PARAMS | 
 |  *  lpTable [I] Pointer to the handle table, which holds the handle to be copied. | 
 |  *  handle  [I] The handle to be copied. | 
 |  *  copy    [O] Pointer to a handle variable, where the copied handle is put. | 
 |  * | 
 |  * RETURNS | 
 |  *  non zero,  if successful | 
 |  *  zero,      if not successful (invalid handle or out of memory) | 
 |  */ | 
 | int copy_handle(HANDLETABLE *lpTable, unsigned int handle, DWORD dwType, unsigned int *copy) | 
 | { | 
 |     OBJECTHDR *pObject; | 
 |     int ret; | 
 |          | 
 |     TRACE("(lpTable=%p, handle=%d, copy=%p)\n", lpTable, handle, copy); | 
 |  | 
 |     EnterCriticalSection(&lpTable->mutex); | 
 |     if (!lookup_handle(lpTable, handle, dwType, &pObject))  | 
 |     { | 
 |         *copy = (unsigned int)INVALID_HANDLE_VALUE; | 
 |         LeaveCriticalSection(&lpTable->mutex); | 
 |         return 0; | 
 |     } | 
 |  | 
 |     ret = alloc_handle(lpTable, pObject, copy); | 
 |     LeaveCriticalSection(&lpTable->mutex); | 
 |     return ret; | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *  new_object | 
 |  * | 
 |  * Allocates a new object of size cbSize on the current process's heap. | 
 |  * Initializes the object header using the destructor and dwType params. | 
 |  * Allocates a handle to the object in the handle table pointed to by lpTable. | 
 |  * Returns a pointer to the created object in ppObject. | 
 |  * Returns a handle to the created object. | 
 |  * | 
 |  * PARAMS | 
 |  *  lpTable    [I] Pointer to the handle table, from which a handle is to be  | 
 |  *              allocated. | 
 |  *  cbSize     [I] Size of the object to be allocated in bytes. | 
 |  *  dwType     [I] Object type; will be copied to the object header. | 
 |  *  destructor [I] Function pointer to a destructor function. Will be called | 
 |  *                 once the object's reference count gets zero. | 
 |  *  ppObject   [O] Pointer to a pointer variable, where a pointer to the newly | 
 |  *                 created object will be stored. You may set this to NULL. | 
 |  * | 
 |  * RETURNS | 
 |  *  INVALID_HANDLE_VALUE,        if something went wrong. | 
 |  *  a handle to the new object,  if successful.  | 
 |  */ | 
 | unsigned int new_object(HANDLETABLE *lpTable, size_t cbSize, DWORD dwType, DESTRUCTOR destructor,  | 
 |                         OBJECTHDR **ppObject) | 
 | { | 
 |     OBJECTHDR *pObject; | 
 |     unsigned int hObject; | 
 |  | 
 |     if (ppObject) | 
 |         *ppObject = NULL; | 
 |  | 
 |     pObject = (OBJECTHDR*)HeapAlloc(GetProcessHeap(), 0, cbSize); | 
 |     if (!pObject) | 
 |         return (unsigned int)INVALID_HANDLE_VALUE; | 
 |  | 
 |     pObject->dwType = dwType; | 
 |     pObject->refcount = 0; | 
 |     pObject->destructor = destructor; | 
 |  | 
 |     if (!alloc_handle(lpTable, pObject, &hObject)) | 
 |         HeapFree(GetProcessHeap(), 0, pObject); | 
 |     else | 
 |         if (ppObject) | 
 |             *ppObject = pObject; | 
 |  | 
 |     return hObject; | 
 | } |