blob: 996d1d14c2f126bb902624b4e4735232668ff5c7 [file] [log] [blame]
Alexandre Julliard43c190e1999-05-15 10:48:19 +00001/*
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
20struct handle_entry
21{
22 struct object *ptr;
23 unsigned int access;
24};
25
26static struct process *initial_process;
27
28/* reserved handle access rights */
29#define RESERVED_SHIFT 25
30#define RESERVED_INHERIT (HANDLE_FLAG_INHERIT << RESERVED_SHIFT)
31#define RESERVED_CLOSE_PROTECT (HANDLE_FLAG_PROTECT_FROM_CLOSE << RESERVED_SHIFT)
32#define RESERVED_ALL (RESERVED_INHERIT | RESERVED_CLOSE_PROTECT)
33
34/* global handle macros */
35#define HANDLE_OBFUSCATOR 0x544a4def
36#define HANDLE_IS_GLOBAL(h) (((h) ^ HANDLE_OBFUSCATOR) < 0x10000)
37#define HANDLE_LOCAL_TO_GLOBAL(h) ((h) ^ HANDLE_OBFUSCATOR)
38#define HANDLE_GLOBAL_TO_LOCAL(h) ((h) ^ HANDLE_OBFUSCATOR)
39
40#define MIN_HANDLE_ENTRIES 32
41
42/* grow a handle table */
43/* return 1 if OK, 0 on error */
Alexandre Julliard3da5f841999-05-16 16:54:54 +000044static int grow_handle_table( struct process *process )
Alexandre Julliard43c190e1999-05-15 10:48:19 +000045{
46 struct handle_entry *new_entries;
Alexandre Julliard3da5f841999-05-16 16:54:54 +000047 int count = process->handle_count;
Alexandre Julliard43c190e1999-05-15 10:48:19 +000048
49 if (count >= INT_MAX / 2) return 0;
50 count *= 2;
Alexandre Julliard3da5f841999-05-16 16:54:54 +000051 if (!(new_entries = realloc( process->entries, count * sizeof(struct handle_entry) )))
Alexandre Julliard43c190e1999-05-15 10:48:19 +000052 {
53 SET_ERROR( ERROR_OUTOFMEMORY );
54 return 0;
55 }
Alexandre Julliard3da5f841999-05-16 16:54:54 +000056 process->handle_count = count;
57 process->entries = new_entries;
Alexandre Julliard43c190e1999-05-15 10:48:19 +000058 return 1;
59}
60
61/* allocate a handle for an object, incrementing its refcount */
62/* return the handle, or -1 on error */
Alexandre Julliard3da5f841999-05-16 16:54:54 +000063int alloc_handle( struct process *process, void *obj, unsigned int access, int inherit )
Alexandre Julliard43c190e1999-05-15 10:48:19 +000064{
Alexandre Julliard43c190e1999-05-15 10:48:19 +000065 struct handle_entry *entry;
66 int handle;
67
68 assert( !(access & RESERVED_ALL) );
69 if (inherit) access |= RESERVED_INHERIT;
70
71 /* find the first free entry */
72
Alexandre Julliard3da5f841999-05-16 16:54:54 +000073 if (!(entry = process->entries)) return -1;
74 for (handle = 0; handle <= process->handle_last; handle++, entry++)
Alexandre Julliard43c190e1999-05-15 10:48:19 +000075 if (!entry->ptr) goto found;
76
Alexandre Julliard3da5f841999-05-16 16:54:54 +000077 if (handle >= process->handle_count)
Alexandre Julliard43c190e1999-05-15 10:48:19 +000078 {
Alexandre Julliard3da5f841999-05-16 16:54:54 +000079 if (!grow_handle_table( process )) return -1;
80 entry = process->entries + handle; /* the table may have moved */
Alexandre Julliard43c190e1999-05-15 10:48:19 +000081 }
Alexandre Julliard3da5f841999-05-16 16:54:54 +000082 process->handle_last = handle;
Alexandre Julliard43c190e1999-05-15 10:48:19 +000083
84 found:
85 entry->ptr = grab_object( obj );
86 entry->access = access;
87 return handle + 1; /* avoid handle 0 */
88}
89
90/* return an handle entry, or NULL if the handle is invalid */
91static struct handle_entry *get_handle( struct process *process, int handle )
92{
Alexandre Julliard43c190e1999-05-15 10:48:19 +000093 struct handle_entry *entry;
94
95 if (HANDLE_IS_GLOBAL(handle))
96 {
97 handle = HANDLE_GLOBAL_TO_LOCAL(handle);
98 process = initial_process;
99 }
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000100 handle--; /* handles start at 1 */
Alexandre Julliard3da5f841999-05-16 16:54:54 +0000101 if ((handle < 0) || (handle > process->handle_last)) goto error;
102 entry = process->entries + handle;
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000103 if (!entry->ptr) goto error;
104 return entry;
105
106 error:
107 SET_ERROR( ERROR_INVALID_HANDLE );
108 return NULL;
109}
110
111/* attempt to shrink a table */
112/* return 1 if OK, 0 on error */
Alexandre Julliard3da5f841999-05-16 16:54:54 +0000113static int shrink_handle_table( struct process *process )
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000114{
115 struct handle_entry *new_entries;
Alexandre Julliard3da5f841999-05-16 16:54:54 +0000116 struct handle_entry *entry = process->entries + process->handle_last;
117 int count = process->handle_count;
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000118
Alexandre Julliard3da5f841999-05-16 16:54:54 +0000119 while (process->handle_last >= 0)
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000120 {
121 if (entry->ptr) break;
Alexandre Julliard3da5f841999-05-16 16:54:54 +0000122 process->handle_last--;
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000123 entry--;
124 }
Alexandre Julliard3da5f841999-05-16 16:54:54 +0000125 if (process->handle_last >= count / 4) return 1; /* no need to shrink */
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000126 if (count < MIN_HANDLE_ENTRIES * 2) return 1; /* too small to shrink */
127 count /= 2;
Alexandre Julliard3da5f841999-05-16 16:54:54 +0000128 if (!(new_entries = realloc( process->entries,
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000129 count * sizeof(struct handle_entry) )))
130 return 0;
Alexandre Julliard3da5f841999-05-16 16:54:54 +0000131 process->handle_count = count;
132 process->entries = new_entries;
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000133 return 1;
134}
135
136/* copy the handle table of the parent process */
137/* return 1 if OK, 0 on error */
138int copy_handle_table( struct process *process, struct process *parent )
139{
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000140 struct handle_entry *ptr;
141 int i, count, last;
142
143 if (!parent) /* first process */
144 {
145 if (!initial_process) initial_process = process;
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000146 count = MIN_HANDLE_ENTRIES;
147 last = -1;
148 }
149 else
150 {
Alexandre Julliard3da5f841999-05-16 16:54:54 +0000151 assert( parent->entries );
152 count = parent->handle_count;
153 last = parent->handle_last;
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000154 }
155
156 if (!(ptr = mem_alloc( count * sizeof(struct handle_entry)))) return 0;
Alexandre Julliard3da5f841999-05-16 16:54:54 +0000157 process->entries = ptr;
158 process->handle_count = count;
159 process->handle_last = last;
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000160
161 if (last >= 0)
162 {
Alexandre Julliard3da5f841999-05-16 16:54:54 +0000163 memcpy( ptr, parent->entries, (last + 1) * sizeof(struct handle_entry) );
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000164 for (i = 0; i <= last; i++, ptr++)
165 {
166 if (!ptr->ptr) continue;
167 if (ptr->access & RESERVED_INHERIT) grab_object( ptr->ptr );
168 else ptr->ptr = NULL; /* don't inherit this entry */
169 }
170 }
171 /* attempt to shrink the table */
Alexandre Julliard3da5f841999-05-16 16:54:54 +0000172 shrink_handle_table( process );
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000173 return 1;
174}
175
176/* close a handle and decrement the refcount of the associated object */
177/* return 1 if OK, 0 on error */
178int close_handle( struct process *process, int handle )
179{
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000180 struct handle_entry *entry;
181 struct object *obj;
182
183 if (HANDLE_IS_GLOBAL(handle))
184 {
185 handle = HANDLE_GLOBAL_TO_LOCAL(handle);
186 process = initial_process;
187 }
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000188 if (!(entry = get_handle( process, handle ))) return 0;
189 if (entry->access & RESERVED_CLOSE_PROTECT) return 0; /* FIXME: error code */
190 obj = entry->ptr;
191 entry->ptr = NULL;
Alexandre Julliard3da5f841999-05-16 16:54:54 +0000192 if (handle-1 == process->handle_last) shrink_handle_table( process );
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000193 release_object( obj );
194 return 1;
195}
196
197/* retrieve the object corresponding to a handle, incrementing its refcount */
198struct object *get_handle_obj( struct process *process, int handle,
199 unsigned int access, const struct object_ops *ops )
200{
201 struct handle_entry *entry;
202 struct object *obj;
203
204 switch( handle )
205 {
206 case 0xfffffffe: /* current thread pseudo-handle */
207 obj = &current->obj;
208 break;
209 case 0x7fffffff: /* current process pseudo-handle */
210 obj = (struct object *)current->process;
211 break;
212 default:
213 if (!(entry = get_handle( process, handle ))) return NULL;
214 if ((entry->access & access) != access)
215 {
216 SET_ERROR( ERROR_ACCESS_DENIED );
217 return NULL;
218 }
219 obj = entry->ptr;
220 break;
221 }
222 if (ops && (obj->ops != ops))
223 {
224 SET_ERROR( ERROR_INVALID_HANDLE ); /* not the right type */
225 return NULL;
226 }
227 return grab_object( obj );
228}
229
230/* get/set the handle reserved flags */
231/* return the new flags (or -1 on error) */
232static int set_handle_info( struct process *process, int handle, int mask, int flags )
233{
234 struct handle_entry *entry;
235
236 if (!(entry = get_handle( process, handle ))) return -1;
237 mask = (mask << RESERVED_SHIFT) & RESERVED_ALL;
238 flags = (flags << RESERVED_SHIFT) & mask;
239 entry->access = (entry->access & ~mask) | flags;
240 return (entry->access & RESERVED_ALL) >> RESERVED_SHIFT;
241}
242
243/* duplicate a handle */
244int duplicate_handle( struct process *src, int src_handle, struct process *dst,
245 unsigned int access, int inherit, int options )
246{
247 int res;
Alexandre Julliard0042cb31999-05-29 11:17:25 +0000248 struct object *obj = get_handle_obj( src, src_handle, 0, NULL );
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000249
Alexandre Julliard0042cb31999-05-29 11:17:25 +0000250 if (!obj) return -1;
251 if (options & DUP_HANDLE_SAME_ACCESS)
252 {
253 struct handle_entry *entry = get_handle( src, src_handle );
254 if (entry)
255 access = entry->access;
256 else /* pseudo-handle, give it full access */
257 {
258 access = STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL;
259 CLEAR_ERROR();
260 }
261 }
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000262 if (options & DUP_HANDLE_MAKE_GLOBAL) dst = initial_process;
263 access &= ~RESERVED_ALL;
Alexandre Julliard0042cb31999-05-29 11:17:25 +0000264 res = alloc_handle( dst, obj, access, inherit );
265 release_object( obj );
266 if ((options & DUP_HANDLE_MAKE_GLOBAL) && (res != -1)) res = HANDLE_LOCAL_TO_GLOBAL(res);
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000267 return res;
268}
269
270/* free the process handle entries */
271void free_handles( struct process *process )
272{
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000273 struct handle_entry *entry;
274 int handle;
275
Alexandre Julliard3da5f841999-05-16 16:54:54 +0000276 if (!(entry = process->entries)) return;
277 for (handle = 0; handle <= process->handle_last; handle++, entry++)
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000278 {
279 struct object *obj = entry->ptr;
280 entry->ptr = NULL;
281 if (obj) release_object( obj );
282 }
Alexandre Julliard3da5f841999-05-16 16:54:54 +0000283 free( process->entries );
284 process->handle_count = 0;
285 process->handle_last = -1;
286 process->entries = NULL;
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000287}
288
289/* open a new handle to an existing object */
290int open_object( const char *name, const struct object_ops *ops,
291 unsigned int access, int inherit )
292{
293 struct object *obj = find_object( name );
294 if (!obj)
295 {
296 SET_ERROR( ERROR_FILE_NOT_FOUND );
297 return -1;
298 }
299 if (ops && obj->ops != ops)
300 {
301 release_object( obj );
302 SET_ERROR( ERROR_INVALID_HANDLE ); /* FIXME: not the right type */
303 return -1;
304 }
305 return alloc_handle( current->process, obj, access, inherit );
306}
307
308/* dump a handle table on stdout */
309void dump_handles( struct process *process )
310{
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000311 struct handle_entry *entry;
312 int i;
313
Alexandre Julliard3da5f841999-05-16 16:54:54 +0000314 if (!process->entries) return;
315 entry = process->entries;
316 for (i = 0; i <= process->handle_last; i++, entry++)
Alexandre Julliard43c190e1999-05-15 10:48:19 +0000317 {
318 if (!entry->ptr) continue;
319 printf( "%5d: %p %08x ", i + 1, entry->ptr, entry->access );
320 entry->ptr->ops->dump( entry->ptr, 0 );
321 }
322}
323
324/* close a handle */
325DECL_HANDLER(close_handle)
326{
327 close_handle( current->process, req->handle );
328 send_reply( current, -1, 0 );
329}
330
331/* get information about a handle */
332DECL_HANDLER(get_handle_info)
333{
334 struct get_handle_info_reply reply;
335 reply.flags = set_handle_info( current->process, req->handle, 0, 0 );
336 send_reply( current, -1, 1, &reply, sizeof(reply) );
337}
338
339/* set a handle information */
340DECL_HANDLER(set_handle_info)
341{
342 set_handle_info( current->process, req->handle, req->mask, req->flags );
343 send_reply( current, -1, 0 );
344}
345
346/* duplicate a handle */
347DECL_HANDLER(dup_handle)
348{
349 struct dup_handle_reply reply = { -1 };
350 struct process *src, *dst;
351
352 if ((src = get_process_from_handle( req->src_process, PROCESS_DUP_HANDLE )))
353 {
354 if (req->options & DUP_HANDLE_MAKE_GLOBAL)
355 {
356 reply.handle = duplicate_handle( src, req->src_handle, NULL,
357 req->access, req->inherit, req->options );
358 }
359 else if ((dst = get_process_from_handle( req->dst_process, PROCESS_DUP_HANDLE )))
360 {
361 reply.handle = duplicate_handle( src, req->src_handle, dst,
362 req->access, req->inherit, req->options );
363 release_object( dst );
364 }
365 /* close the handle no matter what happened */
366 if (req->options & DUP_HANDLE_CLOSE_SOURCE)
367 close_handle( src, req->src_handle );
368 release_object( src );
369 }
370 send_reply( current, -1, 1, &reply, sizeof(reply) );
371}