dbghelp: Always set the size to public symbols to 1 when we don't know the size
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 static BOOL CALLBACK sffip_cb(LPCSTR buffer, void* user)
224 {
225     struct sffip*       s = (struct sffip*)user;
226     DWORD               size, checksum;
227     DWORD_PTR           timestamp;
228     /* FIXME: should check that id/two/three match the file pointed
229      * by buffer
230      */
231     switch (s->kind)
232     {
233     case DMT_PE:
234         {
235             HANDLE  hFile, hMap;
236             void*   mapping;
237
238             timestamp = ~(DWORD_PTR)s->id;
239             size = ~s->two;
240             hFile = CreateFileA(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, 
241                                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
242             if (hFile == INVALID_HANDLE_VALUE) return TRUE;
243             if ((hMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != NULL)
244             {
245                 if ((mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) != NULL)
246                 {
247                     IMAGE_NT_HEADERS*   nth = RtlImageNtHeader(mapping);
248                     timestamp = nth->FileHeader.TimeDateStamp;
249                     size = nth->OptionalHeader.SizeOfImage;
250                     UnmapViewOfFile(mapping);
251                 }
252                 CloseHandle(hMap);
253             }
254             CloseHandle(hFile);
255             if (timestamp != (DWORD_PTR)s->id || size != s->two)
256             {
257                 WARN("Found %s, but wrong size or timestamp\n", buffer);
258                 return TRUE;
259             }
260         }
261         break;
262     case DMT_ELF:
263         if (elf_fetch_file_info(buffer, 0, &size, &checksum))
264         {
265             if (checksum != (DWORD_PTR)s->id)
266             {
267                 WARN("Found %s, but wrong checksums: %08lx %08lx\n",
268                       buffer, checksum, (DWORD_PTR)s->id);
269                 return TRUE;
270             }
271         }
272         else
273         {
274             WARN("Couldn't read %s\n", buffer);
275             return TRUE;
276         }
277         break;
278     case DMT_PDB:
279     case DMT_VIRTUAL:
280         FIXME("NIY on '%s'\n", buffer);
281         break;
282     default:
283         FIXME("What the heck??\n");
284         return TRUE;
285     }
286     /* yes, EnumDirTree/do_search and SymFindFileInPath callbacks use the opposite
287      * convention to stop/continue enumeration. sigh.
288      */
289     return !(s->cb)((char*)buffer, s->user);
290 }
291
292 /******************************************************************
293  *              SymFindFileInPath (DBGHELP.@)
294  *
295  */
296 BOOL WINAPI SymFindFileInPath(HANDLE hProcess, PCSTR searchPath, PCSTR full_path,
297                               PVOID id, DWORD two, DWORD three, DWORD flags,
298                               LPSTR buffer, PFINDFILEINPATHCALLBACK cb,
299                               PVOID user)
300 {
301     struct sffip        s;
302     struct process*     pcs = process_find_by_handle(hProcess);
303     char                tmp[MAX_PATH];
304     char*               ptr;
305     const char*         filename;
306
307     TRACE("(%p %s %s %p %08lx %08lx %08lx %p %p %p)\n",
308           hProcess, searchPath, full_path, id, two, three, flags, 
309           buffer, cb, user);
310
311     if (!pcs) return FALSE;
312     if (!searchPath) searchPath = pcs->search_path;
313
314     s.id = id;
315     s.two = two;
316     s.three = three;
317     s.flags = flags;
318     s.cb = cb;
319     s.user = user;
320
321     filename = file_name(full_path);
322     s.kind = module_get_type_by_name(filename);
323
324     /* first check full path to file */
325     if (sffip_cb(full_path, &s))
326     {
327         strcpy(buffer, full_path);
328         return TRUE;
329     }
330
331     while (searchPath)
332     {
333         ptr = strchr(searchPath, ';');
334         if (ptr)
335         {
336             memcpy(tmp, searchPath, ptr - searchPath);
337             tmp[ptr - searchPath] = 0;
338             searchPath = ptr + 1;
339         }
340         else
341         {
342             strcpy(tmp, searchPath);
343             searchPath = NULL;
344         }
345         if (do_search(filename, tmp, FALSE, sffip_cb, &s)) {
346             strcpy(buffer, tmp);
347             return TRUE;
348         }
349     }
350     return FALSE;
351 }