loadorder.c 14.5 KB
Newer Older
1
/*
2
 * Dlls load order support
3 4
 *
 * Copyright 1999 Bertho Stultiens
5
 * Copyright 2003 Alexandre Julliard
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 21
 */

22
#include "config.h"
23
#include "wine/port.h"
24

25
#include <stdarg.h>
26 27 28 29 30
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "windef.h"
31
#include "winternl.h"
32
#include "ntdll_misc.h"
33

34
#include "wine/debug.h"
35
#include "wine/unicode.h"
36

37
WINE_DEFAULT_DEBUG_CHANNEL(module);
38

39
#define LOADORDER_ALLOC_CLUSTER	32	/* Allocate with 32 entries at a time */
40

41 42
typedef struct module_loadorder
{
43
    const WCHAR        *modulename;
44
    enum loadorder      loadorder;
45
} module_loadorder_t;
46

47 48 49 50 51 52
struct loadorder_list
{
    int                 count;
    int                 alloc;
    module_loadorder_t *order;
};
53

54 55
static const WCHAR separatorsW[] = {',',' ','\t',0};

56 57
static int init_done;
static struct loadorder_list env_list;
58 59


60 61 62 63 64 65 66 67
/***************************************************************************
 *	cmp_sort_func	(internal, static)
 *
 * Sorting and comparing function used in sort and search of loadorder
 * entries.
 */
static int cmp_sort_func(const void *s1, const void *s2)
{
Eric Pouech's avatar
Eric Pouech committed
68
    return strcmpiW(((const module_loadorder_t *)s1)->modulename, ((const module_loadorder_t *)s2)->modulename);
69 70 71
}


72 73 74 75 76
/***************************************************************************
 *	get_basename
 *
 * Return the base name of a file name (i.e. remove the path components).
 */
77
static const WCHAR *get_basename( const WCHAR *name )
78
{
79
    const WCHAR *ptr;
80 81

    if (name[0] && name[1] == ':') name += 2;  /* strip drive specification */
82 83
    if ((ptr = strrchrW( name, '\\' ))) name = ptr + 1;
    if ((ptr = strrchrW( name, '/' ))) name = ptr + 1;
84 85 86
    return name;
}

87 88 89 90 91 92 93 94 95 96 97 98 99 100
/***************************************************************************
 *	remove_dll_ext
 *
 * Remove extension if it is ".dll".
 */
static inline void remove_dll_ext( WCHAR *ext )
{
    if (ext[0] == '.' &&
        toupperW(ext[1]) == 'D' &&
        toupperW(ext[2]) == 'L' &&
        toupperW(ext[3]) == 'L' &&
        !ext[4]) ext[0] = 0;
}

101 102 103 104 105 106

/***************************************************************************
 *	debugstr_loadorder
 *
 * Return a loadorder in printable form.
 */
107
static const char *debugstr_loadorder( enum loadorder lo )
108
{
109
    switch(lo)
110
    {
111 112 113 114 115 116 117
    case LO_DISABLED: return "";
    case LO_NATIVE: return "n";
    case LO_BUILTIN: return "b";
    case LO_NATIVE_BUILTIN: return "n,b";
    case LO_BUILTIN_NATIVE: return "b,n";
    case LO_DEFAULT: return "default";
    default: return "??";
118 119
    }
}
120 121


122 123 124 125 126 127
/***************************************************************************
 *	parse_load_order
 *
 * Parses the loadorder options from the configuration and puts it into
 * a structure.
 */
128
static enum loadorder parse_load_order( const WCHAR *order )
129
{
130 131
    enum loadorder ret = LO_DISABLED;

132 133
    while (*order)
    {
134
        order += strspnW( order, separatorsW );
135 136
        switch(*order)
        {
137
        case 'N':  /* native */
138
        case 'n':
139 140
            if (ret == LO_DISABLED) ret = LO_NATIVE;
            else if (ret == LO_BUILTIN) return LO_BUILTIN_NATIVE;
141
            break;
142
        case 'B':  /* builtin */
143
        case 'b':
144 145
            if (ret == LO_DISABLED) ret = LO_BUILTIN;
            else if (ret == LO_NATIVE) return LO_NATIVE_BUILTIN;
146 147
            break;
        }
148
        order += strcspnW( order, separatorsW );
149
    }
150
    return ret;
151 152 153 154
}


