2 * Server-side handle management
4 * Copyright (C) 1998 Alexandre Julliard
28 struct object obj
; /* object header */
29 struct process
*process
; /* process owning this table */
30 int count
; /* number of allocated entries */
31 int last
; /* last used entry */
32 int free
; /* first entry that may be free */
33 struct handle_entry
*entries
; /* handle entries */
36 static struct handle_table
*global_table
;
38 /* reserved handle access rights */
39 #define RESERVED_SHIFT 25
40 #define RESERVED_INHERIT (HANDLE_FLAG_INHERIT << RESERVED_SHIFT)
41 #define RESERVED_CLOSE_PROTECT (HANDLE_FLAG_PROTECT_FROM_CLOSE << RESERVED_SHIFT)
42 #define RESERVED_ALL (RESERVED_INHERIT | RESERVED_CLOSE_PROTECT)
44 /* global handle macros */
45 #define HANDLE_OBFUSCATOR 0x544a4def
46 #define HANDLE_IS_GLOBAL(h) (((h) ^ HANDLE_OBFUSCATOR) < 0x10000)
47 #define HANDLE_LOCAL_TO_GLOBAL(h) ((h) ^ HANDLE_OBFUSCATOR)
48 #define HANDLE_GLOBAL_TO_LOCAL(h) ((h) ^ HANDLE_OBFUSCATOR)
50 #define MIN_HANDLE_ENTRIES 32
53 /* handle to table index conversion */
55 /* handles are a multiple of 4 under NT; handle 0 is not used */
56 inline static int index_to_handle( int index
)
58 return (index
+ 1) << 2;
60 inline static int handle_to_index( int handle
)
62 return (handle
>> 2) - 1;
66 static void handle_table_dump( struct object
*obj
, int verbose
);
67 static void handle_table_destroy( struct object
*obj
);
69 static const struct object_ops handle_table_ops
=
71 sizeof(struct handle_table
), /* size */
72 handle_table_dump
, /* dump */
73 no_add_queue
, /* add_queue */
74 NULL
, /* remove_queue */
77 NULL
, /* get_poll_events */
78 NULL
, /* poll_event */
79 no_read_fd
, /* get_read_fd */
80 no_write_fd
, /* get_write_fd */
82 no_get_file_info
, /* get_file_info */
83 handle_table_destroy
/* destroy */
86 /* dump a handle table */
87 static void handle_table_dump( struct object
*obj
, int verbose
)
90 struct handle_table
*table
= (struct handle_table
*)obj
;
91 struct handle_entry
*entry
= table
->entries
;
93 assert( obj
->ops
== &handle_table_ops
);
95 fprintf( stderr
, "Handle table last=%d count=%d process=%p\n",
96 table
->last
, table
->count
, table
->process
);
98 entry
= table
->entries
;
99 for (i
= 0; i
<= table
->last
; i
++, entry
++)
101 if (!entry
->ptr
) continue;
102 fprintf( stderr
, "%9d: %p %08x ", index_to_handle(i
), entry
->ptr
, entry
->access
);
103 entry
->ptr
->ops
->dump( entry
->ptr
, 0 );
107 /* destroy a handle table */
108 static void handle_table_destroy( struct object
*obj
)
111 struct handle_table
*table
= (struct handle_table
*)obj
;
112 struct handle_entry
*entry
= table
->entries
;
114 assert( obj
->ops
== &handle_table_ops
);
116 for (i
= 0; i
<= table
->last
; i
++, entry
++)
118 struct object
*obj
= entry
->ptr
;
120 if (obj
) release_object( obj
);
122 free( table
->entries
);
125 /* allocate a new handle table */
126 struct object
*alloc_handle_table( struct process
*process
, int count
)
128 struct handle_table
*table
;
130 if (count
< MIN_HANDLE_ENTRIES
) count
= MIN_HANDLE_ENTRIES
;
131 if (!(table
= alloc_object( &handle_table_ops
, -1 )))
133 table
->process
= process
;
134 table
->count
= count
;
137 if ((table
->entries
= mem_alloc( count
* sizeof(*table
->entries
) ))) return &table
->obj
;
138 release_object( table
);
142 /* grow a handle table */
143 static int grow_handle_table( struct handle_table
*table
)
145 struct handle_entry
*new_entries
;
146 int count
= table
->count
;
148 if (count
>= INT_MAX
/ 2) return 0;
150 if (!(new_entries
= realloc( table
->entries
, count
* sizeof(struct handle_entry
) )))
152 set_error( STATUS_NO_MEMORY
);
155 table
->entries
= new_entries
;
156 table
->count
= count
;
160 /* allocate the first free entry in the handle table */
161 static int alloc_entry( struct handle_table
*table
, void *obj
, unsigned int access
)
163 struct handle_entry
*entry
= table
->entries
+ table
->free
;
166 for (i
= table
->free
; i
<= table
->last
; i
++, entry
++) if (!entry
->ptr
) goto found
;
167 if (i
>= table
->count
)
169 if (!grow_handle_table( table
)) return -1;
170 entry
= table
->entries
+ i
; /* the entries may have moved */
175 entry
->ptr
= grab_object( obj
);
176 entry
->access
= access
;
177 return index_to_handle(i
);
180 /* allocate a handle for an object, incrementing its refcount */
181 /* return the handle, or -1 on error */
182 int alloc_handle( struct process
*process
, void *obj
, unsigned int access
, int inherit
)
184 struct handle_table
*table
= (struct handle_table
*)process
->handles
;
187 assert( !(access
& RESERVED_ALL
) );
188 if (inherit
) access
|= RESERVED_INHERIT
;
189 return alloc_entry( table
, obj
, access
);
192 /* allocate a global handle for an object, incrementing its refcount */
193 /* return the handle, or -1 on error */
194 static int alloc_global_handle( void *obj
, unsigned int access
)
200 if (!(global_table
= (struct handle_table
*)alloc_handle_table( NULL
, 0 ))) return -1;
202 if ((handle
= alloc_entry( global_table
, obj
, access
)) != -1)
203 handle
= HANDLE_LOCAL_TO_GLOBAL(handle
);
207 /* return a handle entry, or NULL if the handle is invalid */
208 static struct handle_entry
*get_handle( struct process
*process
, int handle
)
210 struct handle_table
*table
= (struct handle_table
*)process
->handles
;
211 struct handle_entry
*entry
;
213 if (HANDLE_IS_GLOBAL(handle
))
215 handle
= HANDLE_GLOBAL_TO_LOCAL(handle
);
216 table
= global_table
;
218 if (!table
) goto error
;
219 handle
= handle_to_index( handle
);
220 if (handle
< 0) goto error
;
221 if (handle
> table
->last
) goto error
;
222 entry
= table
->entries
+ handle
;
223 if (!entry
->ptr
) goto error
;
227 set_error( STATUS_INVALID_HANDLE
);
231 /* attempt to shrink a table */
232 static void shrink_handle_table( struct handle_table
*table
)
234 struct handle_entry
*entry
= table
->entries
+ table
->last
;
235 struct handle_entry
*new_entries
;
236 int count
= table
->count
;
238 while (table
->last
>= 0)
240 if (entry
->ptr
) break;
244 if (table
->last
>= count
/ 4) return; /* no need to shrink */
245 if (count
< MIN_HANDLE_ENTRIES
* 2) return; /* too small to shrink */
247 if (!(new_entries
= realloc( table
->entries
, count
* sizeof(*new_entries
) ))) return;
248 table
->count
= count
;
249 table
->entries
= new_entries
;
252 /* copy the handle table of the parent process */
253 /* return 1 if OK, 0 on error */
254 struct object
*copy_handle_table( struct process
*process
, struct process
*parent
)
256 struct handle_table
*parent_table
= (struct handle_table
*)parent
->handles
;
257 struct handle_table
*table
;
260 assert( parent_table
);
261 assert( parent_table
->obj
.ops
== &handle_table_ops
);
263 if (!(table
= (struct handle_table
*)alloc_handle_table( process
, parent_table
->count
)))
266 if ((table
->last
= parent_table
->last
) >= 0)
268 struct handle_entry
*ptr
= table
->entries
;
269 memcpy( ptr
, parent_table
->entries
, (table
->last
+ 1) * sizeof(struct handle_entry
) );
270 for (i
= 0; i
<= table
->last
; i
++, ptr
++)
272 if (!ptr
->ptr
) continue;
273 if (ptr
->access
& RESERVED_INHERIT
) grab_object( ptr
->ptr
);
274 else ptr
->ptr
= NULL
; /* don't inherit this entry */
277 /* attempt to shrink the table */
278 shrink_handle_table( table
);
282 /* close a handle and decrement the refcount of the associated object */
283 /* return 1 if OK, 0 on error */
284 int close_handle( struct process
*process
, int handle
)
286 struct handle_table
*table
;
287 struct handle_entry
*entry
;
290 if (!(entry
= get_handle( process
, handle
))) return 0;
291 if (entry
->access
& RESERVED_CLOSE_PROTECT
)
293 set_error( STATUS_INVALID_HANDLE
);
298 table
= HANDLE_IS_GLOBAL(handle
) ? global_table
: (struct handle_table
*)process
->handles
;
299 if (entry
< table
->entries
+ table
->free
) table
->free
= entry
- table
->entries
;
300 if (entry
== table
->entries
+ table
->last
) shrink_handle_table( table
);
301 release_object( obj
);
305 /* close all the global handles */
306 void close_global_handles(void)
310 release_object( global_table
);
315 /* retrieve the object corresponding to one of the magic pseudo-handles */
316 static inline struct object
*get_magic_handle( int handle
)
320 case 0xfffffffe: /* current thread pseudo-handle */
321 return ¤t
->obj
;
322 case 0x7fffffff: /* current process pseudo-handle */
323 case 0xffffffff: /* current process pseudo-handle */
324 return (struct object
*)current
->process
;
330 /* retrieve the object corresponding to a handle, incrementing its refcount */
331 struct object
*get_handle_obj( struct process
*process
, int handle
,
332 unsigned int access
, const struct object_ops
*ops
)
334 struct handle_entry
*entry
;
337 if (!(obj
= get_magic_handle( handle
)))
339 if (!(entry
= get_handle( process
, handle
))) return NULL
;
340 if ((entry
->access
& access
) != access
)
342 set_error( STATUS_ACCESS_DENIED
);
347 if (ops
&& (obj
->ops
!= ops
))
349 set_error( STATUS_OBJECT_TYPE_MISMATCH
); /* not the right type */
352 return grab_object( obj
);
355 /* get/set the handle reserved flags */
356 /* return the new flags (or -1 on error) */
357 static int set_handle_info( struct process
*process
, int handle
, int mask
, int flags
)
359 struct handle_entry
*entry
;
361 if (get_magic_handle( handle
))
363 /* we can retrieve but not set info for magic handles */
364 if (mask
) set_error( STATUS_ACCESS_DENIED
);
367 if (!(entry
= get_handle( process
, handle
))) return -1;
368 mask
= (mask
<< RESERVED_SHIFT
) & RESERVED_ALL
;
369 flags
= (flags
<< RESERVED_SHIFT
) & mask
;
370 entry
->access
= (entry
->access
& ~mask
) | flags
;
371 return (entry
->access
& RESERVED_ALL
) >> RESERVED_SHIFT
;
374 /* duplicate a handle */
375 int duplicate_handle( struct process
*src
, int src_handle
, struct process
*dst
,
376 unsigned int access
, int inherit
, int options
)
379 struct object
*obj
= get_handle_obj( src
, src_handle
, 0, NULL
);
382 if (options
& DUP_HANDLE_SAME_ACCESS
)
384 struct handle_entry
*entry
= get_handle( src
, src_handle
);
386 access
= entry
->access
;
387 else /* pseudo-handle, give it full access */
389 access
= STANDARD_RIGHTS_ALL
| SPECIFIC_RIGHTS_ALL
;
393 access
&= ~RESERVED_ALL
;
394 if (options
& DUP_HANDLE_MAKE_GLOBAL
)
395 res
= alloc_global_handle( obj
, access
);
397 res
= alloc_handle( dst
, obj
, access
, inherit
);
398 release_object( obj
);
402 /* open a new handle to an existing object */
403 int open_object( const WCHAR
*name
, size_t len
, const struct object_ops
*ops
,
404 unsigned int access
, int inherit
)
407 struct object
*obj
= find_object( name
, len
);
410 if (ops
&& obj
->ops
!= ops
)
411 set_error( STATUS_OBJECT_TYPE_MISMATCH
);
413 handle
= alloc_handle( current
->process
, obj
, access
, inherit
);
414 release_object( obj
);
417 set_error( STATUS_OBJECT_NAME_NOT_FOUND
);
422 DECL_HANDLER(close_handle
)
424 close_handle( current
->process
, req
->handle
);
427 /* get information about a handle */
428 DECL_HANDLER(get_handle_info
)
430 req
->flags
= set_handle_info( current
->process
, req
->handle
, 0, 0 );
433 /* set a handle information */
434 DECL_HANDLER(set_handle_info
)
436 set_handle_info( current
->process
, req
->handle
, req
->mask
, req
->flags
);
439 /* duplicate a handle */
440 DECL_HANDLER(dup_handle
)
442 struct process
*src
, *dst
;
445 if ((src
= get_process_from_handle( req
->src_process
, PROCESS_DUP_HANDLE
)))
447 if (req
->options
& DUP_HANDLE_MAKE_GLOBAL
)
449 req
->handle
= duplicate_handle( src
, req
->src_handle
, NULL
,
450 req
->access
, req
->inherit
, req
->options
);
452 else if ((dst
= get_process_from_handle( req
->dst_process
, PROCESS_DUP_HANDLE
)))
454 req
->handle
= duplicate_handle( src
, req
->src_handle
, dst
,
455 req
->access
, req
->inherit
, req
->options
);
456 release_object( dst
);
458 /* close the handle no matter what happened */
459 if (req
->options
& DUP_HANDLE_CLOSE_SOURCE
)
460 close_handle( src
, req
->src_handle
);
461 release_object( src
);