451 lines
13 KiB
C
451 lines
13 KiB
C
/*
|
|
* Win32 process handles
|
|
*
|
|
* Copyright 1998 Alexandre Julliard
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#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<<RESERVED_SHIFT)
|
|
#define RESERVED_CLOSE_PROTECT (HANDLE_FLAG_PROTECT_FROM_CLOSE<<RESERVED_SHIFT)
|
|
|
|
|
|
/***********************************************************************
|
|
* HANDLE_GrowTable
|
|
*/
|
|
static BOOL HANDLE_GrowTable( PDB *process, INT 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.
|
|
*/
|
|
BOOL HANDLE_CreateTable( PDB *pdb, BOOL 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;
|
|
HANDLE 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.
|
|
*/
|
|
HANDLE HANDLE_Alloc( PDB *pdb, K32OBJ *ptr, DWORD access,
|
|
BOOL inherit, int server_handle )
|
|
{
|
|
HANDLE 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_VALUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* 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( PDB *pdb, HANDLE 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( PDB *pdb, HANDLE 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 BOOL HANDLE_GetAccess( PDB *pdb, HANDLE handle, LPDWORD access )
|
|
{
|
|
BOOL 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 BOOL HANDLE_Close( PDB *pdb, HANDLE handle )
|
|
{
|
|
BOOL 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( PDB *pdb, K32OBJ *obj )
|
|
{
|
|
HANDLE_ENTRY *entry;
|
|
K32OBJ *ptr;
|
|
HANDLE 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)
|
|
*/
|
|
BOOL WINAPI CloseHandle( HANDLE handle )
|
|
{
|
|
return HANDLE_Close( PROCESS_Current(), handle );
|
|
}
|
|
|
|
|
|
/*********************************************************************
|
|
* GetHandleInformation (KERNEL32.336)
|
|
*/
|
|
BOOL WINAPI GetHandleInformation( HANDLE handle, LPDWORD flags )
|
|
{
|
|
BOOL ret = FALSE;
|
|
PDB *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)
|
|
*/
|
|
BOOL WINAPI SetHandleInformation( HANDLE handle, DWORD mask, DWORD flags )
|
|
{
|
|
BOOL ret = FALSE;
|
|
PDB *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)
|
|
*/
|
|
BOOL WINAPI DuplicateHandle( HANDLE source_process, HANDLE source,
|
|
HANDLE dest_process, HANDLE *dest,
|
|
DWORD access, BOOL inherit, DWORD options )
|
|
{
|
|
PDB *src_pdb = NULL, *dst_pdb = NULL;
|
|
K32OBJ *obj = NULL;
|
|
BOOL ret = FALSE;
|
|
HANDLE handle;
|
|
int src_process, src_handle, dst_process, dst_handle;
|
|
|
|
SYSTEM_LOCK();
|
|
|
|
if (!(src_pdb = (PDB *)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 = (PDB *)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_VALUE)
|
|
{
|
|
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)
|
|
*/
|
|
HANDLE WINAPI ConvertToGlobalHandle(HANDLE hSrc)
|
|
{
|
|
int src_handle, dst_handle;
|
|
HANDLE 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_VALUE)
|
|
handle = HANDLE_LOCAL_TO_GLOBAL(handle);
|
|
else
|
|
handle = 0;
|
|
|
|
CloseHandle( hSrc );
|
|
return handle;
|
|
}
|