server: Use attributes instead of inherit flag in token requests.
[wine] / dlls / ntdll / loadorder.c
1 /*
2  * Dlls load order support
3  *
4  * Copyright 1999 Bertho Stultiens
5  * Copyright 2003 Alexandre Julliard
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <assert.h>
29
30 #include "windef.h"
31 #include "winternl.h"
32 #include "ntdll_misc.h"
33 #include "module.h"
34
35 #include "wine/debug.h"
36 #include "wine/unicode.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(module);
39
40 #define LOADORDER_ALLOC_CLUSTER 32      /* Allocate with 32 entries at a time */
41
42 typedef struct module_loadorder
43 {
44     const WCHAR        *modulename;
45     enum loadorder_type loadorder[LOADORDER_NTYPES];
46 } module_loadorder_t;
47
48 struct loadorder_list
49 {
50     int                 count;
51     int                 alloc;
52     module_loadorder_t *order;
53 };
54
55 /* dll to load as builtins if not explicitly specified otherwise */
56 /* the list must remain sorted by dll name */
57 static const WCHAR default_builtins[][10] =
58 {
59     { 'g','d','i','3','2',0 },
60     { 'i','c','m','p',0 },
61     { 'k','e','r','n','e','l','3','2',0 },
62     { 'n','t','d','l','l',0 },
63     { 'o','d','b','c','3','2',0 },
64     { 't','t','y','d','r','v',0 },
65     { 'u','s','e','r','3','2',0 },
66     { 'w','3','2','s','k','r','n','l',0 },
67     { 'w','i','n','e','d','o','s',0 },
68     { 'w','i','n','e','p','s',0 },
69     { 'w','i','n','m','m',0 },
70     { 'w','n','a','s','p','i','3','2',0 },
71     { 'w','o','w','3','2',0 },
72     { 'w','s','2','_','3','2',0 },
73     { 'w','s','o','c','k','3','2',0 },
74     { 'x','1','1','d','r','v',0 }
75 };
76
77 /* default if nothing else specified */
78 static const enum loadorder_type default_loadorder[LOADORDER_NTYPES] =
79 {
80     LOADORDER_BI, LOADORDER_DLL, 0
81 };
82
83 static const WCHAR separatorsW[] = {',',' ','\t',0};
84
85 static int init_done;
86 static struct loadorder_list env_list;
87
88
89 /***************************************************************************
90  *      cmp_sort_func   (internal, static)
91  *
92  * Sorting and comparing function used in sort and search of loadorder
93  * entries.
94  */
95 static int cmp_sort_func(const void *s1, const void *s2)
96 {
97     return strcmpiW(((const module_loadorder_t *)s1)->modulename, ((const module_loadorder_t *)s2)->modulename);
98 }
99
100
101 /***************************************************************************
102  *      strcmp_func
103  */
104 static int strcmp_func(const void *s1, const void *s2)
105 {
106     return strcmpiW( (const WCHAR *)s1, (const WCHAR *)s2 );
107 }
108
109
110 /***************************************************************************
111  *      get_basename
112  *
113  * Return the base name of a file name (i.e. remove the path components).
114  */
115 static const WCHAR *get_basename( const WCHAR *name )
116 {
117     const WCHAR *ptr;
118
119     if (name[0] && name[1] == ':') name += 2;  /* strip drive specification */
120     if ((ptr = strrchrW( name, '\\' ))) name = ptr + 1;
121     if ((ptr = strrchrW( name, '/' ))) name = ptr + 1;
122     return name;
123 }
124
125 /***************************************************************************
126  *      remove_dll_ext
127  *
128  * Remove extension if it is ".dll".
129  */
130 static inline void remove_dll_ext( WCHAR *ext )
131 {
132     if (ext[0] == '.' &&
133         toupperW(ext[1]) == 'D' &&
134         toupperW(ext[2]) == 'L' &&
135         toupperW(ext[3]) == 'L' &&
136         !ext[4]) ext[0] = 0;
137 }
138
139
140 /***************************************************************************
141  *      debugstr_loadorder
142  *
143  * Return a loadorder in printable form.
144  */
145 static const char *debugstr_loadorder( enum loadorder_type lo[] )
146 {
147     int i;
148     char buffer[LOADORDER_NTYPES*3+1];
149
150     buffer[0] = 0;
151     for(i = 0; i < LOADORDER_NTYPES; i++)
152     {
153         if (lo[i] == LOADORDER_INVALID) break;
154         switch(lo[i])
155         {
156         case LOADORDER_DLL: strcat( buffer, "n," ); break;
157         case LOADORDER_BI:  strcat( buffer, "b," ); break;
158         default:            strcat( buffer, "?," ); break;
159         }
160     }
161     if (buffer[0]) buffer[strlen(buffer)-1] = 0;
162     return debugstr_a(buffer);
163 }
164
165
166 /***************************************************************************
167  *      append_load_order
168  *
169  * Append a load order to the list if necessary.
170  */
171 static void append_load_order(enum loadorder_type lo[], enum loadorder_type append)
172 {
173     int i;
174
175     for (i = 0; i < LOADORDER_NTYPES; i++)
176     {
177         if (lo[i] == LOADORDER_INVALID)  /* append it here */
178         {
179             lo[i++] = append;
180             lo[i] = LOADORDER_INVALID;
181             return;
182         }
183         if (lo[i] == append) return;  /* already in the list */
184     }
185     assert(0);  /* cannot get here */
186 }
187
188
189 /***************************************************************************
190  *      parse_load_order
191  *
192  * Parses the loadorder options from the configuration and puts it into
193  * a structure.
194  */
195 static void parse_load_order( const WCHAR *order, enum loadorder_type lo[] )
196 {
197     lo[0] = LOADORDER_INVALID;
198     while (*order)
199     {
200         order += strspnW( order, separatorsW );
201         switch(*order)
202         {
203         case 'N':       /* Native */
204         case 'n':
205             append_load_order( lo, LOADORDER_DLL );
206             break;
207         case 'B':       /* Builtin */
208         case 'b':
209             append_load_order( lo, LOADORDER_BI );
210             break;
211         }
212         order += strcspnW( order, separatorsW );
213     }
214 }
215
216
217 /***************************************************************************
218  *      add_load_order
219  *
220  * Adds an entry in the list of environment overrides.
221  */
222 static void add_load_order( const module_loadorder_t *plo )
223 {
224     int i;
225
226     for(i = 0; i < env_list.count; i++)
227     {
228         if(!cmp_sort_func(plo, &env_list.order[i] ))
229         {
230             /* replace existing option */
231             memcpy( env_list.order[i].loadorder, plo->loadorder, sizeof(plo->loadorder));
232             return;
233         }
234     }
235
236     if (i >= env_list.alloc)
237     {
238         /* No space in current array, make it larger */
239         env_list.alloc += LOADORDER_ALLOC_CLUSTER;
240         if (env_list.order)
241             env_list.order = RtlReAllocateHeap(GetProcessHeap(), 0, env_list.order,
242                                                env_list.alloc * sizeof(module_loadorder_t));
243         else
244             env_list.order = RtlAllocateHeap(GetProcessHeap(), 0,
245                                              env_list.alloc * sizeof(module_loadorder_t));
246         if(!env_list.order)
247         {
248             MESSAGE("Virtual memory exhausted\n");
249             exit(1);
250         }
251     }
252     memcpy(env_list.order[i].loadorder, plo->loadorder, sizeof(plo->loadorder));
253     env_list.order[i].modulename = plo->modulename;
254     env_list.count++;
255 }
256
257
258 /***************************************************************************
259  *      add_load_order_set
260  *
261  * Adds a set of entries in the list of command-line overrides from the key parameter.
262  */
263 static void add_load_order_set( WCHAR *entry )
264 {
265     module_loadorder_t ldo;
266     WCHAR *end = strchrW( entry, '=' );
267
268     if (!end) return;
269     *end++ = 0;
270     parse_load_order( end, ldo.loadorder );
271
272     while (*entry)
273     {
274         entry += strspnW( entry, separatorsW );
275         end = entry + strcspnW( entry, separatorsW );
276         if (*end) *end++ = 0;
277         if (*entry)
278         {
279             WCHAR *ext = strrchrW(entry, '.');
280             if (ext) remove_dll_ext( ext );
281             ldo.modulename = entry;
282             add_load_order( &ldo );
283             entry = end;
284         }
285     }
286 }
287
288
289 /***************************************************************************
290  *      init_load_order
291  */
292 static void init_load_order(void)
293 {
294     const char *order = getenv( "WINEDLLOVERRIDES" );
295     UNICODE_STRING strW;
296     WCHAR *entry, *next;
297
298     init_done = 1;
299     if (!order) return;
300
301     if (!strcmp( order, "help" ))
302     {
303         MESSAGE( "Syntax:\n"
304                  "  WINEDLLOVERRIDES=\"entry;entry;entry...\"\n"
305                  "    where each entry is of the form:\n"
306                  "        module[,module...]={native|builtin}[,{b|n}]\n"
307                  "\n"
308                  "    Only the first letter of the override (native or builtin)\n"
309                  "    is significant.\n\n"
310                  "Example:\n"
311                  "  WINEDLLOVERRIDES=\"comdlg32=n,b;shell32,shlwapi=b\"\n" );
312         exit(0);
313     }
314
315     RtlCreateUnicodeStringFromAsciiz( &strW, order );
316     entry = strW.Buffer;
317     while (*entry)
318     {
319         while (*entry && *entry == ';') entry++;
320         if (!*entry) break;
321         next = strchrW( entry, ';' );
322         if (next) *next++ = 0;
323         else next = entry + strlenW(entry);
324         add_load_order_set( entry );
325         entry = next;
326     }
327
328     /* sort the array for quick lookup */
329     if (env_list.count)
330         qsort(env_list.order, env_list.count, sizeof(env_list.order[0]), cmp_sort_func);
331
332     /* Note: we don't free the Unicode string because the
333      * stored module names point inside it */
334 }
335
336
337 /***************************************************************************
338  *      get_env_load_order
339  *
340  * Get the load order for a given module from the WINEDLLOVERRIDES environment variable.
341  */
342 static inline BOOL get_env_load_order( const WCHAR *module, enum loadorder_type lo[] )
343 {
344     module_loadorder_t tmp, *res = NULL;
345
346     tmp.modulename = module;
347     /* some bsearch implementations (Solaris) are buggy when the number of items is 0 */
348     if (env_list.count &&
349         (res = bsearch(&tmp, env_list.order, env_list.count, sizeof(env_list.order[0]), cmp_sort_func)))
350         memcpy( lo, res->loadorder, sizeof(res->loadorder) );
351     return (res != NULL);
352 }
353
354
355 /***************************************************************************
356  *      get_default_load_order
357  *
358  * Get the load order for a given module from the default list.
359  */
360 static inline BOOL get_default_load_order( const WCHAR *module, enum loadorder_type lo[] )
361 {
362     const int count = sizeof(default_builtins) / sizeof(default_builtins[0]);
363     if (!bsearch( module, default_builtins, count, sizeof(default_builtins[0]), strcmp_func ))
364         return FALSE;
365     lo[0] = LOADORDER_BI;
366     lo[1] = LOADORDER_INVALID;
367     return TRUE;
368 }
369
370
371 /***************************************************************************
372  *      open_app_key
373  *
374  * Open the registry key to the app-specific DllOverrides list.
375  */
376 static HANDLE open_app_key( const WCHAR *app_name, const WCHAR *module )
377 {
378     OBJECT_ATTRIBUTES attr;
379     UNICODE_STRING nameW;
380     HANDLE root, hkey;
381     WCHAR *str;
382     static const WCHAR AppDefaultsW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
383                                          'A','p','p','D','e','f','a','u','l','t','s','\\',0};
384     static const WCHAR DllOverridesW[] = {'\\','D','l','l','O','v','e','r','r','i','d','e','s',0};
385
386     str = RtlAllocateHeap( GetProcessHeap(), 0,
387                            sizeof(AppDefaultsW) + sizeof(DllOverridesW) +
388                            strlenW(app_name) * sizeof(WCHAR) );
389     if (!str) return 0;
390     strcpyW( str, AppDefaultsW );
391     strcatW( str, app_name );
392     strcatW( str, DllOverridesW );
393
394     TRACE( "searching %s in %s\n", debugstr_w(module), debugstr_w(str) );
395
396     RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
397     attr.Length = sizeof(attr);
398     attr.RootDirectory = root;
399     attr.ObjectName = &nameW;
400     attr.Attributes = 0;
401     attr.SecurityDescriptor = NULL;
402     attr.SecurityQualityOfService = NULL;
403     RtlInitUnicodeString( &nameW, str );
404
405     /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\DllOverrides */
406     if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) hkey = 0;
407     NtClose( root );
408     RtlFreeHeap( GetProcessHeap(), 0, str );
409     return hkey;
410 }
411
412
413 /***************************************************************************
414  *      get_registry_value
415  *
416  * Load the registry loadorder value for a given module.
417  */
418 static BOOL get_registry_value( HANDLE hkey, const WCHAR *module, enum loadorder_type lo[] )
419 {
420     UNICODE_STRING valueW;
421     char buffer[80];
422     DWORD count;
423     BOOL ret;
424
425     RtlInitUnicodeString( &valueW, module );
426
427     if ((ret = !NtQueryValueKey( hkey, &valueW, KeyValuePartialInformation,
428                                  buffer, sizeof(buffer), &count )))
429     {
430         int i, n = 0;
431         WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)buffer)->Data;
432
433         while (*str)
434         {
435             enum loadorder_type type = LOADORDER_INVALID;
436
437             while (*str == ',' || isspaceW(*str)) str++;
438             if (!*str) break;
439
440             switch(tolowerW(*str))
441             {
442             case 'n': type = LOADORDER_DLL; break;
443             case 'b': type = LOADORDER_BI; break;
444             case 's': break;  /* no longer supported, ignore */
445             case 0:   break;  /* end of string */
446             default:
447                 ERR("Invalid load order module-type %s, ignored\n", debugstr_w(str));
448                 break;
449             }
450             if (type != LOADORDER_INVALID)
451             {
452                 for (i = 0; i < n; i++) if (lo[i] == type) break;  /* already specified */
453                 if (i == n) lo[n++] = type;
454             }
455             while (*str && *str != ',' && !isspaceW(*str)) str++;
456         }
457         lo[n] = LOADORDER_INVALID;
458     }
459     return ret;
460 }
461
462
463 /***************************************************************************
464  *      MODULE_GetLoadOrderW    (internal)
465  *
466  * Return the loadorder of a module.
467  * The system directory and '.dll' extension is stripped from the path.
468  */
469 void MODULE_GetLoadOrderW( enum loadorder_type loadorder[], const WCHAR *app_name,
470                           const WCHAR *path )
471 {
472     static const WCHAR DllOverridesW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
473                                           'D','l','l','O','v','e','r','r','i','d','e','s',0};
474
475     static HANDLE std_key = (HANDLE)-1;  /* key to standard section, cached */
476
477     HANDLE app_key = 0;
478     WCHAR *module, *basename;
479     UNICODE_STRING path_str;
480     int len;
481
482     if (!init_done) init_load_order();
483
484     TRACE("looking for %s\n", debugstr_w(path));
485
486     loadorder[0] = LOADORDER_INVALID;  /* in case something bad happens below */
487
488     /* Strip path information if the module resides in the system directory
489      */
490     RtlInitUnicodeString( &path_str, path );
491     if (RtlPrefixUnicodeString( &system_dir, &path_str, TRUE ))
492     {
493         const WCHAR *p = path + system_dir.Length / sizeof(WCHAR);
494         while (*p == '\\' || *p == '/') p++;
495         if (!strchrW( p, '\\' ) && !strchrW( p, '/' )) path = p;
496     }
497
498     if (!(len = strlenW(path))) return;
499     if (!(module = RtlAllocateHeap( GetProcessHeap(), 0, (len + 2) * sizeof(WCHAR) ))) return;
500     strcpyW( module+1, path );  /* reserve module[0] for the wildcard char */
501
502     if (len >= 4) remove_dll_ext( module + 1 + len - 4 );
503
504     /* check environment variable first */
505     if (get_env_load_order( module+1, loadorder ))
506     {
507         TRACE( "got environment %s for %s\n",
508                debugstr_loadorder(loadorder), debugstr_w(path) );
509         goto done;
510     }
511
512     /* then explicit module name in AppDefaults */
513     if (app_name)
514     {
515         app_key = open_app_key( app_name, module+1 );
516         if (app_key && get_registry_value( app_key, module+1, loadorder ))
517         {
518             TRACE( "got app defaults %s for %s\n",
519                    debugstr_loadorder(loadorder), debugstr_w(path) );
520             goto done;
521         }
522     }
523
524     /* then explicit module name in standard section */
525     if (std_key == (HANDLE)-1)
526     {
527         OBJECT_ATTRIBUTES attr;
528         UNICODE_STRING nameW;
529         HANDLE root;
530
531         RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
532         attr.Length = sizeof(attr);
533         attr.RootDirectory = root;
534         attr.ObjectName = &nameW;
535         attr.Attributes = 0;
536         attr.SecurityDescriptor = NULL;
537         attr.SecurityQualityOfService = NULL;
538         RtlInitUnicodeString( &nameW, DllOverridesW );
539
540         /* @@ Wine registry key: HKCU\Software\Wine\DllOverrides */
541         if (NtOpenKey( &std_key, KEY_ALL_ACCESS, &attr )) std_key = 0;
542         NtClose( root );
543     }
544
545     if (std_key && get_registry_value( std_key, module+1, loadorder ))
546     {
547         TRACE( "got standard entry %s for %s\n",
548                debugstr_loadorder(loadorder), debugstr_w(path) );
549         goto done;
550     }
551
552     /* then module basename preceded by '*' in environment */
553     basename = (WCHAR *)get_basename( module+1 );
554     basename[-1] = '*';
555     if (get_env_load_order( basename-1, loadorder ))
556     {
557         TRACE( "got environment basename %s for %s\n",
558                debugstr_loadorder(loadorder), debugstr_w(path) );
559         goto done;
560     }
561
562     /* then module basename preceded by '*' in AppDefaults */
563     if (app_key && get_registry_value( app_key, basename-1, loadorder ))
564     {
565         TRACE( "got app defaults basename %s for %s\n",
566                debugstr_loadorder(loadorder), debugstr_w(path) );
567         goto done;
568     }
569
570     /* then module name preceded by '*' in standard section */
571     if (std_key && get_registry_value( std_key, basename-1, loadorder ))
572     {
573         TRACE( "got standard base name %s for %s\n",
574                debugstr_loadorder(loadorder), debugstr_w(path) );
575         goto done;
576     }
577
578     if (basename == module+1)  /* module doesn't contain a path */
579     {
580         static const WCHAR wildcardW[] = {'*',0};
581
582         /* then base name matching compiled-in defaults */
583         if (get_default_load_order( basename, loadorder ))
584         {
585             TRACE( "got compiled-in default %s for %s\n",
586                    debugstr_loadorder(loadorder), debugstr_w(path) );
587             goto done;
588         }
589
590         /* then wildcard entry in AppDefaults (only if no explicit path) */
591         if (app_key && get_registry_value( app_key, wildcardW, loadorder ))
592         {
593             TRACE( "got app defaults wildcard %s for %s\n",
594                    debugstr_loadorder(loadorder), debugstr_w(path) );
595             goto done;
596         }
597
598         /* then wildcard entry in standard section (only if no explicit path) */
599         if (std_key && get_registry_value( std_key, wildcardW, loadorder ))
600         {
601             TRACE( "got standard wildcard %s for %s\n",
602                    debugstr_loadorder(loadorder), debugstr_w(path) );
603             goto done;
604         }
605     }
606     else  /* module contains an explicit path */
607     {
608         /* then base name without '*' in environment */
609         if (get_env_load_order( basename, loadorder ))
610         {
611             TRACE( "got environment basename %s for %s\n",
612                    debugstr_loadorder(loadorder), debugstr_w(path) );
613             goto done;
614         }
615
616         /* then base name without '*' in AppDefaults */
617         if (app_key && get_registry_value( app_key, basename, loadorder ))
618         {
619             TRACE( "got basename app defaults %s for %s\n",
620                    debugstr_loadorder(loadorder), debugstr_w(path) );
621             goto done;
622         }
623
624         /* then base name without '*' in standard section */
625         if (std_key && get_registry_value( std_key, basename, loadorder ))
626         {
627             TRACE( "got basename standard entry %s for %s\n",
628                    debugstr_loadorder(loadorder), debugstr_w(path) );
629             goto done;
630         }
631
632         /* then base name matching compiled-in defaults */
633         if (get_default_load_order( basename, loadorder ))
634         {
635             TRACE( "got compiled-in default %s for %s\n",
636                    debugstr_loadorder(loadorder), debugstr_w(path) );
637             goto done;
638         }
639     }
640
641     /* and last the hard-coded default */
642     memcpy( loadorder, default_loadorder, sizeof(default_loadorder) );
643     TRACE( "got hardcoded default %s for %s\n",
644            debugstr_loadorder(loadorder), debugstr_w(path) );
645
646  done:
647     if (app_key) NtClose( app_key );
648     RtlFreeHeap( GetProcessHeap(), 0, module );
649 }