/***************************************************************************
155
 *	add_load_order
156
 *
157
 * Adds an entry in the list of environment overrides.
158
 */
159
static void add_load_order( const module_loadorder_t *plo )
160
{
161
    int i;
162

163 164 165 166 167
    for(i = 0; i < env_list.count; i++)
    {
        if(!cmp_sort_func(plo, &env_list.order[i] ))
        {
            /* replace existing option */
168
            env_list.order[i].loadorder = plo->loadorder;
169 170 171
            return;
        }
    }
172

173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
    if (i >= env_list.alloc)
    {
        /* No space in current array, make it larger */
        env_list.alloc += LOADORDER_ALLOC_CLUSTER;
        if (env_list.order)
            env_list.order = RtlReAllocateHeap(GetProcessHeap(), 0, env_list.order,
                                               env_list.alloc * sizeof(module_loadorder_t));
        else
            env_list.order = RtlAllocateHeap(GetProcessHeap(), 0,
                                             env_list.alloc * sizeof(module_loadorder_t));
        if(!env_list.order)
        {
            MESSAGE("Virtual memory exhausted\n");
            exit(1);
        }
    }
189
    env_list.order[i].loadorder  = plo->loadorder;
190
    env_list.order[i].modulename = plo->modulename;
191
    env_list.count++;
192 193 194 195
}


/***************************************************************************
196
 *	add_load_order_set
197
 *
198
 * Adds a set of entries in the list of command-line overrides from the key parameter.
199
 */
200
static void add_load_order_set( WCHAR *entry )
201
{
202
    module_loadorder_t ldo;
203
    WCHAR *end = strchrW( entry, '=' );
204

205 206
    if (!end) return;
    *end++ = 0;
207
    ldo.loadorder = parse_load_order( end );
208

209 210
    while (*entry)
    {
211 212
        entry += strspnW( entry, separatorsW );
        end = entry + strcspnW( entry, separatorsW );
213 214
        if (*end) *end++ = 0;
        if (*entry)
215 216 217
        {
            WCHAR *ext = strrchrW(entry, '.');
            if (ext) remove_dll_ext( ext );
218 219 220
            ldo.modulename = entry;
            add_load_order( &ldo );
            entry = end;
221
        }
222
    }
223 224 225 226
}


/***************************************************************************
227
 *	init_load_order
228
 */
229
static void init_load_order(void)
230
{
231
    const char *order = getenv( "WINEDLLOVERRIDES" );
232 233
    UNICODE_STRING strW;
    WCHAR *entry, *next;
234

235 236
    init_done = 1;
    if (!order) return;
237

238 239 240 241 242 243 244 245 246 247
    if (!strcmp( order, "help" ))
    {
        MESSAGE( "Syntax:\n"
                 "  WINEDLLOVERRIDES=\"entry;entry;entry...\"\n"
                 "    where each entry is of the form:\n"
                 "        module[,module...]={native|builtin}[,{b|n}]\n"
                 "\n"
                 "    Only the first letter of the override (native or builtin)\n"
                 "    is significant.\n\n"
                 "Example:\n"
248
                 "  WINEDLLOVERRIDES=\"comdlg32=n,b;shell32,shlwapi=b\"\n" );
249 250
        exit(0);
    }
251

252 253
    RtlCreateUnicodeStringFromAsciiz( &strW, order );
    entry = strW.Buffer;
254 255 256 257
    while (*entry)
    {
        while (*entry && *entry == ';') entry++;
        if (!*entry) break;
258
        next = strchrW( entry, ';' );
259
        if (next) *next++ = 0;
260
        else next = entry + strlenW(entry);
261 262 263
        add_load_order_set( entry );
        entry = next;
    }
264 265

    /* sort the array for quick lookup */
266 267
    if (env_list.count)
        qsort(env_list.order, env_list.count, sizeof(env_list.order[0]), cmp_sort_func);
268 269 270

    /* Note: we don't free the Unicode string because the
     * stored module names point inside it */
271 272 273
}


