path.c 18 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * File path.c - managing path in debugging environments
 *
 * Copyright (C) 2004, Eric Pouech
 *
 * 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
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 20 21 22 23 24 25 26
 */

#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "dbghelp_private.h"
27 28
#include "winnls.h"
#include "winternl.h"
29 30 31 32
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);

33
static inline BOOL is_sep(char ch) {return ch == '/' || ch == '\\';}
34
static inline BOOL is_sepW(WCHAR ch) {return ch == '/' || ch == '\\';}
35

36
static inline const char* file_name(const char* str)
37
{
38
    const char*       p;
39 40 41 42 43

    for (p = str + strlen(str) - 1; p >= str && !is_sep(*p); p--);
    return p + 1;
}

44 45 46 47 48 49 50 51
static inline const WCHAR* file_nameW(const WCHAR* str)
{
    const WCHAR*      p;

    for (p = str + strlenW(str) - 1; p >= str && !is_sepW(*p); p--);
    return p + 1;
}

52 53 54 55
/******************************************************************
 *		FindDebugInfoFile (DBGHELP.@)
 *
 */
56
HANDLE WINAPI FindDebugInfoFile(PCSTR FileName, PCSTR SymbolPath, PSTR DebugFilePath)
57 58 59
{
    HANDLE      h;

60
    h = CreateFileA(FileName, GENERIC_READ, FILE_SHARE_READ, NULL,
61 62 63
                    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (h == INVALID_HANDLE_VALUE)
    {
64
        if (!SearchPathA(SymbolPath, file_name(FileName), NULL, MAX_PATH, DebugFilePath, NULL))
65 66 67 68 69 70 71 72 73 74 75
            return NULL;
        h = CreateFileA(DebugFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
                        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    }
    return (h == INVALID_HANDLE_VALUE) ? NULL : h;
}
 
/******************************************************************
 *		FindDebugInfoFileEx (DBGHELP.@)
 *
 */
76
HANDLE WINAPI FindDebugInfoFileEx(PCSTR FileName, PCSTR SymbolPath,
77 78 79 80 81
                                  PSTR DebugFilePath, 
                                  PFIND_DEBUG_FILE_CALLBACK Callback,
                                  PVOID CallerData)
{
    FIXME("(%s %s %p %p %p): stub\n", 
82
          debugstr_a(FileName), debugstr_a(SymbolPath), debugstr_a(DebugFilePath), Callback, CallerData);
83 84 85 86
    return NULL;
}

/******************************************************************
87
 *		FindExecutableImageExW (DBGHELP.@)
88 89
 *
 */
90
HANDLE WINAPI FindExecutableImageExW(PCWSTR FileName, PCWSTR SymbolPath, PWSTR ImageFilePath,
91
                                     PFIND_EXE_FILE_CALLBACKW Callback, PVOID user)
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
{
    HANDLE h;

    if (Callback) FIXME("Unsupported callback yet\n");
    if (!SearchPathW(SymbolPath, FileName, NULL, MAX_PATH, ImageFilePath, NULL))
        return NULL;
    h = CreateFileW(ImageFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
                    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    return (h == INVALID_HANDLE_VALUE) ? NULL : h;
}

/******************************************************************
 *		FindExecutableImageEx (DBGHELP.@)
 *
 */
HANDLE WINAPI FindExecutableImageEx(PCSTR FileName, PCSTR SymbolPath, PSTR ImageFilePath,
108
                                    PFIND_EXE_FILE_CALLBACK Callback, PVOID user)
109 110
{
    HANDLE h;
111 112

    if (Callback) FIXME("Unsupported callback yet\n");
113 114
    if (!SearchPathA(SymbolPath, FileName, NULL, MAX_PATH, ImageFilePath, NULL))
        return NULL;
115
    h = CreateFileA(ImageFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
116 117 118 119
                    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    return (h == INVALID_HANDLE_VALUE) ? NULL : h;
}

120 121 122 123 124 125 126 127 128
/******************************************************************
 *		FindExecutableImage (DBGHELP.@)
 *
 */
HANDLE WINAPI FindExecutableImage(PCSTR FileName, PCSTR SymbolPath, PSTR ImageFilePath)
{
    return FindExecutableImageEx(FileName, SymbolPath, ImageFilePath, NULL, NULL);
}

129 130 131
/***********************************************************************
 *           MakeSureDirectoryPathExists (DBGHELP.@)
 */
132
BOOL WINAPI MakeSureDirectoryPathExists(PCSTR DirPath)
133
{
134 135 136 137 138 139 140
    char path[MAX_PATH];
    const char *p = DirPath;
    int  n;

    if (p[0] && p[1] == ':') p += 2;
    while (*p == '\\') p++; /* skip drive root */
    while ((p = strchr(p, '\\')) != NULL)
141
    {
142 143 144 145 146 147 148
       n = p - DirPath + 1;
       memcpy(path, DirPath, n);
       path[n] = '\0';
       if( !CreateDirectoryA(path, NULL)            &&
           (GetLastError() != ERROR_ALREADY_EXISTS))
           return FALSE;
       p++;
149
    }
150 151 152 153
    if (GetLastError() == ERROR_ALREADY_EXISTS)
       SetLastError(ERROR_SUCCESS);

    return TRUE;
154 155
}

156 157 158 159
/******************************************************************
 *		SymMatchFileNameW (DBGHELP.@)
 *
 */
160 161
BOOL WINAPI SymMatchFileNameW(PCWSTR file, PCWSTR match,
                              PWSTR* filestop, PWSTR* matchstop)
162
{
163 164
    PCWSTR fptr;
    PCWSTR mptr;
165 166 167 168 169 170 171 172 173 174 175 176 177

    TRACE("(%s %s %p %p)\n",
          debugstr_w(file), debugstr_w(match), filestop, matchstop);

    fptr = file + strlenW(file) - 1;
    mptr = match + strlenW(match) - 1;

    while (fptr >= file && mptr >= match)
    {
        if (toupperW(*fptr) != toupperW(*mptr) && !(is_sepW(*fptr) && is_sepW(*mptr)))
            break;
        fptr--; mptr--;
    }
178 179
    if (filestop) *filestop = (PWSTR)fptr;
    if (matchstop) *matchstop = (PWSTR)mptr;
180 181 182 183

    return mptr == match - 1;
}

184 185 186 187
/******************************************************************
 *		SymMatchFileName (DBGHELP.@)
 *
 */
188 189
BOOL WINAPI SymMatchFileName(PCSTR file, PCSTR match,
                             PSTR* filestop, PSTR* matchstop)
190
{
191 192
    PCSTR fptr;
    PCSTR mptr;
193

194
    TRACE("(%s %s %p %p)\n", debugstr_a(file), debugstr_a(match), filestop, matchstop);
195 196 197 198 199 200 201 202 203 204

    fptr = file + strlen(file) - 1;
    mptr = match + strlen(match) - 1;

    while (fptr >= file && mptr >= match)
    {
        if (toupper(*fptr) != toupper(*mptr) && !(is_sep(*fptr) && is_sep(*mptr)))
            break;
        fptr--; mptr--;
    }
205 206
    if (filestop) *filestop = (PSTR)fptr;
    if (matchstop) *matchstop = (PSTR)mptr;
207 208 209 210

    return mptr == match - 1;
}

211 212
static BOOL do_searchW(PCWSTR file, PWSTR buffer, BOOL recurse,
                       PENUMDIRTREE_CALLBACKW cb, PVOID user)
213 214 215 216 217 218 219
{
    HANDLE              h;
    WIN32_FIND_DATAW    fd;
    unsigned            pos;
    BOOL                found = FALSE;
    static const WCHAR  S_AllW[] = {'*','.','*','\0'};
    static const WCHAR  S_DotW[] = {'.','\0'};
220
    static const WCHAR  S_DotDotW[] = {'.','.','\0'};
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247

    pos = strlenW(buffer);
    if (buffer[pos - 1] != '\\') buffer[pos++] = '\\';
    strcpyW(buffer + pos, S_AllW);
    if ((h = FindFirstFileW(buffer, &fd)) == INVALID_HANDLE_VALUE)
        return FALSE;
    /* doc doesn't specify how the tree is enumerated...
     * doing a depth first based on, but may be wrong
     */
    do
    {
        if (!strcmpW(fd.cFileName, S_DotW) || !strcmpW(fd.cFileName, S_DotDotW)) continue;

        strcpyW(buffer + pos, fd.cFileName);
        if (recurse && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
            found = do_searchW(file, buffer, TRUE, cb, user);
        else if (SymMatchFileNameW(buffer, (WCHAR*)file, NULL, NULL))
        {
            if (!cb || cb(buffer, user)) found = TRUE;
        }
    } while (!found && FindNextFileW(h, &fd));
    if (!found) buffer[--pos] = '\0';
    FindClose(h);

    return found;
}

248 249 250 251 252 253 254 255 256 257 258
/***********************************************************************
 *           SearchTreeForFileW (DBGHELP.@)
 */
BOOL WINAPI SearchTreeForFileW(PCWSTR root, PCWSTR file, PWSTR buffer)
{
    TRACE("(%s, %s, %p)\n",
          debugstr_w(root), debugstr_w(file), buffer);
    strcpyW(buffer, root);
    return do_searchW(file, buffer, TRUE, NULL, NULL);
}

259 260 261
/***********************************************************************
 *           SearchTreeForFile (DBGHELP.@)
 */
262
BOOL WINAPI SearchTreeForFile(PCSTR root, PCSTR file, PSTR buffer)
263
{
264 265 266 267 268 269 270 271 272 273 274
    WCHAR       rootW[MAX_PATH];
    WCHAR       fileW[MAX_PATH];
    WCHAR       bufferW[MAX_PATH];
    BOOL        ret;

    MultiByteToWideChar(CP_ACP, 0, root, -1, rootW, MAX_PATH);
    MultiByteToWideChar(CP_ACP, 0, file, -1, fileW, MAX_PATH);
    ret = SearchTreeForFileW(rootW, fileW, bufferW);
    if (ret)
        WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
    return ret;
275 276
}

277 278 279 280 281 282
/******************************************************************
 *		EnumDirTreeW (DBGHELP.@)
 *
 *
 */
BOOL WINAPI EnumDirTreeW(HANDLE hProcess, PCWSTR root, PCWSTR file,
283
                        PWSTR buffer, PENUMDIRTREE_CALLBACKW cb, PVOID user)
284 285 286 287 288 289 290 291
{
    TRACE("(%p %s %s %p %p %p)\n",
          hProcess, debugstr_w(root), debugstr_w(file), buffer, cb, user);

    strcpyW(buffer, root);
    return do_searchW(file, buffer, TRUE, cb, user);
}

292 293 294 295 296
/******************************************************************
 *		EnumDirTree (DBGHELP.@)
 *
 *
 */
297 298 299 300 301 302 303
struct enum_dir_treeWA
{
    PENUMDIRTREE_CALLBACK       cb;
    void*                       user;
    char                        name[MAX_PATH];
};

304
static BOOL CALLBACK enum_dir_treeWA(PCWSTR name, PVOID user)
305 306 307 308 309 310 311
{
    struct enum_dir_treeWA*     edt = user;

    WideCharToMultiByte(CP_ACP, 0, name, -1, edt->name, MAX_PATH, NULL, NULL);
    return edt->cb(edt->name, edt->user);
}

312
BOOL WINAPI EnumDirTree(HANDLE hProcess, PCSTR root, PCSTR file,
313
                        PSTR buffer, PENUMDIRTREE_CALLBACK cb, PVOID user)
314
{
315 316 317 318 319 320 321 322 323 324 325 326 327
    WCHAR                       rootW[MAX_PATH];
    WCHAR                       fileW[MAX_PATH];
    WCHAR                       bufferW[MAX_PATH];
    struct enum_dir_treeWA      edt;
    BOOL                        ret;

    edt.cb = cb;
    edt.user = user;
    MultiByteToWideChar(CP_ACP, 0, root, -1, rootW, MAX_PATH);
    MultiByteToWideChar(CP_ACP, 0, file, -1, fileW, MAX_PATH);
    if ((ret = EnumDirTreeW(hProcess, rootW, fileW, bufferW, enum_dir_treeWA, &edt)))
        WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
    return ret;
328 329 330 331
}

struct sffip
{
332 333 334 335 336 337 338 339
    enum module_type            kind;
    /* pe:  id  -> DWORD:timestamp
     *      two -> size of image (from PE header)
     * pdb: id  -> PDB signature
     *            I think either DWORD:timestamp or GUID:guid depending on PDB version
     *      two -> PDB age ???
     * elf: id  -> DWORD:CRC 32 of ELF image (Wine only)
     */
340 341 342 343
    PVOID                       id;
    DWORD                       two;
    DWORD                       three;
    DWORD                       flags;
344
    PFINDFILEINPATHCALLBACKW    cb;
345 346 347
    void*                       user;
};

348 349 350
/* checks that buffer (as found by matching the name) matches the info
 * (information is based on file type)
 * returns TRUE when file is found, FALSE to continue searching
351
 * (NB this is the opposite convention of SymFindFileInPathProc)
352
 */
353
static BOOL CALLBACK sffip_cb(PCWSTR buffer, PVOID user)
354 355
{
    struct sffip*       s = (struct sffip*)user;
356
    DWORD               size, checksum;
357

358 359 360
    /* FIXME: should check that id/two/three match the file pointed
     * by buffer
     */
361 362 363 364 365 366
    switch (s->kind)
    {
    case DMT_PE:
        {
            HANDLE  hFile, hMap;
            void*   mapping;
367
            DWORD   timestamp;
368 369 370

            timestamp = ~(DWORD_PTR)s->id;
            size = ~s->two;
371
            hFile = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ, NULL,
372
                                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
373
            if (hFile == INVALID_HANDLE_VALUE) return FALSE;
374
            if ((hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != NULL)
375 376 377 378 379 380 381 382 383 384 385 386 387
            {
                if ((mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) != NULL)
                {
                    IMAGE_NT_HEADERS*   nth = RtlImageNtHeader(mapping);
                    timestamp = nth->FileHeader.TimeDateStamp;
                    size = nth->OptionalHeader.SizeOfImage;
                    UnmapViewOfFile(mapping);
                }
                CloseHandle(hMap);
            }
            CloseHandle(hFile);
            if (timestamp != (DWORD_PTR)s->id || size != s->two)
            {
388
                WARN("Found %s, but wrong size or timestamp\n", debugstr_w(buffer));
389
                return FALSE;
390 391 392 393
            }
        }
        break;
    case DMT_ELF:
394
        if (elf_fetch_file_info(buffer, 0, &size, &checksum))
395
        {
396
            if (checksum != (DWORD_PTR)s->id)
397
            {
398 399
                WARN("Found %s, but wrong checksums: %08x %08lx\n",
                     debugstr_w(buffer), checksum, (DWORD_PTR)s->id);
400
                return FALSE;
401 402
            }
        }
403 404 405 406 407
        else
        {
            WARN("Couldn't read %s\n", debugstr_w(buffer));
            return FALSE;
        }
408 409
        break;
    case DMT_PDB:
410 411
        {
            struct pdb_lookup   pdb_lookup;
412
            char                fn[MAX_PATH];
413

414 415
            WideCharToMultiByte(CP_ACP, 0, buffer, -1, fn, MAX_PATH, NULL, NULL);
            pdb_lookup.filename = fn;
416 417 418 419 420 421 422

            if (!pdb_fetch_file_info(&pdb_lookup)) return FALSE;
            switch (pdb_lookup.kind)
            {
            case PDB_JG:
                if (s->flags & SSRVOPT_GUIDPTR)
                {
423
                    WARN("Found %s, but wrong PDB version\n", debugstr_w(buffer));
424 425 426 427
                    return FALSE;
                }
                if (pdb_lookup.u.jg.timestamp != (DWORD_PTR)s->id)
                {
428
                    WARN("Found %s, but wrong signature: %08x %08lx\n",
429
                         debugstr_w(buffer), pdb_lookup.u.jg.timestamp, (DWORD_PTR)s->id);
430 431 432 433 434 435
                    return FALSE;
                }
                break;
            case PDB_DS:
                if (!(s->flags & SSRVOPT_GUIDPTR))
                {
436
                    WARN("Found %s, but wrong PDB version\n", debugstr_w(buffer));
437 438
                    return FALSE;
                }
439
                if (memcmp(&pdb_lookup.u.ds.guid, (GUID*)s->id, sizeof(GUID)))
440 441
                {
                    WARN("Found %s, but wrong GUID: %s %s\n",
442
                         debugstr_w(buffer), debugstr_guid(&pdb_lookup.u.ds.guid),
443 444 445 446 447 448 449
                         debugstr_guid((GUID*)s->id));
                    return FALSE;
                }
                break;
            }
            if (pdb_lookup.age != s->two)
            {
450
                WARN("Found %s, but wrong age: %08x %08x\n",
451
                     debugstr_w(buffer), pdb_lookup.age, s->two);
452 453 454
                return FALSE;
            }
        }
455 456 457
        break;
    default:
        FIXME("What the heck??\n");
458
        return FALSE;
459 460
    }
    /* yes, EnumDirTree/do_search and SymFindFileInPath callbacks use the opposite
461 462
     * convention to stop/continue enumeration. sigh.
     */
463
    return !(s->cb)((WCHAR*)buffer, s->user);
464 465 466
}

/******************************************************************
467
 *		SymFindFileInPathW (DBGHELP.@)
468 469
 *
 */
470 471
BOOL WINAPI SymFindFileInPathW(HANDLE hProcess, PCWSTR searchPath, PCWSTR full_path,
                               PVOID id, DWORD two, DWORD three, DWORD flags,
472
                               PWSTR buffer, PFINDFILEINPATHCALLBACKW cb,
473
                               PVOID user)
474 475 476
{
    struct sffip        s;
    struct process*     pcs = process_find_by_handle(hProcess);
477 478 479
    WCHAR               tmp[MAX_PATH];
    WCHAR*              ptr;
    const WCHAR*        filename;
480

481
    TRACE("(hProcess = %p, searchPath = %s, full_path = %s, id = %p, two = 0x%08x, three = 0x%08x, flags = 0x%08x, buffer = %p, cb = %p, user = %p)\n",
482 483
          hProcess, debugstr_w(searchPath), debugstr_w(full_path),
          id, two, three, flags, buffer, cb, user);
484 485

    if (!pcs) return FALSE;
486
    if (!searchPath) searchPath = pcs->search_path;
487 488 489 490 491 492 493 494

    s.id = id;
    s.two = two;
    s.three = three;
    s.flags = flags;
    s.cb = cb;
    s.user = user;

495 496
    filename = file_nameW(full_path);
    s.kind = module_get_type_by_name(filename);
497 498 499 500

    /* first check full path to file */
    if (sffip_cb(full_path, &s))
    {
501
        strcpyW(buffer, full_path);
502 503
        return TRUE;
    }
504 505 506

    while (searchPath)
    {
507
        ptr = strchrW(searchPath, ';');
508 509
        if (ptr)
        {
510
            memcpy(tmp, searchPath, (ptr - searchPath) * sizeof(WCHAR));
511 512 513 514 515
            tmp[ptr - searchPath] = 0;
            searchPath = ptr + 1;
        }
        else
        {
516
            strcpyW(tmp, searchPath);
517 518
            searchPath = NULL;
        }
519
        if (do_searchW(filename, tmp, FALSE, sffip_cb, &s))
520
        {
521
            strcpyW(buffer, tmp);
522 523
            return TRUE;
        }
524
    }
525 526
    return FALSE;
}
527 528 529 530 531 532 533

/******************************************************************
 *		SymFindFileInPath (DBGHELP.@)
 *
 */
BOOL WINAPI SymFindFileInPath(HANDLE hProcess, PCSTR searchPath, PCSTR full_path,
                              PVOID id, DWORD two, DWORD three, DWORD flags,
534
                              PSTR buffer, PFINDFILEINPATHCALLBACK cb,
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
                              PVOID user)
{
    WCHAR                       searchPathW[MAX_PATH];
    WCHAR                       full_pathW[MAX_PATH];
    WCHAR                       bufferW[MAX_PATH];
    struct enum_dir_treeWA      edt;
    BOOL                        ret;

    /* a PFINDFILEINPATHCALLBACK and a PENUMDIRTREE_CALLBACK have actually the
     * same signature & semantics, hence we can reuse the EnumDirTree W->A
     * conversion helper
     */
    edt.cb = cb;
    edt.user = user;
    if (searchPath)
        MultiByteToWideChar(CP_ACP, 0, searchPath, -1, searchPathW, MAX_PATH);
    MultiByteToWideChar(CP_ACP, 0, full_path, -1, full_pathW, MAX_PATH);
    if ((ret =  SymFindFileInPathW(hProcess, searchPath ? searchPathW : NULL, full_pathW,
                                   id, two, three, flags,
                                   bufferW, enum_dir_treeWA, &edt)))
        WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
    return ret;
}