quartz: Use '%u' to print GetLastError().
[wine] / dlls / dbghelp / path.c
1 /*
2  * File path.c - managing path in debugging environments
3  *
4  * Copyright (C) 2004, Eric Pouech
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "config.h"
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25
26 #include "dbghelp_private.h"
27 #include "winnls.h"
28 #include "winreg.h"
29 #include "winternl.h"
30 #include "wine/debug.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
33
34 static inline BOOL is_sep(char ch) {return ch == '/' || ch == '\\';}
35
36 static inline const char* file_name(const char* str)
37 {
38     const char*       p;
39
40     for (p = str + strlen(str) - 1; p >= str && !is_sep(*p); p--);
41     return p + 1;
42 }
43
44 /******************************************************************
45  *              FindDebugInfoFile (DBGHELP.@)
46  *
47  */
48 HANDLE WINAPI FindDebugInfoFile(PCSTR FileName, PCSTR SymbolPath, PSTR DebugFilePath)
49 {
50     HANDLE      h;
51
52     h = CreateFileA(DebugFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
53                     OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
54     if (h == INVALID_HANDLE_VALUE)
55     {
56         if (!SearchPathA(SymbolPath, file_name(FileName), NULL, MAX_PATH, DebugFilePath, NULL))
57             return NULL;
58         h = CreateFileA(DebugFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
59                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
60     }
61     return (h == INVALID_HANDLE_VALUE) ? NULL : h;
62 }
63  
64 /******************************************************************
65  *              FindDebugInfoFileEx (DBGHELP.@)
66  *
67  */
68 HANDLE WINAPI FindDebugInfoFileEx(PCSTR FileName, PCSTR SymbolPath,
69                                   PSTR DebugFilePath, 
70                                   PFIND_DEBUG_FILE_CALLBACK Callback,
71                                   PVOID CallerData)
72 {
73     FIXME("(%s %s %p %p %p): stub\n", 
74           FileName, SymbolPath, DebugFilePath, Callback, CallerData);
75     return NULL;
76 }
77
78 /******************************************************************
79  *              FindExecutableImage (DBGHELP.@)
80  *
81  */
82 HANDLE WINAPI FindExecutableImage(PCSTR FileName, PCSTR SymbolPath, PSTR ImageFilePath)
83 {
84     HANDLE h;
85     if (!SearchPathA(SymbolPath, FileName, NULL, MAX_PATH, ImageFilePath, NULL))
86         return NULL;
87     h = CreateFileA(ImageFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, 
88                     OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
89     return (h == INVALID_HANDLE_VALUE) ? NULL : h;
90 }
91
92 /***********************************************************************
93  *           MakeSureDirectoryPathExists (DBGHELP.@)
94  */
95 BOOL WINAPI MakeSureDirectoryPathExists(LPCSTR DirPath)
96 {
97     char path[MAX_PATH];
98     const char *p = DirPath;
99     int  n;
100
101     if (p[0] && p[1] == ':') p += 2;
102     while (*p == '\\') p++; /* skip drive root */
103     while ((p = strchr(p, '\\')) != NULL)
104     {
105        n = p - DirPath + 1;
106        memcpy(path, DirPath, n);
107        path[n] = '\0';
108        if( !CreateDirectoryA(path, NULL)            &&
109            (GetLastError() != ERROR_ALREADY_EXISTS))
110            return FALSE;
111        p++;
112     }
113     if (GetLastError() == ERROR_ALREADY_EXISTS)
114        SetLastError(ERROR_SUCCESS);
115
116     return TRUE;
117 }
118
119 /******************************************************************
120  *              SymMatchFileName (DBGHELP.@)
121  *
122  */
123 BOOL WINAPI SymMatchFileName(char* file, char* match,
124                              char** filestop, char** matchstop)
125 {
126     char*       fptr;
127     char*       mptr;
128
129     TRACE("(%s %s %p %p)\n", file, match, filestop, matchstop);
130
131     fptr = file + strlen(file) - 1;
132     mptr = match + strlen(match) - 1;
133
134     while (fptr >= file && mptr >= match)
135     {
136         if (toupper(*fptr) != toupper(*mptr) && !(is_sep(*fptr) && is_sep(*mptr)))
137             break;
138         fptr--; mptr--;
139     }
140     if (filestop) *filestop = fptr;
141     if (matchstop) *matchstop = mptr;
142
143     return mptr == match - 1;
144 }
145
146 static BOOL do_search(const char* file, char* buffer, BOOL recurse,
147                       PENUMDIRTREE_CALLBACK cb, void* user)
148 {
149     HANDLE              h;
150     WIN32_FIND_DATAA    fd;
151     unsigned            pos;
152     BOOL                found = FALSE;
153
154     pos = strlen(buffer);
155     if (buffer[pos - 1] != '\\') buffer[pos++] = '\\';
156     strcpy(buffer + pos, "*.*");
157     if ((h = FindFirstFileA(buffer, &fd)) == INVALID_HANDLE_VALUE)
158         return FALSE;
159     /* doc doesn't specify how the tree is enumerated... 
160      * doing a depth first based on, but may be wrong
161      */
162     do
163     {
164         if (!strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, "..")) continue;
165
166         strcpy(buffer + pos, fd.cFileName);
167         if (recurse && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
168             found = do_search(file, buffer, TRUE, cb, user);
169         else if (SymMatchFileName(buffer, (char*)file, NULL, NULL))
170         {
171             if (!cb || cb(buffer, user)) found = TRUE;
172         }
173     } while (!found && FindNextFileA(h, &fd));
174     if (!found) buffer[--pos] = '\0';
175     FindClose(h);
176
177     return found;
178 }
179
180 /***********************************************************************
181  *           SearchTreeForFile (DBGHELP.@)
182  */
183 BOOL WINAPI SearchTreeForFile(PCSTR root, PCSTR file, PSTR buffer)
184 {
185     TRACE("(%s, %s, %p)\n", 
186           debugstr_a(root), debugstr_a(file), buffer);
187     strcpy(buffer, root);
188     return do_search(file, buffer, TRUE, NULL, NULL);
189 }
190
191 /******************************************************************
192  *              EnumDirTree (DBGHELP.@)
193  *
194  *
195  */
196 BOOL WINAPI EnumDirTree(HANDLE hProcess, PCSTR root, PCSTR file,
197                         LPSTR buffer, PENUMDIRTREE_CALLBACK cb, PVOID user)
198 {
199     TRACE("(%p %s %s %p %p %p)\n", hProcess, root, file, buffer, cb, user);
200
201     strcpy(buffer, root);
202     return do_search(file, buffer, TRUE, cb, user);
203 }
204
205 struct sffip
206 {
207     enum module_type            kind;
208     /* pe:  id  -> DWORD:timestamp
209      *      two -> size of image (from PE header)
210      * pdb: id  -> PDB signature
211      *            I think either DWORD:timestamp or GUID:guid depending on PDB version
212      *      two -> PDB age ???
213      * elf: id  -> DWORD:CRC 32 of ELF image (Wine only)
214      */
215     PVOID                       id;
216     DWORD                       two;
217     DWORD                       three;
218     DWORD                       flags;
219     PFINDFILEINPATHCALLBACK     cb;
220     void*                       user;
221 };
222
223 /* checks that buffer (as found by matching the name) matches the info
224  * (information is based on file type)
225  * returns TRUE when file is found, FALSE to continue searching
226  * (NB this is the opposite conventions as for SymFindFileInPathProc)
227  */
228 static BOOL CALLBACK sffip_cb(LPCSTR buffer, void* user)
229 {
230     struct sffip*       s = (struct sffip*)user;
231     DWORD               size, checksum;
232
233     /* FIXME: should check that id/two/three match the file pointed
234      * by buffer
235      */
236     switch (s->kind)
237     {
238     case DMT_PE:
239         {
240             HANDLE  hFile, hMap;
241             void*   mapping;
242             DWORD   timestamp;
243
244             timestamp = ~(DWORD_PTR)s->id;
245             size = ~s->two;
246             hFile = CreateFileA(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, 
247                                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
248             if (hFile == INVALID_HANDLE_VALUE) return FALSE;
249             if ((hMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != NULL)
250             {
251                 if ((mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) != NULL)
252                 {
253                     IMAGE_NT_HEADERS*   nth = RtlImageNtHeader(mapping);
254                     timestamp = nth->FileHeader.TimeDateStamp;
255                     size = nth->OptionalHeader.SizeOfImage;
256                     UnmapViewOfFile(mapping);
257                 }
258                 CloseHandle(hMap);
259             }
260             CloseHandle(hFile);
261             if (timestamp != (DWORD_PTR)s->id || size != s->two)
262             {
263                 WARN("Found %s, but wrong size or timestamp\n", buffer);
264                 return FALSE;
265             }
266         }
267         break;
268     case DMT_ELF:
269         if (elf_fetch_file_info(buffer, 0, &size, &checksum))
270         {
271             if (checksum != (DWORD_PTR)s->id)
272             {
273                 WARN("Found %s, but wrong checksums: %08x %08lx\n",
274                       buffer, checksum, (DWORD_PTR)s->id);
275                 return FALSE;
276             }
277         }
278         else
279         {
280             WARN("Couldn't read %s\n", buffer);
281             return FALSE;
282         }
283         break;
284     case DMT_PDB:
285         {
286             struct pdb_lookup   pdb_lookup;
287
288             pdb_lookup.filename = buffer;
289
290             if (!pdb_fetch_file_info(&pdb_lookup)) return FALSE;
291             switch (pdb_lookup.kind)
292             {
293             case PDB_JG:
294                 if (s->flags & SSRVOPT_GUIDPTR)
295                 {
296                     WARN("Found %s, but wrong PDB version\n", buffer);
297                     return FALSE;
298                 }
299                 if (pdb_lookup.u.jg.timestamp != (DWORD_PTR)s->id)
300                 {
301                     WARN("Found %s, but wrong signature: %08x %08lx\n",
302                          buffer, pdb_lookup.u.jg.timestamp, (DWORD_PTR)s->id);
303                     return FALSE;
304                 }
305                 break;
306             case PDB_DS:
307                 if (!(s->flags & SSRVOPT_GUIDPTR))
308                 {
309                     WARN("Found %s, but wrong PDB version\n", buffer);
310                     return FALSE;
311                 }
312                 if (memcmp(&pdb_lookup.u.ds.guid, (GUID*)s->id, sizeof(GUID)))
313                 {
314                     WARN("Found %s, but wrong GUID: %s %s\n",
315                          buffer, debugstr_guid(&pdb_lookup.u.ds.guid),
316                          debugstr_guid((GUID*)s->id));
317                     return FALSE;
318                 }
319                 break;
320             }
321             if (pdb_lookup.age != s->two)
322             {
323                 WARN("Found %s, but wrong age: %08x %08x\n",
324                      buffer, pdb_lookup.age, s->two);
325                 return FALSE;
326             }
327         }
328         break;
329     default:
330         FIXME("What the heck??\n");
331         return FALSE;
332     }
333     /* yes, EnumDirTree/do_search and SymFindFileInPath callbacks use the opposite
334      * convention to stop/continue enumeration. sigh.
335      */
336     return !(s->cb)((char*)buffer, s->user);
337 }
338
339 /******************************************************************
340  *              SymFindFileInPath (DBGHELP.@)
341  *
342  */
343 BOOL WINAPI SymFindFileInPath(HANDLE hProcess, PCSTR inSearchPath, PCSTR full_path,
344                               PVOID id, DWORD two, DWORD three, DWORD flags,
345                               LPSTR buffer, PFINDFILEINPATHCALLBACK cb,
346                               PVOID user)
347 {
348     struct sffip        s;
349     struct process*     pcs = process_find_by_handle(hProcess);
350     char                tmp[MAX_PATH];
351     char*               ptr;
352     char*               buf = NULL;
353     const char*         filename;
354     const char*         searchPath = inSearchPath;
355
356     TRACE("(%p %s %s %p %08x %08x %08x %p %p %p)\n",
357           hProcess, searchPath, full_path, id, two, three, flags,
358           buffer, cb, user);
359
360     if (!pcs) return FALSE;
361     if (!searchPath)
362     {
363         unsigned len = WideCharToMultiByte(CP_ACP, 0, pcs->search_path, -1, NULL, 0, NULL, NULL);
364
365         searchPath = buf = HeapAlloc(GetProcessHeap(), 0, len);
366         if (!searchPath) return FALSE;
367         WideCharToMultiByte(CP_ACP, 0, pcs->search_path, -1, buf, len, NULL, NULL);
368     }
369
370     s.id = id;
371     s.two = two;
372     s.three = three;
373     s.flags = flags;
374     s.cb = cb;
375     s.user = user;
376
377     filename = file_name(full_path);
378     s.kind = module_get_type_by_name(filename);
379
380     /* first check full path to file */
381     if (sffip_cb(full_path, &s))
382     {
383         strcpy(buffer, full_path);
384         HeapFree(GetProcessHeap(), 0, buf);
385         return TRUE;
386     }
387
388     while (searchPath)
389     {
390         ptr = strchr(searchPath, ';');
391         if (ptr)
392         {
393             memcpy(tmp, searchPath, ptr - searchPath);
394             tmp[ptr - searchPath] = 0;
395             searchPath = ptr + 1;
396         }
397         else
398         {
399             strcpy(tmp, searchPath);
400             searchPath = NULL;
401         }
402         if (do_search(filename, tmp, FALSE, sffip_cb, &s))
403         {
404             strcpy(buffer, tmp);
405             HeapFree(GetProcessHeap(), 0, buf);
406             return TRUE;
407         }
408     }
409     HeapFree(GetProcessHeap(), 0, buf);
410     return FALSE;
411 }