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 static void handle_table_dump( struct object
*obj
, int verbose
);
54 static void handle_table_destroy( struct object
*obj
);
56 static const struct object_ops handle_table_ops
=
60 NULL
, /* should never get called */
61 NULL
, /* should never get called */
62 NULL
, /* should never get called */
70 /* dump a handle table */
71 static void handle_table_dump( struct object
*obj
, int verbose
)
74 struct handle_table
*table
= (struct handle_table
*)obj
;
75 struct handle_entry
*entry
= table
->entries
;
77 assert( obj
->ops
== &handle_table_ops
);
79 fprintf( stderr
, "Handle table last=%d count=%d process=%p\n",
80 table
->last
, table
->count
, table
->process
);
82 entry
= table
->entries
;
83 for (i
= 0; i
<= table
->last
; i
++, entry
++)
85 if (!entry
->ptr
) continue;
86 fprintf( stderr
, "%9d: %p %08x ", i
+ 1, entry
->ptr
, entry
->access
);
87 entry
->ptr
->ops
->dump( entry
->ptr
, 0 );
91 /* destroy a handle table */
92 static void handle_table_destroy( struct object
*obj
)
95 struct handle_table
*table
= (struct handle_table
*)obj
;
96 struct handle_entry
*entry
= table
->entries
;
98 assert( obj
->ops
== &handle_table_ops
);
100 for (i
= 0; i
<= table
->last
; i
++, entry
++)
102 struct object
*obj
= entry
->ptr
;
104 if (obj
) release_object( obj
);
106 free( table
->entries
);
110 /* allocate a new handle table */
111 struct object
*alloc_handle_table( struct process
*process
, int count
)
113 struct handle_table
*table
;
115 if (count
< MIN_HANDLE_ENTRIES
) count
= MIN_HANDLE_ENTRIES
;
116 if (!(table
= alloc_object( sizeof(*table
), &handle_table_ops
, NULL
)))
118 table
->process
= process
;
119 table
->count
= count
;
122 if ((table
->entries
= mem_alloc( count
* sizeof(*table
->entries
) ))) return &table
->obj
;
123 release_object( table
);
127 /* grow a handle table */
128 static int grow_handle_table( struct handle_table
*table
)
130 struct handle_entry
*new_entries
;
131 int count
= table
->count
;
133 if (count
>= INT_MAX
/ 2) return 0;
135 if (!(new_entries
= realloc( table
->entries
, count
* sizeof(struct handle_entry
) )))
137 SET_ERROR( ERROR_OUTOFMEMORY
);
140 table
->entries
= new_entries
;
141 table
->count
= count
;
145 /* find the first free entry in the handle table */
146 static struct handle_entry
*get_free_entry( struct handle_table
*table
, int *phandle
)
148 struct handle_entry
*entry
= table
->entries
+ table
->free
;
151 for (handle
= table
->free
; handle
<= table
->last
; handle
++, entry
++)
152 if (!entry
->ptr
) goto found
;
153 if (handle
>= table
->count
)
155 if (!grow_handle_table( table
)) return NULL
;
156 entry
= table
->entries
+ handle
; /* the entries may have moved */
158 table
->last
= handle
;
160 table
->free
= *phandle
= handle
+ 1; /* avoid handle 0 */
164 /* allocate a handle for an object, incrementing its refcount */
165 /* return the handle, or -1 on error */
166 int alloc_handle( struct process
*process
, void *obj
, unsigned int access
, int inherit
)
168 struct handle_table
*table
= (struct handle_table
*)process
->handles
;
169 struct handle_entry
*entry
;
173 assert( !(access
& RESERVED_ALL
) );
174 if (inherit
) access
|= RESERVED_INHERIT
;
176 if (!(entry
= get_free_entry( table
, &handle
))) return -1;
177 entry
->ptr
= grab_object( obj
);
178 entry
->access
= access
;
182 /* allocate a global handle for an object, incrementing its refcount */
183 /* return the handle, or -1 on error */
184 static int alloc_global_handle( void *obj
, unsigned int access
)
186 struct handle_entry
*entry
;
191 if (!(global_table
= (struct handle_table
*)alloc_handle_table( NULL
, 0 ))) return -1;
193 if (!(entry
= get_free_entry( global_table
, &handle
))) return -1;
194 entry
->ptr
= grab_object( obj
);
195 entry
->access
= access
;
196 return HANDLE_LOCAL_TO_GLOBAL(handle
);
199 /* return an handle entry, or NULL if the handle is invalid */
200 static struct handle_entry
*get_handle( struct process
*process
, int handle
)
202 struct handle_table
*table
= (struct handle_table
*)process
->handles
;
203 struct handle_entry
*entry
;
205 if (HANDLE_IS_GLOBAL(handle
))
207 handle
= HANDLE_GLOBAL_TO_LOCAL(handle
);
208 table
= global_table
;
210 if (!table
) goto error
;
211 handle
--; /* handles start at 1 */
212 if (handle
< 0) goto error
;
213 if (handle
> table
->last
) goto error
;
214 entry
= table
->entries
+ handle
;
215 if (!entry
->ptr
) goto error
;
219 SET_ERROR( ERROR_INVALID_HANDLE
);
223 /* attempt to shrink a table */
224 static void shrink_handle_table( struct handle_table
*table
)
226 struct handle_entry
*entry
= table
->entries
+ table
->last
;
227 struct handle_entry
*new_entries
;
228 int count
= table
->count
;
230 while (table
->last
>= 0)
232 if (entry
->ptr
) break;
236 if (table
->last
>= count
/ 4) return; /* no need to shrink */
237 if (count
< MIN_HANDLE_ENTRIES
* 2) return; /* too small to shrink */
239 if (!(new_entries
= realloc( table
->entries
, count
* sizeof(*new_entries
) ))) return;
240 table
->count
= count
;
241 table
->entries
= new_entries
;
244 /* copy the handle table of the parent process */
245 /* return 1 if OK, 0 on error */
246 struct object
*copy_handle_table( struct process
*process
, struct process
*parent
)
248 struct handle_table
*parent_table
= (struct handle_table
*)parent
->handles
;
249 struct handle_table
*table
;
252 assert( parent_table
);
253 assert( parent_table
->obj
.ops
== &handle_table_ops
);
255 if (!(table
= (struct handle_table
*)alloc_handle_table( process
, parent_table
->count
)))
258 if ((table
->last
= parent_table
->last
) >= 0)
260 struct handle_entry
*ptr
= table
->entries
;
261 memcpy( ptr
, parent_table
->entries
, (table
->last
+ 1) * sizeof(struct handle_entry
) );
262 for (i
= 0; i
<= table
->last
; i
++, ptr
++)
264 if (!ptr
->ptr
) continue;
265 if (ptr
->access
& RESERVED_INHERIT
) grab_object( ptr
->ptr
);
266 else ptr
->ptr
= NULL
; /* don't inherit this entry */
269 /* attempt to shrink the table */
270 shrink_handle_table( table
);
274 /* close a handle and decrement the refcount of the associated object */
275 /* return 1 if OK, 0 on error */
276 int close_handle( struct process
*process
, int handle
)
278 struct handle_table
*table
;
279 struct handle_entry
*entry
;
282 if (!(entry
= get_handle( process
, handle
))) return 0;
283 if (entry
->access
& RESERVED_CLOSE_PROTECT
) return 0; /* FIXME: error code */
286 table
= HANDLE_IS_GLOBAL(handle
) ? global_table
: (struct handle_table
*)process
->handles
;
287 if (entry
< table
->entries
+ table
->free
) table
->free
= entry
- table
->entries
;
288 if (entry
== table
->entries
+ table
->last
) shrink_handle_table( table
);
289 release_object( obj
);
293 /* close all the global handles */
294 void close_global_handles(void)
298 release_object( global_table
);
303 /* retrieve the object corresponding to a handle, incrementing its refcount */
304 struct object
*get_handle_obj( struct process
*process
, int handle
,
305 unsigned int access
, const struct object_ops
*ops
)
307 struct handle_entry
*entry
;
312 case 0xfffffffe: /* current thread pseudo-handle */
315 case 0x7fffffff: /* current process pseudo-handle */
316 obj
= (struct object
*)current
->process
;
319 if (!(entry
= get_handle( process
, handle
))) return NULL
;
320 if ((entry
->access
& access
) != access
)
322 SET_ERROR( ERROR_ACCESS_DENIED
);
328 if (ops
&& (obj
->ops
!= ops
))
330 SET_ERROR( ERROR_INVALID_HANDLE
); /* not the right type */
333 return grab_object( obj
);
336 /* get/set the handle reserved flags */
337 /* return the new flags (or -1 on error) */
338 static int set_handle_info( struct process
*process
, int handle
, int mask
, int flags
)
340 struct handle_entry
*entry
;
342 if (!(entry
= get_handle( process
, handle
))) return -1;
343 mask
= (mask
<< RESERVED_SHIFT
) & RESERVED_ALL
;
344 flags
= (flags
<< RESERVED_SHIFT
) & mask
;
345 entry
->access
= (entry
->access
& ~mask
) | flags
;
346 return (entry
->access
& RESERVED_ALL
) >> RESERVED_SHIFT
;
349 /* duplicate a handle */
350 int duplicate_handle( struct process
*src
, int src_handle
, struct process
*dst
,
351 unsigned int access
, int inherit
, int options
)
354 struct object
*obj
= get_handle_obj( src
, src_handle
, 0, NULL
);
357 if (options
& DUP_HANDLE_SAME_ACCESS
)
359 struct handle_entry
*entry
= get_handle( src
, src_handle
);
361 access
= entry
->access
;
362 else /* pseudo-handle, give it full access */
364 access
= STANDARD_RIGHTS_ALL
| SPECIFIC_RIGHTS_ALL
;
368 access
&= ~RESERVED_ALL
;
369 if (options
& DUP_HANDLE_MAKE_GLOBAL
)
370 res
= alloc_global_handle( obj
, access
);
372 res
= alloc_handle( dst
, obj
, access
, inherit
);
373 release_object( obj
);
377 /* open a new handle to an existing object */
378 int open_object( const char *name
, const struct object_ops
*ops
,
379 unsigned int access
, int inherit
)
381 struct object
*obj
= find_object( name
);
384 SET_ERROR( ERROR_FILE_NOT_FOUND
);
387 if (ops
&& obj
->ops
!= ops
)
389 release_object( obj
);
390 SET_ERROR( ERROR_INVALID_HANDLE
); /* FIXME: not the right type */
393 return alloc_handle( current
->process
, obj
, access
, inherit
);
397 DECL_HANDLER(close_handle
)
399 close_handle( current
->process
, req
->handle
);
400 send_reply( current
, -1, 0 );
403 /* get information about a handle */
404 DECL_HANDLER(get_handle_info
)
406 struct get_handle_info_reply reply
;
407 reply
.flags
= set_handle_info( current
->process
, req
->handle
, 0, 0 );
408 send_reply( current
, -1, 1, &reply
, sizeof(reply
) );
411 /* set a handle information */
412 DECL_HANDLER(set_handle_info
)
414 set_handle_info( current
->process
, req
->handle
, req
->mask
, req
->flags
);
415 send_reply( current
, -1, 0 );
418 /* duplicate a handle */
419 DECL_HANDLER(dup_handle
)
421 struct dup_handle_reply reply
= { -1 };
422 struct process
*src
, *dst
;
424 if ((src
= get_process_from_handle( req
->src_process
, PROCESS_DUP_HANDLE
)))
426 if (req
->options
& DUP_HANDLE_MAKE_GLOBAL
)
428 reply
.handle
= duplicate_handle( src
, req
->src_handle
, NULL
,
429 req
->access
, req
->inherit
, req
->options
);
431 else if ((dst
= get_process_from_handle( req
->dst_process
, PROCESS_DUP_HANDLE
)))
433 reply
.handle
= duplicate_handle( src
, req
->src_handle
, dst
,
434 req
->access
, req
->inherit
, req
->options
);
435 release_object( dst
);
437 /* close the handle no matter what happened */
438 if (req
->options
& DUP_HANDLE_CLOSE_SOURCE
)
439 close_handle( src
, req
->src_handle
);
440 release_object( src
);
442 send_reply( current
, -1, 1, &reply
, sizeof(reply
) );