274
/***************************************************************************
275
 *	get_env_load_order
276
 *
277
 * Get the load order for a given module from the WINEDLLOVERRIDES environment variable.
278
 */
279
static inline enum loadorder get_env_load_order( const WCHAR *module )
280
{
281
    module_loadorder_t tmp, *res;
282

283
    tmp.modulename = module;
Eric Pouech's avatar
Eric Pouech committed
284
    /* some bsearch implementations (Solaris) are buggy when the number of items is 0 */
285 286
    if (env_list.count &&
        (res = bsearch(&tmp, env_list.order, env_list.count, sizeof(env_list.order[0]), cmp_sort_func)))
287 288
        return res->loadorder;
    return LO_INVALID;
289
}
290 291


292
/***************************************************************************
293
 *	get_standard_key
294
 *
295
 * Return a handle to the standard DllOverrides registry section.
296
 */
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
static HANDLE get_standard_key(void)
{
    static const WCHAR DllOverridesW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
                                          'D','l','l','O','v','e','r','r','i','d','e','s',0};
    static HANDLE std_key = (HANDLE)-1;

    if (std_key == (HANDLE)-1)
    {
        OBJECT_ATTRIBUTES attr;
        UNICODE_STRING nameW;
        HANDLE root;

        RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
        attr.Length = sizeof(attr);
        attr.RootDirectory = root;
        attr.ObjectName = &nameW;
        attr.Attributes = 0;
        attr.SecurityDescriptor = NULL;
        attr.SecurityQualityOfService = NULL;
        RtlInitUnicodeString( &nameW, DllOverridesW );

        /* @@ Wine registry key: HKCU\Software\Wine\DllOverrides */
        if (NtOpenKey( &std_key, KEY_ALL_ACCESS, &attr )) std_key = 0;
        NtClose( root );
    }
    return std_key;
}


/***************************************************************************
 *	get_app_key
 *
 * Get the registry key for the app-specific DllOverrides list.
 */
static HANDLE get_app_key( const WCHAR *app_name )
332
{
333 334
    OBJECT_ATTRIBUTES attr;
    UNICODE_STRING nameW;
335
    HANDLE root;
336
    WCHAR *str;
337
    static const WCHAR AppDefaultsW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
338 339
                                         'A','p','p','D','e','f','a','u','l','t','s','\\',0};
    static const WCHAR DllOverridesW[] = {'\\','D','l','l','O','v','e','r','r','i','d','e','s',0};
340 341 342
    static HANDLE app_key = (HANDLE)-1;

    if (app_key != (HANDLE)-1) return app_key;
343

344 345 346 347 348 349 350
    str = RtlAllocateHeap( GetProcessHeap(), 0,
                           sizeof(AppDefaultsW) + sizeof(DllOverridesW) +
                           strlenW(app_name) * sizeof(WCHAR) );
    if (!str) return 0;
    strcpyW( str, AppDefaultsW );
    strcatW( str, app_name );
    strcatW( str, DllOverridesW );
351

352
    RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
353
    attr.Length = sizeof(attr);
354
    attr.RootDirectory = root;
355 356 357 358
    attr.ObjectName = &nameW;
    attr.Attributes = 0;
    attr.SecurityDescriptor = NULL;
    attr.SecurityQualityOfService = NULL;
359 360
    RtlInitUnicodeString( &nameW, str );

361
    /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\DllOverrides */
362
    if (NtOpenKey( &app_key, KEY_ALL_ACCESS, &attr )) app_key = 0;
363
    NtClose( root );
364
    RtlFreeHeap( GetProcessHeap(), 0, str );
365
    return app_key;
366
}
367

368

369
/***************************************************************************
370
 *	get_registry_value
371
 *
372
 * Load the registry loadorder value for a given module.
373
 */
374
static enum loadorder get_registry_value( HANDLE hkey, const WCHAR *module )
375
{
376
    UNICODE_STRING valueW;
377
    char buffer[80];
378 379
    DWORD count;

380
    RtlInitUnicodeString( &valueW, module );
381

382 383
    if (!NtQueryValueKey( hkey, &valueW, KeyValuePartialInformation,
                                 buffer, sizeof(buffer), &count ))
384 385
    {
        WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)buffer)->Data;
386
        return parse_load_order( str );
