Moved all signal support to a new platform-specific file.
[wine] / server / handle.c
1 /*
2  * Server-side handle management
3  *
4  * Copyright (C) 1998 Alexandre Julliard
5  */
6
7 #include <assert.h>
8 #include <limits.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12
13 #include "winerror.h"
14 #include "winbase.h"
15
16 #include "handle.h"
17 #include "process.h"
18 #include "thread.h"
19 #include "request.h"
20
21 struct handle_entry
22 {
23     struct object *ptr;
24     unsigned int   access;
25 };
26
27 struct handle_table
28 {
29     struct object        obj;         /* object header */
30     struct process      *process;     /* process owning this table */
31     int                  count;       /* number of allocated entries */
32     int                  last;        /* last used entry */
33     int                  free;        /* first entry that may be free */
34     struct handle_entry *entries;     /* handle entries */
35 };
36
37 static struct handle_table *global_table;
38
39 /* reserved handle access rights */
40 #define RESERVED_SHIFT         25
41 #define RESERVED_INHERIT       (HANDLE_FLAG_INHERIT << RESERVED_SHIFT)
42 #define RESERVED_CLOSE_PROTECT (HANDLE_FLAG_PROTECT_FROM_CLOSE << RESERVED_SHIFT)
43 #define RESERVED_ALL           (RESERVED_INHERIT | RESERVED_CLOSE_PROTECT)
44
45 /* global handle macros */
46 #define HANDLE_OBFUSCATOR         0x544a4def
47 #define HANDLE_IS_GLOBAL(h)       (((h) ^ HANDLE_OBFUSCATOR) < 0x10000)
48 #define HANDLE_LOCAL_TO_GLOBAL(h) ((h) ^ HANDLE_OBFUSCATOR)
49 #define HANDLE_GLOBAL_TO_LOCAL(h) ((h) ^ HANDLE_OBFUSCATOR)
50
51 #define MIN_HANDLE_ENTRIES  32
52
53
54 static void handle_table_dump( struct object *obj, int verbose );
55 static void handle_table_destroy( struct object *obj );
56
57 static const struct object_ops handle_table_ops =
58 {
59     sizeof(struct handle_table),
60     handle_table_dump,
61     no_add_queue,
62     NULL,  /* should never get called */
63     NULL,  /* should never get called */
64     NULL,  /* should never get called */
65     no_read_fd,
66     no_write_fd,
67     no_flush,
68     no_get_file_info,
69     handle_table_destroy
70 };
71
72 /* dump a handle table */
73 static void handle_table_dump( struct object *obj, int verbose )
74 {
75     int i;
76     struct handle_table *table = (struct handle_table *)obj;
77     struct handle_entry *entry = table->entries;
78
79     assert( obj->ops == &handle_table_ops );
80
81     fprintf( stderr, "Handle table last=%d count=%d process=%p\n",
82              table->last, table->count, table->process );
83     if (!verbose) return;
84     entry = table->entries;
85     for (i = 0; i <= table->last; i++, entry++)
86     {
87         if (!entry->ptr) continue;
88         fprintf( stderr, "%9d: %p %08x ", i + 1, entry->ptr, entry->access );
89         entry->ptr->ops->dump( entry->ptr, 0 );
90     }
91 }
92
93 /* destroy a handle table */
94 static void handle_table_destroy( struct object *obj )
95 {
96     int i;
97     struct handle_table *table = (struct handle_table *)obj;
98     struct handle_entry *entry = table->entries;
99
100     assert( obj->ops == &handle_table_ops );
101
102     for (i = 0; i <= table->last; i++, entry++)
103     {
104         struct object *obj = entry->ptr;
105         entry->ptr = NULL;
106         if (obj) release_object( obj );
107     }
108     free( table->entries );
109 }
110
111 /* allocate a new handle table */
112 struct object *alloc_handle_table( struct process *process, int count )
113 {
114     struct handle_table *table;
115
116     if (count < MIN_HANDLE_ENTRIES) count = MIN_HANDLE_ENTRIES;
117     if (!(table = alloc_object( &handle_table_ops )))
118         return NULL;
119     table->process = process;
120     table->count   = count;
121     table->last    = -1;
122     table->free    = 0;
123     if ((table->entries = mem_alloc( count * sizeof(*table->entries) ))) return &table->obj;
124     release_object( table );
125     return NULL;
126 }
127
128 /* grow a handle table */
129 static int grow_handle_table( struct handle_table *table )
130 {
131     struct handle_entry *new_entries;
132     int count = table->count;
133
134     if (count >= INT_MAX / 2) return 0;
135     count *= 2;
136     if (!(new_entries = realloc( table->entries, count * sizeof(struct handle_entry) )))
137     {
138         set_error( ERROR_OUTOFMEMORY );
139         return 0;
140     }
141     table->entries = new_entries;
142     table->count   = count;
143     return 1;
144 }
145
146 /* find the first free entry in the handle table */
147 static struct handle_entry *get_free_entry( struct handle_table *table, int *phandle )
148 {
149     struct handle_entry *entry = table->entries + table->free;
150     int handle;
151
152     for (handle = table->free; handle <= table->last; handle++, entry++)
153         if (!entry->ptr) goto found;
154     if (handle >= table->count)
155     {
156         if (!grow_handle_table( table )) return NULL;
157         entry = table->entries + handle;  /* the entries may have moved */
158     }
159     table->last = handle;
160  found:
161     table->free = *phandle = handle + 1;  /* avoid handle 0 */
162     return entry;
163 }
164
165 /* allocate a handle for an object, incrementing its refcount */
166 /* return the handle, or -1 on error */
167 int alloc_handle( struct process *process, void *obj, unsigned int access, int inherit )
168 {
169     struct handle_table *table = (struct handle_table *)process->handles;
170     struct handle_entry *entry;
171     int handle;
172
173     assert( table );
174     assert( !(access & RESERVED_ALL) );
175     if (inherit) access |= RESERVED_INHERIT;
176
177     if (!(entry = get_free_entry( table, &handle ))) return -1;
178     entry->ptr    = grab_object( obj );
179     entry->access = access;
180     return handle;
181 }
182
183 /* allocate a global handle for an object, incrementing its refcount */
184 /* return the handle, or -1 on error */
185 static int alloc_global_handle( void *obj, unsigned int access )
186 {
187     struct handle_entry *entry;
188     int handle;
189
190     if (!global_table)
191     {
192         if (!(global_table = (struct handle_table *)alloc_handle_table( NULL, 0 ))) return -1;
193     }
194     if (!(entry = get_free_entry( global_table, &handle ))) return -1;
195     entry->ptr    = grab_object( obj );
196     entry->access = access;
197     return HANDLE_LOCAL_TO_GLOBAL(handle);
198 }
199
200 /* return an handle entry, or NULL if the handle is invalid */
201 static struct handle_entry *get_handle( struct process *process, int handle )
202 {
203     struct handle_table *table = (struct handle_table *)process->handles;
204     struct handle_entry *entry;
205
206     if (HANDLE_IS_GLOBAL(handle))
207     {
208         handle = HANDLE_GLOBAL_TO_LOCAL(handle);
209         table = global_table;
210     }
211     if (!table) goto error;
212     handle--;  /* handles start at 1 */
213     if (handle < 0) goto error;
214     if (handle > table->last) goto error;
215     entry = table->entries + handle;
216     if (!entry->ptr) goto error;
217     return entry;
218
219  error:
220     set_error( ERROR_INVALID_HANDLE );
221     return NULL;
222 }
223
224 /* attempt to shrink a table */
225 static void shrink_handle_table( struct handle_table *table )
226 {
227     struct handle_entry *entry = table->entries + table->last;
228     struct handle_entry *new_entries;
229     int count = table->count;
230
231     while (table->last >= 0)
232     {
233         if (entry->ptr) break;
234         table->last--;
235         entry--;
236     }
237     if (table->last >= count / 4) return;  /* no need to shrink */
238     if (count < MIN_HANDLE_ENTRIES * 2) return;  /* too small to shrink */
239     count /= 2;
240     if (!(new_entries = realloc( table->entries, count * sizeof(*new_entries) ))) return;
241     table->count   = count;
242     table->entries = new_entries;
243 }
244
245 /* copy the handle table of the parent process */
246 /* return 1 if OK, 0 on error */
247 struct object *copy_handle_table( struct process *process, struct process *parent )
248 {
249     struct handle_table *parent_table = (struct handle_table *)parent->handles;
250     struct handle_table *table;
251     int i;
252
253     assert( parent_table );
254     assert( parent_table->obj.ops == &handle_table_ops );
255
256     if (!(table = (struct handle_table *)alloc_handle_table( process, parent_table->count )))
257         return NULL;
258
259     if ((table->last = parent_table->last) >= 0)
260     {
261         struct handle_entry *ptr = table->entries;
262         memcpy( ptr, parent_table->entries, (table->last + 1) * sizeof(struct handle_entry) );
263         for (i = 0; i <= table->last; i++, ptr++)
264         {
265             if (!ptr->ptr) continue;
266             if (ptr->access & RESERVED_INHERIT) grab_object( ptr->ptr );
267             else ptr->ptr = NULL; /* don't inherit this entry */
268         }
269     }
270     /* attempt to shrink the table */
271     shrink_handle_table( table );
272     return &table->obj;
273 }
274
275 /* close a handle and decrement the refcount of the associated object */
276 /* return 1 if OK, 0 on error */
277 int close_handle( struct process *process, int handle )
278 {
279     struct handle_table *table;
280     struct handle_entry *entry;
281     struct object *obj;
282
283     if (!(entry = get_handle( process, handle ))) return 0;
284     if (entry->access & RESERVED_CLOSE_PROTECT) return 0;  /* FIXME: error code */
285     obj = entry->ptr;
286     entry->ptr = NULL;
287     table = HANDLE_IS_GLOBAL(handle) ? global_table : (struct handle_table *)process->handles;
288     if (entry < table->entries + table->free) table->free = entry - table->entries;
289     if (entry == table->entries + table->last) shrink_handle_table( table );
290     release_object( obj );
291     return 1;
292 }
293
294 /* close all the global handles */
295 void close_global_handles(void)
296 {
297     if (global_table)
298     {
299         release_object( global_table );
300         global_table = NULL;
301     }
302 }
303
304 /* retrieve the object corresponding to a handle, incrementing its refcount */
305 struct object *get_handle_obj( struct process *process, int handle,
306                                unsigned int access, const struct object_ops *ops )
307 {
308     struct handle_entry *entry;
309     struct object *obj;
310
311     switch( handle )
312     {
313     case 0xfffffffe:  /* current thread pseudo-handle */
314         obj = &current->obj;
315         break;
316     case 0x7fffffff:  /* current process pseudo-handle */
317         obj = (struct object *)current->process;
318         break;
319     default:
320         if (!(entry = get_handle( process, handle ))) return NULL;
321         if ((entry->access & access) != access)
322         {
323             set_error( ERROR_ACCESS_DENIED );
324             return NULL;
325         }
326         obj = entry->ptr;
327         break;
328     }
329     if (ops && (obj->ops != ops))
330     {
331         set_error( ERROR_INVALID_HANDLE );  /* not the right type */
332         return NULL;
333     }
334     return grab_object( obj );
335 }
336
337 /* get/set the handle reserved flags */
338 /* return the new flags (or -1 on error) */
339 static int set_handle_info( struct process *process, int handle, int mask, int flags )
340 {
341     struct handle_entry *entry;
342
343     if (!(entry = get_handle( process, handle ))) return -1;
344     mask  = (mask << RESERVED_SHIFT) & RESERVED_ALL;
345     flags = (flags << RESERVED_SHIFT) & mask;
346     entry->access = (entry->access & ~mask) | flags;
347     return (entry->access & RESERVED_ALL) >> RESERVED_SHIFT;
348 }
349
350 /* duplicate a handle */
351 int duplicate_handle( struct process *src, int src_handle, struct process *dst,
352                       unsigned int access, int inherit, int options )
353 {
354     int res;
355     struct object *obj = get_handle_obj( src, src_handle, 0, NULL );
356
357     if (!obj) return -1;
358     if (options & DUP_HANDLE_SAME_ACCESS)
359     {
360         struct handle_entry *entry = get_handle( src, src_handle );
361         if (entry)
362             access = entry->access;
363         else  /* pseudo-handle, give it full access */
364         {
365             access = STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL;
366             clear_error();
367         }
368     }
369     access &= ~RESERVED_ALL;
370     if (options & DUP_HANDLE_MAKE_GLOBAL)
371         res = alloc_global_handle( obj, access );
372     else
373         res = alloc_handle( dst, obj, access, inherit );
374     release_object( obj );
375     return res;
376 }
377
378 /* open a new handle to an existing object */
379 int open_object( const char *name, size_t len, const struct object_ops *ops,
380                  unsigned int access, int inherit )
381 {
382     struct object *obj = find_object( name, len );
383     if (!obj) 
384     {
385         set_error( ERROR_FILE_NOT_FOUND );
386         return -1;
387     }
388     if (ops && obj->ops != ops)
389     {
390         release_object( obj );
391         set_error( ERROR_INVALID_HANDLE );  /* FIXME: not the right type */ 
392         return -1;
393     }
394     return alloc_handle( current->process, obj, access, inherit );
395 }
396
397 /* close a handle */
398 DECL_HANDLER(close_handle)
399 {
400     close_handle( current->process, req->handle );
401 }
402
403 /* get information about a handle */
404 DECL_HANDLER(get_handle_info)
405 {
406     req->flags = set_handle_info( current->process, req->handle, 0, 0 );
407 }
408
409 /* set a handle information */
410 DECL_HANDLER(set_handle_info)
411 {
412     set_handle_info( current->process, req->handle, req->mask, req->flags );
413 }
414
415 /* duplicate a handle */
416 DECL_HANDLER(dup_handle)
417 {
418     struct process *src, *dst;
419
420     req->handle = -1;
421     if ((src = get_process_from_handle( req->src_process, PROCESS_DUP_HANDLE )))
422     {
423         if (req->options & DUP_HANDLE_MAKE_GLOBAL)
424         {
425             req->handle = duplicate_handle( src, req->src_handle, NULL,
426                                             req->access, req->inherit, req->options );
427         }
428         else if ((dst = get_process_from_handle( req->dst_process, PROCESS_DUP_HANDLE )))
429         {
430             req->handle = duplicate_handle( src, req->src_handle, dst,
431                                             req->access, req->inherit, req->options );
432             release_object( dst );
433         }
434         /* close the handle no matter what happened */
435         if (req->options & DUP_HANDLE_CLOSE_SOURCE)
436             close_handle( src, req->src_handle );
437         release_object( src );
438     }
439 }