2 * File path.c - managing path in debugging environments
4 * Copyright (C) 2004,2008, Eric Pouech
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * 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
26 #include "dbghelp_private.h"
29 #include "wine/debug.h"
31 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
== '\\';}
36 static inline const char* file_name(const char* str
)
40 for (p
= str
+ strlen(str
) - 1; p
>= str
&& !is_sep(*p
); p
--);
44 static inline const WCHAR
* file_nameW(const WCHAR
* str
)
48 for (p
= str
+ strlenW(str
) - 1; p
>= str
&& !is_sepW(*p
); p
--);
52 static inline void file_pathW(const WCHAR
*src
, WCHAR
*dst
)
56 for (len
= strlenW(src
) - 1; (len
> 0) && (!is_sepW(src
[len
])); len
--);
57 memcpy( dst
, src
, len
* sizeof(WCHAR
) );
61 /******************************************************************
62 * FindDebugInfoFile (DBGHELP.@)
65 HANDLE WINAPI
FindDebugInfoFile(PCSTR FileName
, PCSTR SymbolPath
, PSTR DebugFilePath
)
69 h
= CreateFileA(FileName
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
70 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
71 if (h
== INVALID_HANDLE_VALUE
)
73 if (!SearchPathA(SymbolPath
, file_name(FileName
), NULL
, MAX_PATH
, DebugFilePath
, NULL
))
75 h
= CreateFileA(DebugFilePath
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
76 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
78 return (h
== INVALID_HANDLE_VALUE
) ? NULL
: h
;
81 /******************************************************************
82 * FindDebugInfoFileEx (DBGHELP.@)
85 HANDLE WINAPI
FindDebugInfoFileEx(PCSTR FileName
, PCSTR SymbolPath
,
87 PFIND_DEBUG_FILE_CALLBACK Callback
,
90 FIXME("(%s %s %s %p %p): stub\n", debugstr_a(FileName
), debugstr_a(SymbolPath
),
91 debugstr_a(DebugFilePath
), Callback
, CallerData
);
95 /******************************************************************
96 * FindExecutableImageExW (DBGHELP.@)
99 HANDLE WINAPI
FindExecutableImageExW(PCWSTR FileName
, PCWSTR SymbolPath
, PWSTR ImageFilePath
,
100 PFIND_EXE_FILE_CALLBACKW Callback
, PVOID user
)
104 if (Callback
) FIXME("Unsupported callback yet\n");
105 if (!SearchPathW(SymbolPath
, FileName
, NULL
, MAX_PATH
, ImageFilePath
, NULL
))
107 h
= CreateFileW(ImageFilePath
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
108 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
109 return (h
== INVALID_HANDLE_VALUE
) ? NULL
: h
;
112 /******************************************************************
113 * FindExecutableImageEx (DBGHELP.@)
116 HANDLE WINAPI
FindExecutableImageEx(PCSTR FileName
, PCSTR SymbolPath
, PSTR ImageFilePath
,
117 PFIND_EXE_FILE_CALLBACK Callback
, PVOID user
)
121 if (Callback
) FIXME("Unsupported callback yet\n");
122 if (!SearchPathA(SymbolPath
, FileName
, NULL
, MAX_PATH
, ImageFilePath
, NULL
))
124 h
= CreateFileA(ImageFilePath
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
125 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
126 return (h
== INVALID_HANDLE_VALUE
) ? NULL
: h
;
129 /******************************************************************
130 * FindExecutableImage (DBGHELP.@)
133 HANDLE WINAPI
FindExecutableImage(PCSTR FileName
, PCSTR SymbolPath
, PSTR ImageFilePath
)
135 return FindExecutableImageEx(FileName
, SymbolPath
, ImageFilePath
, NULL
, NULL
);
138 /***********************************************************************
139 * MakeSureDirectoryPathExists (DBGHELP.@)
141 BOOL WINAPI
MakeSureDirectoryPathExists(PCSTR DirPath
)
144 const char *p
= DirPath
;
147 if (p
[0] && p
[1] == ':') p
+= 2;
148 while (*p
== '\\') p
++; /* skip drive root */
149 while ((p
= strchr(p
, '\\')) != NULL
)
152 memcpy(path
, DirPath
, n
);
154 if( !CreateDirectoryA(path
, NULL
) &&
155 (GetLastError() != ERROR_ALREADY_EXISTS
))
159 if (GetLastError() == ERROR_ALREADY_EXISTS
)
160 SetLastError(ERROR_SUCCESS
);
165 /******************************************************************
166 * SymMatchFileNameW (DBGHELP.@)
169 BOOL WINAPI
SymMatchFileNameW(PCWSTR file
, PCWSTR match
,
170 PWSTR
* filestop
, PWSTR
* matchstop
)
175 TRACE("(%s %s %p %p)\n",
176 debugstr_w(file
), debugstr_w(match
), filestop
, matchstop
);
178 fptr
= file
+ strlenW(file
) - 1;
179 mptr
= match
+ strlenW(match
) - 1;
181 while (fptr
>= file
&& mptr
>= match
)
183 if (toupperW(*fptr
) != toupperW(*mptr
) && !(is_sepW(*fptr
) && is_sepW(*mptr
)))
187 if (filestop
) *filestop
= (PWSTR
)fptr
;
188 if (matchstop
) *matchstop
= (PWSTR
)mptr
;
190 return mptr
== match
- 1;
193 /******************************************************************
194 * SymMatchFileName (DBGHELP.@)
197 BOOL WINAPI
SymMatchFileName(PCSTR file
, PCSTR match
,
198 PSTR
* filestop
, PSTR
* matchstop
)
203 TRACE("(%s %s %p %p)\n", debugstr_a(file
), debugstr_a(match
), filestop
, matchstop
);
205 fptr
= file
+ strlen(file
) - 1;
206 mptr
= match
+ strlen(match
) - 1;
208 while (fptr
>= file
&& mptr
>= match
)
210 if (toupper(*fptr
) != toupper(*mptr
) && !(is_sep(*fptr
) && is_sep(*mptr
)))
214 if (filestop
) *filestop
= (PSTR
)fptr
;
215 if (matchstop
) *matchstop
= (PSTR
)mptr
;
217 return mptr
== match
- 1;
220 static BOOL
do_searchW(PCWSTR file
, PWSTR buffer
, BOOL recurse
,
221 PENUMDIRTREE_CALLBACKW cb
, PVOID user
)
227 static const WCHAR S_AllW
[] = {'*','.','*','\0'};
228 static const WCHAR S_DotW
[] = {'.','\0'};
229 static const WCHAR S_DotDotW
[] = {'.','.','\0'};
231 pos
= strlenW(buffer
);
232 if (buffer
[pos
- 1] != '\\') buffer
[pos
++] = '\\';
233 strcpyW(buffer
+ pos
, S_AllW
);
234 if ((h
= FindFirstFileW(buffer
, &fd
)) == INVALID_HANDLE_VALUE
)
236 /* doc doesn't specify how the tree is enumerated...
237 * doing a depth first based on, but may be wrong
241 if (!strcmpW(fd
.cFileName
, S_DotW
) || !strcmpW(fd
.cFileName
, S_DotDotW
)) continue;
243 strcpyW(buffer
+ pos
, fd
.cFileName
);
244 if (recurse
&& (fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
245 found
= do_searchW(file
, buffer
, TRUE
, cb
, user
);
246 else if (SymMatchFileNameW(buffer
, file
, NULL
, NULL
))
248 if (!cb
|| cb(buffer
, user
)) found
= TRUE
;
250 } while (!found
&& FindNextFileW(h
, &fd
));
251 if (!found
) buffer
[--pos
] = '\0';
257 /***********************************************************************
258 * SearchTreeForFileW (DBGHELP.@)
260 BOOL WINAPI
SearchTreeForFileW(PCWSTR root
, PCWSTR file
, PWSTR buffer
)
262 TRACE("(%s, %s, %p)\n",
263 debugstr_w(root
), debugstr_w(file
), buffer
);
264 strcpyW(buffer
, root
);
265 return do_searchW(file
, buffer
, TRUE
, NULL
, NULL
);
268 /***********************************************************************
269 * SearchTreeForFile (DBGHELP.@)
271 BOOL WINAPI
SearchTreeForFile(PCSTR root
, PCSTR file
, PSTR buffer
)
273 WCHAR rootW
[MAX_PATH
];
274 WCHAR fileW
[MAX_PATH
];
275 WCHAR bufferW
[MAX_PATH
];
278 MultiByteToWideChar(CP_ACP
, 0, root
, -1, rootW
, MAX_PATH
);
279 MultiByteToWideChar(CP_ACP
, 0, file
, -1, fileW
, MAX_PATH
);
280 ret
= SearchTreeForFileW(rootW
, fileW
, bufferW
);
282 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, MAX_PATH
, NULL
, NULL
);
286 /******************************************************************
287 * EnumDirTreeW (DBGHELP.@)
291 BOOL WINAPI
EnumDirTreeW(HANDLE hProcess
, PCWSTR root
, PCWSTR file
,
292 PWSTR buffer
, PENUMDIRTREE_CALLBACKW cb
, PVOID user
)
294 TRACE("(%p %s %s %p %p %p)\n",
295 hProcess
, debugstr_w(root
), debugstr_w(file
), buffer
, cb
, user
);
297 strcpyW(buffer
, root
);
298 return do_searchW(file
, buffer
, TRUE
, cb
, user
);
301 /******************************************************************
302 * EnumDirTree (DBGHELP.@)
306 struct enum_dir_treeWA
308 PENUMDIRTREE_CALLBACK cb
;
313 static BOOL CALLBACK
enum_dir_treeWA(PCWSTR name
, PVOID user
)
315 struct enum_dir_treeWA
* edt
= user
;
317 WideCharToMultiByte(CP_ACP
, 0, name
, -1, edt
->name
, MAX_PATH
, NULL
, NULL
);
318 return edt
->cb(edt
->name
, edt
->user
);
321 BOOL WINAPI
EnumDirTree(HANDLE hProcess
, PCSTR root
, PCSTR file
,
322 PSTR buffer
, PENUMDIRTREE_CALLBACK cb
, PVOID user
)
324 WCHAR rootW
[MAX_PATH
];
325 WCHAR fileW
[MAX_PATH
];
326 WCHAR bufferW
[MAX_PATH
];
327 struct enum_dir_treeWA edt
;
332 MultiByteToWideChar(CP_ACP
, 0, root
, -1, rootW
, MAX_PATH
);
333 MultiByteToWideChar(CP_ACP
, 0, file
, -1, fileW
, MAX_PATH
);
334 if ((ret
= EnumDirTreeW(hProcess
, rootW
, fileW
, bufferW
, enum_dir_treeWA
, &edt
)))
335 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, MAX_PATH
, NULL
, NULL
);
341 PFINDFILEINPATHCALLBACKW cb
;
345 /* checks that buffer (as found by matching the name) matches the info
346 * (information is based on file type)
347 * returns TRUE when file is found, FALSE to continue searching
348 * (NB this is the opposite convention of SymFindFileInPathProc)
350 static BOOL CALLBACK
sffip_cb(PCWSTR buffer
, PVOID user
)
352 struct sffip
* s
= user
;
354 if (!s
->cb
) return TRUE
;
355 /* yes, EnumDirTree/do_search and SymFindFileInPath callbacks use the opposite
356 * convention to stop/continue enumeration. sigh.
358 return !(s
->cb
)(buffer
, s
->user
);
361 /******************************************************************
362 * SymFindFileInPathW (DBGHELP.@)
365 BOOL WINAPI
SymFindFileInPathW(HANDLE hProcess
, PCWSTR searchPath
, PCWSTR full_path
,
366 PVOID id
, DWORD two
, DWORD three
, DWORD flags
,
367 PWSTR buffer
, PFINDFILEINPATHCALLBACKW cb
,
371 struct process
* pcs
= process_find_by_handle(hProcess
);
374 const WCHAR
* filename
;
376 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",
377 hProcess
, debugstr_w(searchPath
), debugstr_w(full_path
),
378 id
, two
, three
, flags
, buffer
, cb
, user
);
380 if (!pcs
) return FALSE
;
381 if (!searchPath
) searchPath
= pcs
->search_path
;
386 filename
= file_nameW(full_path
);
388 /* first check full path to file */
389 if (sffip_cb(full_path
, &s
))
391 strcpyW(buffer
, full_path
);
397 ptr
= strchrW(searchPath
, ';');
400 memcpy(tmp
, searchPath
, (ptr
- searchPath
) * sizeof(WCHAR
));
401 tmp
[ptr
- searchPath
] = 0;
402 searchPath
= ptr
+ 1;
406 strcpyW(tmp
, searchPath
);
409 if (do_searchW(filename
, tmp
, FALSE
, sffip_cb
, &s
))
411 strcpyW(buffer
, tmp
);
418 /******************************************************************
419 * SymFindFileInPath (DBGHELP.@)
422 BOOL WINAPI
SymFindFileInPath(HANDLE hProcess
, PCSTR searchPath
, PCSTR full_path
,
423 PVOID id
, DWORD two
, DWORD three
, DWORD flags
,
424 PSTR buffer
, PFINDFILEINPATHCALLBACK cb
,
427 WCHAR searchPathW
[MAX_PATH
];
428 WCHAR full_pathW
[MAX_PATH
];
429 WCHAR bufferW
[MAX_PATH
];
430 struct enum_dir_treeWA edt
;
433 /* a PFINDFILEINPATHCALLBACK and a PENUMDIRTREE_CALLBACK have actually the
434 * same signature & semantics, hence we can reuse the EnumDirTree W->A
440 MultiByteToWideChar(CP_ACP
, 0, searchPath
, -1, searchPathW
, MAX_PATH
);
441 MultiByteToWideChar(CP_ACP
, 0, full_path
, -1, full_pathW
, MAX_PATH
);
442 if ((ret
= SymFindFileInPathW(hProcess
, searchPath
? searchPathW
: NULL
, full_pathW
,
443 id
, two
, three
, flags
,
444 bufferW
, enum_dir_treeWA
, &edt
)))
445 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, MAX_PATH
, NULL
, NULL
);
451 enum module_type kind
;
452 /* pe: dw1 DWORD:timestamp
453 * dw2 size of image (from PE header)
454 * pdb: guid PDB guid (if DS PDB file)
455 * or dw1 PDB timestamp (if JG PDB file)
457 * elf: dw1 DWORD:CRC 32 of ELF image (Wine only)
462 WCHAR filename
[MAX_PATH
];
466 /* checks that buffer (as found by matching the name) matches the info
467 * (information is based on file type)
468 * returns TRUE when file is found, FALSE to continue searching
469 * (NB this is the opposite convention of SymFindFileInPathProc)
471 static BOOL CALLBACK
module_find_cb(PCWSTR buffer
, PVOID user
)
473 struct module_find
* mf
= user
;
474 DWORD size
, checksum
, timestamp
;
475 unsigned matched
= 0;
477 /* the matching weights:
478 * +1 if a file with same name is found and is a decent file of expected type
479 * +1 if first parameter and second parameter match
482 /* FIXME: should check that id/two match the file pointed
492 timestamp
= ~mf
->dw1
;
494 hFile
= CreateFileW(buffer
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
495 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
496 if (hFile
== INVALID_HANDLE_VALUE
) return FALSE
;
497 if ((hMap
= CreateFileMappingW(hFile
, NULL
, PAGE_READONLY
, 0, 0, NULL
)) != NULL
)
499 if ((mapping
= MapViewOfFile(hMap
, FILE_MAP_READ
, 0, 0, 0)) != NULL
)
501 IMAGE_NT_HEADERS
* nth
= RtlImageNtHeader(mapping
);
504 UnmapViewOfFile(mapping
);
510 timestamp
= nth
->FileHeader
.TimeDateStamp
;
511 size
= nth
->OptionalHeader
.SizeOfImage
;
512 UnmapViewOfFile(mapping
);
517 if (timestamp
!= mf
->dw1
)
518 WARN("Found %s, but wrong timestamp\n", debugstr_w(buffer
));
520 WARN("Found %s, but wrong size\n", debugstr_w(buffer
));
521 if (timestamp
== mf
->dw1
&& size
== mf
->dw2
) matched
++;
525 if (elf_fetch_file_info(buffer
, 0, &size
, &checksum
))
528 if (checksum
== mf
->dw1
) matched
++;
530 WARN("Found %s, but wrong checksums: %08x %08x\n",
531 debugstr_w(buffer
), checksum
, mf
->dw1
);
535 WARN("Couldn't read %s\n", debugstr_w(buffer
));
540 if (macho_fetch_file_info(NULL
, buffer
, 0, 0, &size
, &checksum
))
543 if (checksum
== mf
->dw1
) matched
++;
545 WARN("Found %s, but wrong checksums: %08x %08x\n",
546 debugstr_w(buffer
), checksum
, mf
->dw1
);
550 WARN("Couldn't read %s\n", debugstr_w(buffer
));
556 struct pdb_lookup pdb_lookup
;
559 WideCharToMultiByte(CP_ACP
, 0, buffer
, -1, fn
, MAX_PATH
, NULL
, NULL
);
560 pdb_lookup
.filename
= fn
;
564 pdb_lookup
.kind
= PDB_DS
;
565 pdb_lookup
.timestamp
= 0;
566 pdb_lookup
.guid
= *mf
->guid
;
570 pdb_lookup
.kind
= PDB_JG
;
571 pdb_lookup
.timestamp
= mf
->dw1
;
572 /* pdb_loopkup.guid = */
574 pdb_lookup
.age
= mf
->dw2
;
576 if (!pdb_fetch_file_info(&pdb_lookup
, &matched
)) return FALSE
;
584 timestamp
= ~mf
->dw1
;
585 hFile
= CreateFileW(buffer
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
586 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
587 if (hFile
== INVALID_HANDLE_VALUE
) return FALSE
;
588 if ((hMap
= CreateFileMappingW(hFile
, NULL
, PAGE_READONLY
, 0, 0, NULL
)) != NULL
)
590 if ((mapping
= MapViewOfFile(hMap
, FILE_MAP_READ
, 0, 0, 0)) != NULL
)
592 const IMAGE_SEPARATE_DEBUG_HEADER
* hdr
;
595 if (hdr
->Signature
== IMAGE_SEPARATE_DEBUG_SIGNATURE
)
598 timestamp
= hdr
->TimeDateStamp
;
600 UnmapViewOfFile(mapping
);
605 if (timestamp
== mf
->dw1
) matched
++;
606 else WARN("Found %s, but wrong timestamp\n", debugstr_w(buffer
));
610 FIXME("What the heck??\n");
613 if (matched
> mf
->matched
)
615 strcpyW(mf
->filename
, buffer
);
616 mf
->matched
= matched
;
618 /* yes, EnumDirTree/do_search and SymFindFileInPath callbacks use the opposite
619 * convention to stop/continue enumeration. sigh.
621 return mf
->matched
== 2;
624 BOOL
path_find_symbol_file(const struct process
* pcs
, const struct module
* module
,
625 PCSTR full_path
, const GUID
* guid
, DWORD dw1
, DWORD dw2
,
626 WCHAR
*buffer
, BOOL
* is_unmatched
)
628 struct module_find mf
;
629 WCHAR full_pathW
[MAX_PATH
];
631 const WCHAR
* filename
;
632 WCHAR
* searchPath
= pcs
->search_path
;
634 TRACE("(pcs = %p, full_path = %s, guid = %s, dw1 = 0x%08x, dw2 = 0x%08x, buffer = %p)\n",
635 pcs
, debugstr_a(full_path
), debugstr_guid(guid
), dw1
, dw2
, buffer
);
642 MultiByteToWideChar(CP_ACP
, 0, full_path
, -1, full_pathW
, MAX_PATH
);
643 filename
= file_nameW(full_pathW
);
644 mf
.kind
= module_get_type_by_name(filename
);
645 *is_unmatched
= FALSE
;
647 /* first check full path to file */
648 if (module_find_cb(full_pathW
, &mf
))
650 strcpyW( buffer
, full_pathW
);
654 /* FIXME: Use Environment-Variables (see MS docs)
655 _NT_SYMBOL_PATH and _NT_ALT_SYMBOL_PATH
656 FIXME: Implement "Standard Path Elements" (Path) ... (see MS docs)
657 do a search for (every?) path-element like this ...
661 (dll may be exe, or sys depending on the file extension) */
663 /* 2. check module-path */
664 file_pathW(module
->module
.LoadedImageName
, buffer
);
665 if (do_searchW(filename
, buffer
, FALSE
, module_find_cb
, &mf
)) return TRUE
;
669 ptr
= strchrW(searchPath
, ';');
672 memcpy(buffer
, searchPath
, (ptr
- searchPath
) * sizeof(WCHAR
));
673 buffer
[ptr
- searchPath
] = '\0';
674 searchPath
= ptr
+ 1;
678 strcpyW(buffer
, searchPath
);
681 /* return first fully matched file */
682 if (do_searchW(filename
, buffer
, FALSE
, module_find_cb
, &mf
)) return TRUE
;
684 /* if no fully matching file is found, return the best matching file if any */
685 if ((dbghelp_options
& SYMOPT_LOAD_ANYTHING
) && mf
.matched
)
687 strcpyW( buffer
, mf
.filename
);
688 *is_unmatched
= TRUE
;