387
    }
388
    return LO_INVALID;
389
}
390 391


392 393 394 395 396 397 398 399
/***************************************************************************
 *	get_load_order_value
 *
 * Get the load order for the exact specified module string, looking in:
 * 1. The WINEDLLOVERRIDES environment variable
 * 2. The per-application DllOverrides key
 * 3. The standard DllOverrides key
 */
400
static enum loadorder get_load_order_value( HANDLE std_key, HANDLE app_key, const WCHAR *module )
401
{
402 403 404
    enum loadorder ret;

    if ((ret = get_env_load_order( module )) != LO_INVALID)
405
    {
406 407
        TRACE( "got environment %s for %s\n", debugstr_loadorder(ret), debugstr_w(module) );
        return ret;
408 409
    }

410
    if (app_key && ((ret = get_registry_value( app_key, module )) != LO_INVALID))
411
    {
412 413
        TRACE( "got app defaults %s for %s\n", debugstr_loadorder(ret), debugstr_w(module) );
        return ret;
414 415
    }

416
    if (std_key && ((ret = get_registry_value( std_key, module )) != LO_INVALID))
417
    {
418 419
        TRACE( "got standard key %s for %s\n", debugstr_loadorder(ret), debugstr_w(module) );
        return ret;
420
    }
421 422

    return ret;
423 424 425
}


426
/***************************************************************************
427
 *	get_load_order   (internal)
428
 *
429 430
 * Return the loadorder of a module.
 * The system directory and '.dll' extension is stripped from the path.
431
 */
432
enum loadorder get_load_order( const WCHAR *app_name, const WCHAR *path )
433
{
434
    enum loadorder ret = LO_INVALID;
435
    HANDLE std_key, app_key = 0;
436
    WCHAR *module, *basename;
437
    UNICODE_STRING path_str;
438
    int len;
439

440
    if (!init_done) init_load_order();
441 442
    std_key = get_standard_key();
    if (app_name) app_key = get_app_key( app_name );
443

444
    TRACE("looking for %s\n", debugstr_w(path));
445

446 447
    /* Strip path information if the module resides in the system directory
     */
448
    RtlInitUnicodeString( &path_str, path );
449
    if (RtlPrefixUnicodeString( &system_dir, &path_str, TRUE ))
450
    {
451
        const WCHAR *p = path + system_dir.Length / sizeof(WCHAR);
452 453
        while (*p == '\\' || *p == '/') p++;
        if (!strchrW( p, '\\' ) && !strchrW( p, '/' )) path = p;
454 455
    }

456 457
    if (!(len = strlenW(path))) return ret;
    if (!(module = RtlAllocateHeap( GetProcessHeap(), 0, (len + 2) * sizeof(WCHAR) ))) return ret;
458
    strcpyW( module+1, path );  /* reserve module[0] for the wildcard char */
459
    basename = (WCHAR *)get_basename( module+1 );
460

461
    if (len >= 4) remove_dll_ext( module + 1 + len - 4 );
462

463
    /* first explicit module name */
464
    if ((ret = get_load_order_value( std_key, app_key, module+1 )) != LO_INVALID)
465
        goto done;
466

467
    /* then module basename preceded by '*' */
468
    basename[-1] = '*';
469
    if ((ret = get_load_order_value( std_key, app_key, basename-1 )) != LO_INVALID)
470 471
        goto done;

472
    /* then module basename without '*' (only if explicit path) */
473
    if (basename != module+1 && ((ret = get_load_order_value( std_key, app_key, basename )) != LO_INVALID))
474
        goto done;
475

476 477 478 479 480 481 482 483
    /* if loading the main exe with an explicit path, try native first */
    if (!app_name && basename != module+1)
    {
        ret = LO_NATIVE_BUILTIN;
        TRACE( "got main exe default %s for %s\n", debugstr_loadorder(ret), debugstr_w(path) );
        goto done;
    }

484
    /* and last the hard-coded default */
485
    ret = LO_DEFAULT;
486
    TRACE( "got hardcoded %s for %s\n", debugstr_loadorder(ret), debugstr_w(path) );
487

488
 done:
489
    RtlFreeHeap( GetProcessHeap(), 0, module );
490
    return ret;
491
}