/* * Win32 process handles * * Copyright 1998 Alexandre Julliard */ #include #include #include "winbase.h" #include "winerror.h" #include "heap.h" #include "process.h" #include "server.h" #include "thread.h" #include "debug.h" #define HTABLE_SIZE 0x30 /* Handle table initial size */ #define HTABLE_INC 0x10 /* Handle table increment */ /* Reserved access rights */ #define RESERVED_ALL (0x0007 << RESERVED_SHIFT) #define RESERVED_SHIFT 25 #define RESERVED_INHERIT (HANDLE_FLAG_INHERIT<handle_table; table = HeapReAlloc( process->system_heap, HEAP_ZERO_MEMORY | HEAP_NO_SERIALIZE, table, sizeof(HANDLE_TABLE) + (table->count + incr - 1) * sizeof(HANDLE_ENTRY) ); if (table) { table->count += incr; process->handle_table = table; } SYSTEM_UNLOCK(); return (table != NULL); } /*********************************************************************** * HANDLE_CreateTable * * Create a process handle table, optionally inheriting the parent's handles. */ BOOL32 HANDLE_CreateTable( PDB32 *pdb, BOOL32 inherit ) { DWORD size; /* Process must not already have a handle table */ assert( !pdb->handle_table ); /* If this is the first process, simply allocate a table */ if (!pdb->parent) inherit = FALSE; SYSTEM_LOCK(); size = inherit ? pdb->parent->handle_table->count : HTABLE_SIZE; if ((pdb->handle_table = HeapAlloc( pdb->system_heap, HEAP_ZERO_MEMORY | HEAP_NO_SERIALIZE, sizeof(HANDLE_TABLE) + (size-1) * sizeof(HANDLE_ENTRY) ))) { pdb->handle_table->count = size; if (inherit) { HANDLE_ENTRY *src = pdb->parent->handle_table->entries; HANDLE_ENTRY *dst = pdb->handle_table->entries; HANDLE32 h; for (h = 0; h < size; h++, src++, dst++) { /* Check if handle is valid and inheritable */ if (src->ptr && (src->access & RESERVED_INHERIT)) { dst->access = src->access; dst->ptr = src->ptr; dst->server = src->server; K32OBJ_IncCount( dst->ptr ); } } } /* Handle 1 is the process itself (unless the parent decided otherwise) */ if (!pdb->handle_table->entries[1].ptr) { pdb->handle_table->entries[1].ptr = &pdb->header; pdb->handle_table->entries[1].access = PROCESS_ALL_ACCESS; pdb->handle_table->entries[1].server = -1; /* FIXME */ K32OBJ_IncCount( &pdb->header ); } } SYSTEM_UNLOCK(); return (pdb->handle_table != NULL); } /*********************************************************************** * HANDLE_Alloc * * Allocate a handle for a kernel object and increment its refcount. */ HANDLE32 HANDLE_Alloc( PDB32 *pdb, K32OBJ *ptr, DWORD access, BOOL32 inherit, int server_handle ) { HANDLE32 h; HANDLE_ENTRY *entry; assert( ptr ); /* Set the inherit reserved flag */ access &= ~RESERVED_ALL; if (inherit) access |= RESERVED_INHERIT; SYSTEM_LOCK(); K32OBJ_IncCount( ptr ); /* Don't try to allocate handle 0 */ entry = pdb->handle_table->entries + 1; for (h = 1; h < pdb->handle_table->count; h++, entry++) if (!entry->ptr) break; if ((h < pdb->handle_table->count) || HANDLE_GrowTable( pdb, HTABLE_INC )) { entry = &pdb->handle_table->entries[h]; entry->access = access; entry->ptr = ptr; entry->server = server_handle; SYSTEM_UNLOCK(); return h; } K32OBJ_DecCount( ptr ); SYSTEM_UNLOCK(); if (server_handle != -1) CLIENT_CloseHandle( server_handle ); SetLastError( ERROR_OUTOFMEMORY ); return INVALID_HANDLE_VALUE32; } /*********************************************************************** * HANDLE_GetObjPtr * * Retrieve a pointer to a kernel object and increments its reference count. * The refcount must be decremented when the pointer is no longer used. */ K32OBJ *HANDLE_GetObjPtr( PDB32 *pdb, HANDLE32 handle, K32OBJ_TYPE type, DWORD access, int *server_handle ) { K32OBJ *ptr = NULL; SYSTEM_LOCK(); if (HANDLE_IS_GLOBAL( handle )) { handle = HANDLE_GLOBAL_TO_LOCAL( handle ); pdb = PROCESS_Initial(); } if ((handle > 0) && (handle < pdb->handle_table->count)) { HANDLE_ENTRY *entry = &pdb->handle_table->entries[handle]; if ((entry->access & access) != access) WARN(win32, "Handle %08x bad access (acc=%08lx req=%08lx)\n", handle, entry->access, access ); ptr = entry->ptr; if (server_handle) *server_handle = entry->server; } else if (handle == CURRENT_THREAD_PSEUDOHANDLE) { ptr = (K32OBJ *)THREAD_Current(); if (server_handle) *server_handle = CURRENT_THREAD_PSEUDOHANDLE; } else if (handle == CURRENT_PROCESS_PSEUDOHANDLE) { ptr = (K32OBJ *)PROCESS_Current(); if (server_handle) *server_handle = CURRENT_PROCESS_PSEUDOHANDLE; } if (ptr && ((type == K32OBJ_UNKNOWN) || (ptr->type == type))) K32OBJ_IncCount( ptr ); else ptr = NULL; SYSTEM_UNLOCK(); if (!ptr) SetLastError( ERROR_INVALID_HANDLE ); return ptr; } /*********************************************************************** * HANDLE_GetServerHandle * * Retrieve the server handle associated to an object. */ int HANDLE_GetServerHandle( PDB32 *pdb, HANDLE32 handle, K32OBJ_TYPE type, DWORD access ) { int server_handle; K32OBJ *obj; SYSTEM_LOCK(); if ((obj = HANDLE_GetObjPtr( pdb, handle, type, access, &server_handle ))) K32OBJ_DecCount( obj ); else server_handle = -1; SYSTEM_UNLOCK(); return server_handle; } /********************************************************************* * HANDLE_GetAccess */ static BOOL32 HANDLE_GetAccess( PDB32 *pdb, HANDLE32 handle, LPDWORD access ) { BOOL32 ret = FALSE; SYSTEM_LOCK(); if ((handle > 0) && (handle < pdb->handle_table->count)) { HANDLE_ENTRY *entry = &pdb->handle_table->entries[handle]; if (entry->ptr) { *access = entry->access & ~RESERVED_ALL; ret = TRUE; } } SYSTEM_UNLOCK(); if (!ret) SetLastError( ERROR_INVALID_HANDLE ); return ret; } /********************************************************************* * HANDLE_Close */ static BOOL32 HANDLE_Close( PDB32 *pdb, HANDLE32 handle ) { BOOL32 ret = FALSE; K32OBJ *ptr; if (HANDLE_IS_GLOBAL( handle )) { handle = HANDLE_GLOBAL_TO_LOCAL( handle ); pdb = PROCESS_Initial(); } SYSTEM_LOCK(); if ((handle > 0) && (handle < pdb->handle_table->count)) { HANDLE_ENTRY *entry = &pdb->handle_table->entries[handle]; if ((ptr = entry->ptr)) { if (!(entry->access & RESERVED_CLOSE_PROTECT)) { entry->access = 0; entry->ptr = NULL; if (entry->server != -1) CLIENT_CloseHandle( entry->server ); K32OBJ_DecCount( ptr ); ret = TRUE; } /* FIXME: else SetLastError */ } } SYSTEM_UNLOCK(); if (!ret) SetLastError( ERROR_INVALID_HANDLE ); return ret; } /********************************************************************* * HANDLE_CloseAll * * Close all handles pointing to a given object (or all handles of the * process if the object is NULL) */ void HANDLE_CloseAll( PDB32 *pdb, K32OBJ *obj ) { HANDLE_ENTRY *entry; K32OBJ *ptr; HANDLE32 handle; SYSTEM_LOCK(); entry = pdb->handle_table->entries; for (handle = 0; handle < pdb->handle_table->count; handle++, entry++) { if (!(ptr = entry->ptr)) continue; /* empty slot */ if (obj && (ptr != obj)) continue; /* not the right object */ entry->access = 0; entry->ptr = NULL; if (entry->server != -1) CLIENT_CloseHandle( entry->server ); K32OBJ_DecCount( ptr ); } SYSTEM_UNLOCK(); } /********************************************************************* * CloseHandle (KERNEL32.23) */ BOOL32 WINAPI CloseHandle( HANDLE32 handle ) { return HANDLE_Close( PROCESS_Current(), handle ); } /********************************************************************* * GetHandleInformation (KERNEL32.336) */ BOOL32 WINAPI GetHandleInformation( HANDLE32 handle, LPDWORD flags ) { BOOL32 ret = FALSE; PDB32 *pdb = PROCESS_Current(); SYSTEM_LOCK(); if ((handle > 0) && (handle < pdb->handle_table->count)) { HANDLE_ENTRY *entry = &pdb->handle_table->entries[handle]; if (entry->ptr) { if (flags) *flags = (entry->access & RESERVED_ALL) >> RESERVED_SHIFT; ret = TRUE; } } SYSTEM_UNLOCK(); if (!ret) SetLastError( ERROR_INVALID_HANDLE ); return ret; } /********************************************************************* * SetHandleInformation (KERNEL32.653) */ BOOL32 WINAPI SetHandleInformation( HANDLE32 handle, DWORD mask, DWORD flags ) { BOOL32 ret = FALSE; PDB32 *pdb = PROCESS_Current(); mask = (mask << RESERVED_SHIFT) & RESERVED_ALL; flags = (flags << RESERVED_SHIFT) & RESERVED_ALL; SYSTEM_LOCK(); if ((handle > 0) && (handle < pdb->handle_table->count)) { HANDLE_ENTRY *entry = &pdb->handle_table->entries[handle]; if (entry->ptr) { entry->access = (entry->access & ~mask) | flags; ret = TRUE; } } SYSTEM_UNLOCK(); if (!ret) SetLastError( ERROR_INVALID_HANDLE ); return ret; } /********************************************************************* * DuplicateHandle (KERNEL32.192) */ BOOL32 WINAPI DuplicateHandle( HANDLE32 source_process, HANDLE32 source, HANDLE32 dest_process, HANDLE32 *dest, DWORD access, BOOL32 inherit, DWORD options ) { PDB32 *src_pdb = NULL, *dst_pdb = NULL; K32OBJ *obj = NULL; BOOL32 ret = FALSE; HANDLE32 handle; int src_process, src_handle, dst_process, dst_handle; SYSTEM_LOCK(); if (!(src_pdb = (PDB32 *)HANDLE_GetObjPtr( PROCESS_Current(), source_process, K32OBJ_PROCESS, PROCESS_DUP_HANDLE, &src_process ))) goto done; if (!(obj = HANDLE_GetObjPtr( src_pdb, source, K32OBJ_UNKNOWN, 0, &src_handle ))) goto done; /* Now that we are sure the source is valid, handle the options */ if (options & DUPLICATE_SAME_ACCESS) HANDLE_GetAccess( src_pdb, source, &access ); if (options & DUPLICATE_CLOSE_SOURCE) HANDLE_Close( src_pdb, source ); /* And duplicate the handle in the dest process */ if (!(dst_pdb = (PDB32 *)HANDLE_GetObjPtr( PROCESS_Current(), dest_process, K32OBJ_PROCESS, PROCESS_DUP_HANDLE, &dst_process ))) goto done; if ((src_process != -1) && (src_handle != -1) && (dst_process != -1)) dst_handle = CLIENT_DuplicateHandle( src_process, src_handle, dst_process, -1, access, inherit, options ); else dst_handle = -1; if ((handle = HANDLE_Alloc( dst_pdb, obj, access, inherit, dst_handle )) != INVALID_HANDLE_VALUE32) { if (dest) *dest = handle; ret = TRUE; } done: if (dst_pdb) K32OBJ_DecCount( &dst_pdb->header ); if (obj) K32OBJ_DecCount( obj ); if (src_pdb) K32OBJ_DecCount( &src_pdb->header ); SYSTEM_UNLOCK(); return ret; } /*********************************************************************** * ConvertToGlobalHandle (KERNEL32) */ HANDLE32 WINAPI ConvertToGlobalHandle(HANDLE32 hSrc) { int src_handle, dst_handle; HANDLE32 handle; K32OBJ *obj = NULL; DWORD access; if (HANDLE_IS_GLOBAL(hSrc)) return hSrc; if (!(obj = HANDLE_GetObjPtr( PROCESS_Current(), hSrc, K32OBJ_UNKNOWN, 0, &src_handle ))) return 0; HANDLE_GetAccess( PROCESS_Current(), hSrc, &access ); if (src_handle != -1) dst_handle = CLIENT_DuplicateHandle( GetCurrentProcess(), src_handle, -1, -1, 0, FALSE, DUP_HANDLE_MAKE_GLOBAL | DUP_HANDLE_SAME_ACCESS ); else dst_handle = -1; if ((handle = HANDLE_Alloc( PROCESS_Initial(), obj, access, FALSE, dst_handle )) != INVALID_HANDLE_VALUE32) handle = HANDLE_LOCAL_TO_GLOBAL(handle); else handle = 0; CloseHandle( hSrc ); return handle; }