- New implementation of SendMessage, ReceiveMessage, ReplyMessage functions
[wine] / scheduler / handle.c
1 /*
2  * Win32 process handles
3  *
4  * Copyright 1998 Alexandre Julliard
5  */
6
7 #include <assert.h>
8 #include <stdio.h>
9 #include "winbase.h"
10 #include "winerror.h"
11 #include "heap.h"
12 #include "process.h"
13 #include "server.h"
14 #include "thread.h"
15 #include "debug.h"
16
17 #define HTABLE_SIZE  0x30  /* Handle table initial size */
18 #define HTABLE_INC   0x10  /* Handle table increment */
19
20 /* Reserved access rights */
21 #define RESERVED_ALL           (0x0007 << RESERVED_SHIFT)
22 #define RESERVED_SHIFT         25
23 #define RESERVED_INHERIT       (HANDLE_FLAG_INHERIT<<RESERVED_SHIFT)
24 #define RESERVED_CLOSE_PROTECT (HANDLE_FLAG_PROTECT_FROM_CLOSE<<RESERVED_SHIFT)
25
26
27 /***********************************************************************
28  *           HANDLE_GrowTable
29  */
30 static BOOL32 HANDLE_GrowTable( PDB32 *process, INT32 incr )
31 {
32     HANDLE_TABLE *table;
33
34     SYSTEM_LOCK();
35     table = process->handle_table;
36     table = HeapReAlloc( process->system_heap,
37                          HEAP_ZERO_MEMORY | HEAP_NO_SERIALIZE, table,
38                          sizeof(HANDLE_TABLE) +
39                          (table->count + incr - 1) * sizeof(HANDLE_ENTRY) );
40     if (table)
41     {
42         table->count += incr;
43         process->handle_table = table;
44     }
45     SYSTEM_UNLOCK();
46     return (table != NULL);
47 }
48
49
50 /***********************************************************************
51  *           HANDLE_CreateTable
52  *
53  * Create a process handle table, optionally inheriting the parent's handles.
54  */
55 BOOL32 HANDLE_CreateTable( PDB32 *pdb, BOOL32 inherit )
56 {
57     DWORD size;
58
59     /* Process must not already have a handle table */
60     assert( !pdb->handle_table );
61
62     /* If this is the first process, simply allocate a table */
63     if (!pdb->parent) inherit = FALSE;
64
65     SYSTEM_LOCK();
66     size = inherit ? pdb->parent->handle_table->count : HTABLE_SIZE;
67     if ((pdb->handle_table = HeapAlloc( pdb->system_heap,
68                                         HEAP_ZERO_MEMORY | HEAP_NO_SERIALIZE,
69                                         sizeof(HANDLE_TABLE) +
70                                         (size-1) * sizeof(HANDLE_ENTRY) )))
71     {
72         pdb->handle_table->count = size;
73         if (inherit)
74         {
75             HANDLE_ENTRY *src = pdb->parent->handle_table->entries;
76             HANDLE_ENTRY *dst = pdb->handle_table->entries;
77             HANDLE32 h;
78
79             for (h = 0; h < size; h++, src++, dst++)
80             {
81                 /* Check if handle is valid and inheritable */
82                 if (src->ptr && (src->access & RESERVED_INHERIT))
83                 {
84                     dst->access = src->access;
85                     dst->ptr    = src->ptr;
86                     dst->server = src->server;
87                     K32OBJ_IncCount( dst->ptr );
88                 }
89             }
90         }
91         /* Handle 1 is the process itself (unless the parent decided otherwise) */
92         if (!pdb->handle_table->entries[1].ptr)
93         {
94             pdb->handle_table->entries[1].ptr    = &pdb->header;
95             pdb->handle_table->entries[1].access = PROCESS_ALL_ACCESS;
96             pdb->handle_table->entries[1].server = -1;  /* FIXME */
97             K32OBJ_IncCount( &pdb->header );
98         }
99     }
100     SYSTEM_UNLOCK();
101     return (pdb->handle_table != NULL);
102 }
103
104
105 /***********************************************************************
106  *           HANDLE_Alloc
107  *
108  * Allocate a handle for a kernel object and increment its refcount.
109  */
110 HANDLE32 HANDLE_Alloc( PDB32 *pdb, K32OBJ *ptr, DWORD access,
111                        BOOL32 inherit, int server_handle )
112 {
113     HANDLE32 h;
114     HANDLE_ENTRY *entry;
115
116     assert( ptr );
117
118     /* Set the inherit reserved flag */
119     access &= ~RESERVED_ALL;
120     if (inherit) access |= RESERVED_INHERIT;
121
122     SYSTEM_LOCK();
123     K32OBJ_IncCount( ptr );
124     /* Don't try to allocate handle 0 */
125     entry = pdb->handle_table->entries + 1;
126     for (h = 1; h < pdb->handle_table->count; h++, entry++)
127         if (!entry->ptr) break;
128     if ((h < pdb->handle_table->count) || HANDLE_GrowTable( pdb, HTABLE_INC ))
129     {
130         entry = &pdb->handle_table->entries[h];
131         entry->access = access;
132         entry->ptr    = ptr;
133         entry->server = server_handle;
134         SYSTEM_UNLOCK();
135         return h;
136     }
137     K32OBJ_DecCount( ptr );
138     SYSTEM_UNLOCK();
139     if (server_handle != -1) CLIENT_CloseHandle( server_handle );
140     SetLastError( ERROR_OUTOFMEMORY );
141     return INVALID_HANDLE_VALUE32;
142 }
143
144
145 /***********************************************************************
146  *           HANDLE_GetObjPtr
147  *
148  * Retrieve a pointer to a kernel object and increments its reference count.
149  * The refcount must be decremented when the pointer is no longer used.
150  */
151 K32OBJ *HANDLE_GetObjPtr( PDB32 *pdb, HANDLE32 handle,
152                           K32OBJ_TYPE type, DWORD access,
153                           int *server_handle )
154 {
155     K32OBJ *ptr = NULL;
156
157     SYSTEM_LOCK();
158     if (HANDLE_IS_GLOBAL( handle ))
159     {
160         handle = HANDLE_GLOBAL_TO_LOCAL( handle );
161         pdb = PROCESS_Initial();
162     }
163     if ((handle > 0) && (handle < pdb->handle_table->count))
164     {
165         HANDLE_ENTRY *entry = &pdb->handle_table->entries[handle];
166         if ((entry->access & access) != access)
167             WARN(win32, "Handle %08x bad access (acc=%08lx req=%08lx)\n",
168                      handle, entry->access, access );
169         ptr = entry->ptr;
170         if (server_handle) *server_handle = entry->server;
171     }
172     else if (handle == CURRENT_THREAD_PSEUDOHANDLE)
173     {
174        ptr = (K32OBJ *)THREAD_Current();
175        if (server_handle) *server_handle = CURRENT_THREAD_PSEUDOHANDLE;
176     }
177     else if (handle == CURRENT_PROCESS_PSEUDOHANDLE)
178     {
179        ptr = (K32OBJ *)PROCESS_Current();
180        if (server_handle) *server_handle = CURRENT_PROCESS_PSEUDOHANDLE;
181     }
182
183     if (ptr && ((type == K32OBJ_UNKNOWN) || (ptr->type == type)))
184         K32OBJ_IncCount( ptr );
185     else
186         ptr = NULL;
187
188     SYSTEM_UNLOCK();
189     if (!ptr) SetLastError( ERROR_INVALID_HANDLE );
190     return ptr;
191 }
192
193
194 /***********************************************************************
195  *           HANDLE_GetServerHandle
196  *
197  * Retrieve the server handle associated to an object.
198  */
199 int HANDLE_GetServerHandle( PDB32 *pdb, HANDLE32 handle,
200                             K32OBJ_TYPE type, DWORD access )
201 {
202     int server_handle;
203     K32OBJ *obj;
204
205     SYSTEM_LOCK();
206     if ((obj = HANDLE_GetObjPtr( pdb, handle, type, access, &server_handle )))
207         K32OBJ_DecCount( obj );
208     else
209         server_handle = -1;
210     SYSTEM_UNLOCK();
211     return server_handle;
212 }
213
214
215 /*********************************************************************
216  *           HANDLE_GetAccess
217  */
218 static BOOL32 HANDLE_GetAccess( PDB32 *pdb, HANDLE32 handle, LPDWORD access )
219 {
220     BOOL32 ret = FALSE;
221
222     SYSTEM_LOCK();
223     if ((handle > 0) && (handle < pdb->handle_table->count))
224     {
225         HANDLE_ENTRY *entry = &pdb->handle_table->entries[handle];
226         if (entry->ptr)
227         {
228             *access = entry->access & ~RESERVED_ALL;
229             ret = TRUE;
230         }
231     }
232     SYSTEM_UNLOCK();
233     if (!ret) SetLastError( ERROR_INVALID_HANDLE );
234     return ret;
235 }
236
237
238 /*********************************************************************
239  *           HANDLE_Close
240  */
241 static BOOL32 HANDLE_Close( PDB32 *pdb, HANDLE32 handle )
242 {
243     BOOL32 ret = FALSE;
244     K32OBJ *ptr;
245
246     if (HANDLE_IS_GLOBAL( handle ))
247     {
248         handle = HANDLE_GLOBAL_TO_LOCAL( handle );
249         pdb = PROCESS_Initial();
250     }
251     SYSTEM_LOCK();
252     if ((handle > 0) && (handle < pdb->handle_table->count))
253     {
254         HANDLE_ENTRY *entry = &pdb->handle_table->entries[handle];
255         if ((ptr = entry->ptr))
256         {
257             if (!(entry->access & RESERVED_CLOSE_PROTECT))
258             {
259                 entry->access = 0;
260                 entry->ptr    = NULL;
261                 if (entry->server != -1)
262                     CLIENT_CloseHandle( entry->server );
263                 K32OBJ_DecCount( ptr );
264                 ret = TRUE;
265             }
266             /* FIXME: else SetLastError */
267         }
268     }
269     SYSTEM_UNLOCK();
270     if (!ret) SetLastError( ERROR_INVALID_HANDLE );
271     return ret;
272 }
273
274
275 /*********************************************************************
276  *           HANDLE_CloseAll
277  *
278  * Close all handles pointing to a given object (or all handles of the
279  * process if the object is NULL)
280  */
281 void HANDLE_CloseAll( PDB32 *pdb, K32OBJ *obj )
282 {
283     HANDLE_ENTRY *entry;
284     K32OBJ *ptr;
285     HANDLE32 handle;
286
287     SYSTEM_LOCK();
288     entry = pdb->handle_table->entries;
289     for (handle = 0; handle < pdb->handle_table->count; handle++, entry++)
290     {
291         if (!(ptr = entry->ptr)) continue;  /* empty slot */
292         if (obj && (ptr != obj)) continue;  /* not the right object */
293         entry->access = 0;
294         entry->ptr    = NULL;
295         if (entry->server != -1) CLIENT_CloseHandle( entry->server );
296         K32OBJ_DecCount( ptr );
297     }
298     SYSTEM_UNLOCK();
299 }
300
301
302 /*********************************************************************
303  *           CloseHandle   (KERNEL32.23)
304  */
305 BOOL32 WINAPI CloseHandle( HANDLE32 handle )
306 {
307     return HANDLE_Close( PROCESS_Current(), handle );
308 }
309
310
311 /*********************************************************************
312  *           GetHandleInformation   (KERNEL32.336)
313  */
314 BOOL32 WINAPI GetHandleInformation( HANDLE32 handle, LPDWORD flags )
315 {
316     BOOL32 ret = FALSE;
317     PDB32 *pdb = PROCESS_Current();
318
319     SYSTEM_LOCK();
320     if ((handle > 0) && (handle < pdb->handle_table->count))
321     {
322         HANDLE_ENTRY *entry = &pdb->handle_table->entries[handle];
323         if (entry->ptr)
324         {
325             if (flags)
326                 *flags = (entry->access & RESERVED_ALL) >> RESERVED_SHIFT;
327             ret = TRUE;
328         }
329     }
330     SYSTEM_UNLOCK();
331     if (!ret) SetLastError( ERROR_INVALID_HANDLE );
332     return ret;
333 }
334
335
336 /*********************************************************************
337  *           SetHandleInformation   (KERNEL32.653)
338  */
339 BOOL32 WINAPI SetHandleInformation( HANDLE32 handle, DWORD mask, DWORD flags )
340 {
341     BOOL32 ret = FALSE;
342     PDB32 *pdb = PROCESS_Current();
343
344     mask  = (mask << RESERVED_SHIFT) & RESERVED_ALL;
345     flags = (flags << RESERVED_SHIFT) & RESERVED_ALL;
346     SYSTEM_LOCK();
347     if ((handle > 0) && (handle < pdb->handle_table->count))
348     {
349         HANDLE_ENTRY *entry = &pdb->handle_table->entries[handle];
350         if (entry->ptr)
351         {
352             entry->access = (entry->access & ~mask) | flags;
353             ret = TRUE;
354         }
355     }
356     SYSTEM_UNLOCK();
357     if (!ret) SetLastError( ERROR_INVALID_HANDLE );
358     return ret;
359 }
360
361
362 /*********************************************************************
363  *           DuplicateHandle   (KERNEL32.192)
364  */
365 BOOL32 WINAPI DuplicateHandle( HANDLE32 source_process, HANDLE32 source,
366                                HANDLE32 dest_process, HANDLE32 *dest,
367                                DWORD access, BOOL32 inherit, DWORD options )
368 {
369     PDB32 *src_pdb = NULL, *dst_pdb = NULL;
370     K32OBJ *obj = NULL;
371     BOOL32 ret = FALSE;
372     HANDLE32 handle;
373     int src_process, src_handle, dst_process, dst_handle;
374
375     SYSTEM_LOCK();
376
377     if (!(src_pdb = (PDB32 *)HANDLE_GetObjPtr( PROCESS_Current(), source_process,
378                                     K32OBJ_PROCESS, PROCESS_DUP_HANDLE, &src_process )))
379         goto done;
380     if (!(obj = HANDLE_GetObjPtr( src_pdb, source, K32OBJ_UNKNOWN, 0, &src_handle )))
381         goto done;
382
383     /* Now that we are sure the source is valid, handle the options */
384
385     if (options & DUPLICATE_SAME_ACCESS)
386         HANDLE_GetAccess( src_pdb, source, &access );
387     if (options & DUPLICATE_CLOSE_SOURCE)
388         HANDLE_Close( src_pdb, source );
389
390     /* And duplicate the handle in the dest process */
391
392     if (!(dst_pdb = (PDB32 *)HANDLE_GetObjPtr( PROCESS_Current(), dest_process,
393                                     K32OBJ_PROCESS, PROCESS_DUP_HANDLE, &dst_process )))
394         goto done;
395
396     if ((src_process != -1) && (src_handle != -1) && (dst_process != -1))
397         dst_handle = CLIENT_DuplicateHandle( src_process, src_handle, dst_process, -1,
398                                              access, inherit, options );
399     else
400         dst_handle = -1;
401
402     if ((handle = HANDLE_Alloc( dst_pdb, obj, access, inherit,
403                                 dst_handle )) != INVALID_HANDLE_VALUE32)
404     {
405         if (dest) *dest = handle;
406         ret = TRUE;
407     }
408
409 done:
410     if (dst_pdb) K32OBJ_DecCount( &dst_pdb->header );
411     if (obj) K32OBJ_DecCount( obj );
412     if (src_pdb) K32OBJ_DecCount( &src_pdb->header );
413     SYSTEM_UNLOCK();
414     return ret;
415 }
416
417
418 /***********************************************************************
419  *           ConvertToGlobalHandle              (KERNEL32)
420  */
421 HANDLE32 WINAPI ConvertToGlobalHandle(HANDLE32 hSrc)
422 {
423     int src_handle, dst_handle;
424     HANDLE32 handle;
425     K32OBJ *obj = NULL;
426     DWORD access;
427
428     if (HANDLE_IS_GLOBAL(hSrc))
429         return hSrc;
430
431     if (!(obj = HANDLE_GetObjPtr( PROCESS_Current(), hSrc, K32OBJ_UNKNOWN, 0, &src_handle )))
432         return 0;
433
434     HANDLE_GetAccess( PROCESS_Current(), hSrc, &access );
435
436     if (src_handle != -1)
437         dst_handle = CLIENT_DuplicateHandle( GetCurrentProcess(), src_handle, -1, -1, 0, FALSE,
438                                              DUP_HANDLE_MAKE_GLOBAL | DUP_HANDLE_SAME_ACCESS );
439     else
440         dst_handle = -1;
441
442     if ((handle = HANDLE_Alloc( PROCESS_Initial(), obj, access, FALSE,
443                                 dst_handle )) != INVALID_HANDLE_VALUE32)
444         handle = HANDLE_LOCAL_TO_GLOBAL(handle);
445     else
446         handle = 0;
447
448     CloseHandle( hSrc );
449     return handle;
450 }