| /* |
| * Win32 process handles |
| * |
| * Copyright 1998 Alexandre Julliard |
| */ |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include "debug.h" |
| #include "winbase.h" |
| #include "winerror.h" |
| #include "heap.h" |
| #include "process.h" |
| #include "server.h" |
| #include "thread.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<<RESERVED_SHIFT) |
| #define RESERVED_CLOSE_PROTECT (HANDLE_FLAG_PROTECT_FROM_CLOSE<<RESERVED_SHIFT) |
| |
| |
| /*********************************************************************** |
| * HANDLE_GrowTable |
| */ |
| static BOOL32 HANDLE_GrowTable( PDB32 *process, INT32 incr ) |
| { |
| HANDLE_TABLE *table; |
| |
| SYSTEM_LOCK(); |
| table = process->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_SetObjPtr |
| * |
| * Change the object pointer of a handle, and increment the refcount. |
| * Use with caution! |
| */ |
| BOOL32 HANDLE_SetObjPtr( PDB32 *pdb, HANDLE32 handle, K32OBJ *ptr, |
| DWORD access ) |
| { |
| BOOL32 ret = FALSE; |
| |
| SYSTEM_LOCK(); |
| if ((handle > 0) && (handle < pdb->handle_table->count)) |
| { |
| HANDLE_ENTRY *entry = &pdb->handle_table->entries[handle]; |
| K32OBJ *old_ptr = entry->ptr; |
| K32OBJ_IncCount( ptr ); |
| entry->access = access; |
| entry->ptr = ptr; |
| if (old_ptr) K32OBJ_DecCount( old_ptr ); |
| ret = TRUE; |
| } |
| SYSTEM_UNLOCK(); |
| if (!ret) SetLastError( ERROR_INVALID_HANDLE ); |
| return ret; |
| } |
| |
| |
| /********************************************************************* |
| * 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; |
| |
| 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 = PROCESS_GetPtr( source_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 = PROCESS_GetPtr( dest_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; |
| } |