Fixed typelib reading to correctly use the offset of the typelib that
[wine/multimedia.git] / server / handle.c
blob8619e8ba65f67cd1d777eaecfb008181ccae0e54
1 /*
2 * Server-side handle management
4 * Copyright (C) 1998 Alexandre Julliard
5 */
7 #include <assert.h>
8 #include <limits.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <stdlib.h>
13 #include "winerror.h"
14 #include "winbase.h"
16 #include "handle.h"
17 #include "process.h"
18 #include "thread.h"
19 #include "request.h"
21 struct handle_entry
23 struct object *ptr;
24 unsigned int access;
27 struct handle_table
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 */
37 static struct handle_table *global_table;
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)
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)
51 #define MIN_HANDLE_ENTRIES 32
54 static void handle_table_dump( struct object *obj, int verbose );
55 static void handle_table_destroy( struct object *obj );
57 static const struct object_ops handle_table_ops =
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
72 /* dump a handle table */
73 static void handle_table_dump( struct object *obj, int verbose )
75 int i;
76 struct handle_table *table = (struct handle_table *)obj;
77 struct handle_entry *entry = table->entries;
79 assert( obj->ops == &handle_table_ops );
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++)
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 );
93 /* destroy a handle table */
94 static void handle_table_destroy( struct object *obj )
96 int i;
97 struct handle_table *table = (struct handle_table *)obj;
98 struct handle_entry *entry = table->entries;
100 assert( obj->ops == &handle_table_ops );
102 for (i = 0; i <= table->last; i++, entry++)
104 struct object *obj = entry->ptr;
105 entry->ptr = NULL;
106 if (obj) release_object( obj );
108 free( table->entries );
111 /* allocate a new handle table */
112 struct object *alloc_handle_table( struct process *process, int count )
114 struct handle_table *table;
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;
128 /* grow a handle table */
129 static int grow_handle_table( struct handle_table *table )
131 struct handle_entry *new_entries;
132 int count = table->count;
134 if (count >= INT_MAX / 2) return 0;
135 count *= 2;
136 if (!(new_entries = realloc( table->entries, count * sizeof(struct handle_entry) )))
138 set_error( ERROR_OUTOFMEMORY );
139 return 0;
141 table->entries = new_entries;
142 table->count = count;
143 return 1;
146 /* find the first free entry in the handle table */
147 static struct handle_entry *get_free_entry( struct handle_table *table, int *phandle )
149 struct handle_entry *entry = table->entries + table->free;
150 int handle;
152 for (handle = table->free; handle <= table->last; handle++, entry++)
153 if (!entry->ptr) goto found;
154 if (handle >= table->count)
156 if (!grow_handle_table( table )) return NULL;
157 entry = table->entries + handle; /* the entries may have moved */
159 table->last = handle;
160 found:
161 table->free = *phandle = handle + 1; /* avoid handle 0 */
162 return entry;
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 )
169 struct handle_table *table = (struct handle_table *)process->handles;
170 struct handle_entry *entry;
171 int handle;
173 assert( table );
174 assert( !(access & RESERVED_ALL) );
175 if (inherit) access |= RESERVED_INHERIT;
177 if (!(entry = get_free_entry( table, &handle ))) return -1;
178 entry->ptr = grab_object( obj );
179 entry->access = access;
180 return handle;
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 )
187 struct handle_entry *entry;
188 int handle;
190 if (!global_table)
192 if (!(global_table = (struct handle_table *)alloc_handle_table( NULL, 0 ))) return -1;
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);
200 /* return an handle entry, or NULL if the handle is invalid */
201 static struct handle_entry *get_handle( struct process *process, int handle )
203 struct handle_table *table = (struct handle_table *)process->handles;
204 struct handle_entry *entry;
206 if (HANDLE_IS_GLOBAL(handle))
208 handle = HANDLE_GLOBAL_TO_LOCAL(handle);
209 table = global_table;
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;
219 error:
220 set_error( ERROR_INVALID_HANDLE );
221 return NULL;
224 /* attempt to shrink a table */
225 static void shrink_handle_table( struct handle_table *table )
227 struct handle_entry *entry = table->entries + table->last;
228 struct handle_entry *new_entries;
229 int count = table->count;
231 while (table->last >= 0)
233 if (entry->ptr) break;
234 table->last--;
235 entry--;
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;
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 )
249 struct handle_table *parent_table = (struct handle_table *)parent->handles;
250 struct handle_table *table;
251 int i;
253 assert( parent_table );
254 assert( parent_table->obj.ops == &handle_table_ops );
256 if (!(table = (struct handle_table *)alloc_handle_table( process, parent_table->count )))
257 return NULL;
259 if ((table->last = parent_table->last) >= 0)
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++)
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 */
270 /* attempt to shrink the table */
271 shrink_handle_table( table );
272 return &table->obj;
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 )
279 struct handle_table *table;
280 struct handle_entry *entry;
281 struct object *obj;
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;
294 /* close all the global handles */
295 void close_global_handles(void)
297 if (global_table)
299 release_object( global_table );
300 global_table = NULL;
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 )
308 struct handle_entry *entry;
309 struct object *obj;
311 switch( handle )
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)
323 set_error( ERROR_ACCESS_DENIED );
324 return NULL;
326 obj = entry->ptr;
327 break;
329 if (ops && (obj->ops != ops))
331 set_error( ERROR_INVALID_HANDLE ); /* not the right type */
332 return NULL;
334 return grab_object( obj );
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 )
341 struct handle_entry *entry;
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;
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 )
354 int res;
355 struct object *obj = get_handle_obj( src, src_handle, 0, NULL );
357 if (!obj) return -1;
358 if (options & DUP_HANDLE_SAME_ACCESS)
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 */
365 access = STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL;
366 clear_error();
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;
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 )
382 struct object *obj = find_object( name, len );
383 if (!obj)
385 set_error( ERROR_FILE_NOT_FOUND );
386 return -1;
388 if (ops && obj->ops != ops)
390 release_object( obj );
391 set_error( ERROR_INVALID_HANDLE ); /* FIXME: not the right type */
392 return -1;
394 return alloc_handle( current->process, obj, access, inherit );
397 /* close a handle */
398 DECL_HANDLER(close_handle)
400 close_handle( current->process, req->handle );
403 /* get information about a handle */
404 DECL_HANDLER(get_handle_info)
406 req->flags = set_handle_info( current->process, req->handle, 0, 0 );
409 /* set a handle information */
410 DECL_HANDLER(set_handle_info)
412 set_handle_info( current->process, req->handle, req->mask, req->flags );
415 /* duplicate a handle */
416 DECL_HANDLER(dup_handle)
418 struct process *src, *dst;
420 req->handle = -1;
421 if ((src = get_process_from_handle( req->src_process, PROCESS_DUP_HANDLE )))
423 if (req->options & DUP_HANDLE_MAKE_GLOBAL)
425 req->handle = duplicate_handle( src, req->src_handle, NULL,
426 req->access, req->inherit, req->options );
428 else if ((dst = get_process_from_handle( req->dst_process, PROCESS_DUP_HANDLE )))
430 req->handle = duplicate_handle( src, req->src_handle, dst,
431 req->access, req->inherit, req->options );
432 release_object( dst );
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 );