VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/dbg/dbgcfg.cpp@ 73895

Last change on this file since 73895 was 73334, checked in by vboxsync, 6 years ago

IPRT: Added RTHttpSetFollowRedirects (translates to CURLOPT_FOLLOWLOCATION+CURLOPT_MAXREDIRS) and make use of it RTDbgCfg and RTHttp[.exe]. This should fix the current pdb download issues as we're getting 302 redirects from the servers. Also, changed RTHttp to return VERR_HTTP_REDIRECTED when receiving 302, 303, 307 & 308 HTTP statuses.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 85.5 KB
Line 
1/* $Id: dbgcfg.cpp 73334 2018-07-23 16:52:04Z vboxsync $ */
2/** @file
3 * IPRT - Debugging Configuration.
4 */
5
6/*
7 * Copyright (C) 2013-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_DBG
32#include <iprt/dbg.h>
33#include "internal/iprt.h"
34
35#include <iprt/alloca.h>
36#include <iprt/asm.h>
37#include <iprt/assert.h>
38#include <iprt/critsect.h>
39#include <iprt/ctype.h>
40#include <iprt/dir.h>
41#include <iprt/err.h>
42#include <iprt/env.h>
43#include <iprt/file.h>
44#ifdef IPRT_WITH_HTTP
45# include <iprt/http.h>
46#endif
47#include <iprt/list.h>
48#include <iprt/log.h>
49#include <iprt/mem.h>
50#include <iprt/path.h>
51#include <iprt/process.h>
52#include <iprt/semaphore.h>
53#include <iprt/string.h>
54#include <iprt/uuid.h>
55#include "internal/magics.h"
56
57
58/*********************************************************************************************************************************
59* Structures and Typedefs *
60*********************************************************************************************************************************/
61/**
62 * String list entry.
63 */
64typedef struct RTDBGCFGSTR
65{
66 /** List entry. */
67 RTLISTNODE ListEntry;
68 /** Domain specific flags. */
69 uint16_t fFlags;
70 /** The length of the string. */
71 uint16_t cch;
72 /** The string. */
73 char sz[1];
74} RTDBGCFGSTR;
75/** Pointer to a string list entry. */
76typedef RTDBGCFGSTR *PRTDBGCFGSTR;
77
78
79/**
80 * Configuration instance.
81 */
82typedef struct RTDBGCFGINT
83{
84 /** The magic value (RTDBGCFG_MAGIC). */
85 uint32_t u32Magic;
86 /** Reference counter. */
87 uint32_t volatile cRefs;
88 /** Flags, see RTDBGCFG_FLAGS_XXX. */
89 uint64_t fFlags;
90
91 /** List of paths to search for debug files and executable images. */
92 RTLISTANCHOR PathList;
93 /** List of debug file suffixes. */
94 RTLISTANCHOR SuffixList;
95 /** List of paths to search for source files. */
96 RTLISTANCHOR SrcPathList;
97
98#ifdef RT_OS_WINDOWS
99 /** The _NT_ALT_SYMBOL_PATH and _NT_SYMBOL_PATH combined. */
100 RTLISTANCHOR NtSymbolPathList;
101 /** The _NT_EXECUTABLE_PATH. */
102 RTLISTANCHOR NtExecutablePathList;
103 /** The _NT_SOURCE_PATH. */
104 RTLISTANCHOR NtSourcePath;
105#endif
106
107 /** Log callback function. */
108 PFNRTDBGCFGLOG pfnLogCallback;
109 /** User argument to pass to the log callback. */
110 void *pvLogUser;
111
112 /** Critical section protecting the instance data. */
113 RTCRITSECTRW CritSect;
114} *PRTDBGCFGINT;
115
116/**
117 * Mnemonics map entry for a 64-bit unsigned property value.
118 */
119typedef struct RTDBGCFGU64MNEMONIC
120{
121 /** The flags to set or clear. */
122 uint64_t fFlags;
123 /** The mnemonic. */
124 const char *pszMnemonic;
125 /** The length of the mnemonic. */
126 uint8_t cchMnemonic;
127 /** If @c true, the bits in fFlags will be set, if @c false they will be
128 * cleared. */
129 bool fSet;
130} RTDBGCFGU64MNEMONIC;
131/** Pointer to a read only mnemonic map entry for a uint64_t property. */
132typedef RTDBGCFGU64MNEMONIC const *PCRTDBGCFGU64MNEMONIC;
133
134
135/** @name Open flags.
136 * @{ */
137/** The operative system mask. The values are RT_OPSYS_XXX. */
138#define RTDBGCFG_O_OPSYS_MASK UINT32_C(0x000000ff)
139/** The files may be compressed MS styled. */
140#define RTDBGCFG_O_MAYBE_COMPRESSED_MS RT_BIT_32(26)
141/** Whether to make a recursive search. */
142#define RTDBGCFG_O_RECURSIVE RT_BIT_32(27)
143/** We're looking for a separate debug file. */
144#define RTDBGCFG_O_EXT_DEBUG_FILE RT_BIT_32(28)
145/** We're looking for an executable image. */
146#define RTDBGCFG_O_EXECUTABLE_IMAGE RT_BIT_32(29)
147/** The file search should be done in an case insensitive fashion. */
148#define RTDBGCFG_O_CASE_INSENSITIVE RT_BIT_32(30)
149/** Use Windbg style symbol servers when encountered in the path. */
150#define RTDBGCFG_O_SYMSRV RT_BIT_32(31)
151/** @} */
152
153
154/*********************************************************************************************************************************
155* Defined Constants And Macros *
156*********************************************************************************************************************************/
157/** Validates a debug module handle and returns rc if not valid. */
158#define RTDBGCFG_VALID_RETURN_RC(pThis, rc) \
159 do { \
160 AssertPtrReturn((pThis), (rc)); \
161 AssertReturn((pThis)->u32Magic == RTDBGCFG_MAGIC, (rc)); \
162 AssertReturn((pThis)->cRefs > 0, (rc)); \
163 } while (0)
164
165
166/*********************************************************************************************************************************
167* Global Variables *
168*********************************************************************************************************************************/
169/** Mnemonics map for RTDBGCFGPROP_FLAGS. */
170static const RTDBGCFGU64MNEMONIC g_aDbgCfgFlags[] =
171{
172 { RTDBGCFG_FLAGS_DEFERRED, RT_STR_TUPLE("deferred"), true },
173 { RTDBGCFG_FLAGS_DEFERRED, RT_STR_TUPLE("nodeferred"), false },
174 { RTDBGCFG_FLAGS_NO_SYM_SRV, RT_STR_TUPLE("symsrv"), false },
175 { RTDBGCFG_FLAGS_NO_SYM_SRV, RT_STR_TUPLE("nosymsrv"), true },
176 { RTDBGCFG_FLAGS_NO_SYSTEM_PATHS, RT_STR_TUPLE("syspaths"), false },
177 { RTDBGCFG_FLAGS_NO_SYSTEM_PATHS, RT_STR_TUPLE("nosyspaths"), true },
178 { RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH, RT_STR_TUPLE("rec"), false },
179 { RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH, RT_STR_TUPLE("norec"), true },
180 { RTDBGCFG_FLAGS_NO_RECURSIV_SRC_SEARCH, RT_STR_TUPLE("recsrc"), false },
181 { RTDBGCFG_FLAGS_NO_RECURSIV_SRC_SEARCH, RT_STR_TUPLE("norecsrc"), true },
182 { 0, NULL, 0, false }
183};
184
185
186/** Interesting bundle suffixes. */
187static const char * const g_apszBundleSuffixes[] =
188{
189 ".kext",
190 ".app",
191 ".framework",
192 ".component",
193 ".action",
194 ".caction",
195 ".bundle",
196 ".sourcebundle",
197 ".menu",
198 ".plugin",
199 ".ppp",
200 ".monitorpanel",
201 ".scripting",
202 ".prefPane",
203 ".qlgenerator",
204 ".brailledriver",
205 ".saver",
206 ".SpeechVoice",
207 ".SpeechRecognizer",
208 ".SpeechSynthesizer",
209 ".mdimporter",
210 ".spreporter",
211 ".xpc",
212 NULL
213};
214
215/** Debug bundle suffixes. (Same as above + .dSYM) */
216static const char * const g_apszDSymBundleSuffixes[] =
217{
218 ".dSYM",
219 ".kext.dSYM",
220 ".app.dSYM",
221 ".framework.dSYM",
222 ".component.dSYM",
223 ".action.dSYM",
224 ".caction.dSYM",
225 ".bundle.dSYM",
226 ".sourcebundle.dSYM",
227 ".menu.dSYM",
228 ".plugin.dSYM",
229 ".ppp.dSYM",
230 ".monitorpanel.dSYM",
231 ".scripting.dSYM",
232 ".prefPane.dSYM",
233 ".qlgenerator.dSYM",
234 ".brailledriver.dSYM",
235 ".saver.dSYM",
236 ".SpeechVoice.dSYM",
237 ".SpeechRecognizer.dSYM",
238 ".SpeechSynthesizer.dSYM",
239 ".mdimporter.dSYM",
240 ".spreporter.dSYM",
241 ".xpc.dSYM",
242 NULL
243};
244
245
246
247/**
248 * Runtime logging, level 1.
249 *
250 * @param pThis The debug config instance data.
251 * @param pszFormat The message format string.
252 * @param ... Arguments references in the format string.
253 */
254static void rtDbgCfgLog1(PRTDBGCFGINT pThis, const char *pszFormat, ...)
255{
256 if (LogIsEnabled() || (pThis && pThis->pfnLogCallback))
257 {
258 va_list va;
259 va_start(va, pszFormat);
260 char *pszMsg = RTStrAPrintf2V(pszFormat, va);
261 va_end(va);
262
263 Log(("RTDbgCfg: %s", pszMsg));
264 if (pThis && pThis->pfnLogCallback)
265 pThis->pfnLogCallback(pThis, 1, pszMsg, pThis->pvLogUser);
266 RTStrFree(pszMsg);
267 }
268}
269
270
271/**
272 * Runtime logging, level 2.
273 *
274 * @param pThis The debug config instance data.
275 * @param pszFormat The message format string.
276 * @param ... Arguments references in the format string.
277 */
278static void rtDbgCfgLog2(PRTDBGCFGINT pThis, const char *pszFormat, ...)
279{
280 if (LogIs2Enabled() || (pThis && pThis->pfnLogCallback))
281 {
282 va_list va;
283 va_start(va, pszFormat);
284 char *pszMsg = RTStrAPrintf2V(pszFormat, va);
285 va_end(va);
286
287 Log(("RTDbgCfg: %s", pszMsg));
288 if (pThis && pThis->pfnLogCallback)
289 pThis->pfnLogCallback(pThis, 2, pszMsg, pThis->pvLogUser);
290 RTStrFree(pszMsg);
291 }
292}
293
294
295/**
296 * Checks if the file system at the given path is case insensitive or not.
297 *
298 * @returns true / false
299 * @param pszPath The path to query about.
300 */
301static int rtDbgCfgIsFsCaseInsensitive(const char *pszPath)
302{
303 RTFSPROPERTIES Props;
304 int rc = RTFsQueryProperties(pszPath, &Props);
305 if (RT_FAILURE(rc))
306 return RT_OPSYS == RT_OPSYS_DARWIN
307 || RT_OPSYS == RT_OPSYS_DOS
308 || RT_OPSYS == RT_OPSYS_OS2
309 || RT_OPSYS == RT_OPSYS_NT
310 || RT_OPSYS == RT_OPSYS_WINDOWS;
311 return !Props.fCaseSensitive;
312}
313
314
315/**
316 * Worker that does case sensitive file/dir searching.
317 *
318 * @returns true / false.
319 * @param pszPath The path buffer containing an existing directory and
320 * at @a offLastComp the name we're looking for.
321 * RTPATH_MAX in size. On success, this last component
322 * will have the correct case. On failure, the last
323 * component is stripped off.
324 * @param offLastComp The offset of the last component (for chopping it
325 * off).
326 * @param enmType What kind of thing we're looking for.
327 */
328static bool rtDbgCfgIsXxxxAndFixCaseWorker(char *pszPath, size_t offLastComp, RTDIRENTRYTYPE enmType)
329{
330 /** @todo IPRT should generalize this so we can use host specific tricks to
331 * speed it up. */
332
333 char *pszName = &pszPath[offLastComp];
334
335 /* Return straight away if the name isn't case foldable. */
336 if (!RTStrIsCaseFoldable(pszName))
337 {
338 *pszName = '\0';
339 return false;
340 }
341
342 /*
343 * Try some simple case folding games.
344 */
345 RTStrToLower(pszName);
346 if (RTFileExists(pszPath))
347 return true;
348
349 RTStrToUpper(pszName);
350 if (RTFileExists(pszPath))
351 return true;
352
353 /*
354 * Open the directory and check each entry in it.
355 */
356 char chSaved = *pszName;
357 *pszName = '\0';
358
359 RTDIR hDir;
360 int rc = RTDirOpen(&hDir, pszPath);
361 if (RT_FAILURE(rc))
362 return false;
363
364 *pszName = chSaved;
365
366 for (;;)
367 {
368 /* Read the next entry. */
369 union
370 {
371 RTDIRENTRY Entry;
372 uint8_t ab[_4K];
373 } u;
374 size_t cbBuf = sizeof(u);
375 rc = RTDirRead(hDir, &u.Entry, &cbBuf);
376 if (RT_FAILURE(rc))
377 break;
378
379 if ( !RTStrICmp(pszName, u.Entry.szName)
380 && ( u.Entry.enmType == enmType
381 || u.Entry.enmType == RTDIRENTRYTYPE_UNKNOWN
382 || u.Entry.enmType == RTDIRENTRYTYPE_SYMLINK) )
383 {
384 strcpy(pszName, u.Entry.szName);
385 if (u.Entry.enmType != enmType)
386 RTDirQueryUnknownType(pszPath, true /*fFollowSymlinks*/, &u.Entry.enmType);
387 if (u.Entry.enmType == enmType)
388 {
389 RTDirClose(hDir);
390 return true;
391 }
392 }
393 }
394
395 RTDirClose(hDir);
396 *pszName = '\0';
397
398 return false;
399}
400
401
402/**
403 * Appends @a pszSubDir to @a pszPath and check whether it exists and is a
404 * directory.
405 *
406 * If @a fCaseInsensitive is set, we will do a case insensitive search for a
407 * matching sub directory.
408 *
409 * @returns true / false
410 * @param pszPath The path buffer containing an existing
411 * directory. RTPATH_MAX in size.
412 * @param pszSubDir The sub directory to append.
413 * @param fCaseInsensitive Whether case insensitive searching is required.
414 */
415static bool rtDbgCfgIsDirAndFixCase(char *pszPath, const char *pszSubDir, bool fCaseInsensitive)
416{
417 /* Save the length of the input path so we can restore it in the case
418 insensitive branch further down. */
419 size_t const cchPath = strlen(pszPath);
420
421 /*
422 * Append the sub directory and check if we got a hit.
423 */
424 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszSubDir);
425 if (RT_FAILURE(rc))
426 return false;
427
428 if (RTDirExists(pszPath))
429 return true;
430
431 /*
432 * Do case insensitive lookup if requested.
433 */
434 if (fCaseInsensitive)
435 return rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_DIRECTORY);
436
437 pszPath[cchPath] = '\0';
438 return false;
439}
440
441
442/**
443 * Appends @a pszSubDir1 and @a pszSuffix to @a pszPath and check whether it
444 * exists and is a directory.
445 *
446 * If @a fCaseInsensitive is set, we will do a case insensitive search for a
447 * matching sub directory.
448 *
449 * @returns true / false
450 * @param pszPath The path buffer containing an existing
451 * directory. RTPATH_MAX in size.
452 * @param pszSubDir The sub directory to append.
453 * @param pszSuffix The suffix to append.
454 * @param fCaseInsensitive Whether case insensitive searching is required.
455 */
456static bool rtDbgCfgIsDirAndFixCase2(char *pszPath, const char *pszSubDir, const char *pszSuffix, bool fCaseInsensitive)
457{
458 Assert(!strpbrk(pszSuffix, ":/\\"));
459
460 /* Save the length of the input path so we can restore it in the case
461 insensitive branch further down. */
462 size_t const cchPath = strlen(pszPath);
463
464 /*
465 * Append the subdirectory and suffix, then check if we got a hit.
466 */
467 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszSubDir);
468 if (RT_SUCCESS(rc))
469 {
470 rc = RTStrCat(pszPath, RTPATH_MAX, pszSuffix);
471 if (RT_SUCCESS(rc))
472 {
473 if (RTDirExists(pszPath))
474 return true;
475
476 /*
477 * Do case insensitive lookup if requested.
478 */
479 if (fCaseInsensitive)
480 return rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_DIRECTORY);
481 }
482 }
483
484 pszPath[cchPath] = '\0';
485 return false;
486}
487
488
489/**
490 * Appends @a pszFilename to @a pszPath and check whether it exists and is a
491 * directory.
492 *
493 * If @a fCaseInsensitive is set, we will do a case insensitive search for a
494 * matching filename.
495 *
496 * @returns true / false
497 * @param pszPath The path buffer containing an existing
498 * directory. RTPATH_MAX in size.
499 * @param pszFilename The filename to append.
500 * @param pszSuffix Optional filename suffix to append.
501 * @param fCaseInsensitive Whether case insensitive searching is required.
502 * @param fMsCompressed Whether to look for the MS compressed file name
503 * variant.
504 * @param pfProbablyCompressed This is set to true if a MS compressed
505 * filename variant is returned. Optional.
506 */
507static bool rtDbgCfgIsFileAndFixCase(char *pszPath, const char *pszFilename, const char *pszSuffix, bool fCaseInsensitive,
508 bool fMsCompressed, bool *pfProbablyCompressed)
509{
510 /* Save the length of the input path so we can restore it in the case
511 insensitive branch further down. */
512 size_t cchPath = strlen(pszPath);
513 if (pfProbablyCompressed)
514 *pfProbablyCompressed = false;
515
516 /*
517 * Append the filename and optionally suffix, then check if we got a hit.
518 */
519 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
520 if (RT_FAILURE(rc))
521 return false;
522 if (pszSuffix)
523 {
524 Assert(!fMsCompressed);
525 rc = RTStrCat(pszPath, RTPATH_MAX, pszSuffix);
526 if (RT_FAILURE(rc))
527 return false;
528 }
529
530 if (RTFileExists(pszPath))
531 return true;
532
533 /*
534 * Do case insensitive file lookup if requested.
535 */
536 if (fCaseInsensitive)
537 {
538 if (rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_FILE))
539 return true;
540 }
541
542 /*
543 * Look for MS compressed file if requested.
544 */
545 if ( fMsCompressed
546 && (unsigned char)pszFilename[strlen(pszFilename) - 1] < 0x7f)
547 {
548 pszPath[cchPath] = '\0';
549 rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
550 AssertRCReturn(rc, false);
551 pszPath[strlen(pszPath) - 1] = '_';
552
553 if (pfProbablyCompressed)
554 *pfProbablyCompressed = true;
555
556 if ( RTFileExists(pszPath)
557 || ( fCaseInsensitive
558 && rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_FILE) ))
559 return true;
560
561 if (pfProbablyCompressed)
562 *pfProbablyCompressed = false;
563 }
564
565 pszPath[cchPath] = '\0';
566 return false;
567}
568
569
570static int rtDbgCfgTryOpenDir(PRTDBGCFGINT pThis, char *pszPath, PRTPATHSPLIT pSplitFn, uint32_t fFlags,
571 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
572{
573 int rcRet = VWRN_NOT_FOUND;
574 int rc2;
575
576 /* If the directory doesn't exist, just quit immediately.
577 Note! Our case insensitivity doesn't extend to the search dirs themselfs,
578 only to the bits under neath them. */
579 if (!RTDirExists(pszPath))
580 {
581 rtDbgCfgLog2(pThis, "Dir does not exist: '%s'\n", pszPath);
582 return rcRet;
583 }
584
585 /* Figure out whether we have to do a case sensitive search or not.
586 Note! As a simplification, we don't ask for case settings in each
587 directory under the user specified path, we assume the file
588 systems that mounted there have compatible settings. Faster
589 that way. */
590 bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
591 && !rtDbgCfgIsFsCaseInsensitive(pszPath);
592
593 size_t const cchPath = strlen(pszPath);
594
595 /*
596 * Look for the file with less and less of the original path given.
597 */
598 for (unsigned i = RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps); i < pSplitFn->cComps; i++)
599 {
600 pszPath[cchPath] = '\0';
601
602 rc2 = VINF_SUCCESS;
603 for (unsigned j = i; j < pSplitFn->cComps - 1U && RT_SUCCESS(rc2); j++)
604 if (!rtDbgCfgIsDirAndFixCase(pszPath, pSplitFn->apszComps[i], fCaseInsensitive))
605 rc2 = VERR_FILE_NOT_FOUND;
606
607 if (RT_SUCCESS(rc2))
608 {
609 if (rtDbgCfgIsFileAndFixCase(pszPath, pSplitFn->apszComps[pSplitFn->cComps - 1], NULL /*pszSuffix*/,
610 fCaseInsensitive, false, NULL))
611 {
612 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
613 rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
614 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
615 {
616 if (rc2 == VINF_CALLBACK_RETURN)
617 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
618 else
619 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
620 return rc2;
621 }
622 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
623 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
624 rcRet = rc2;
625 }
626 }
627 }
628
629 /*
630 * Do a recursive search if requested.
631 */
632 if ( (fFlags & RTDBGCFG_O_RECURSIVE)
633 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH) )
634 {
635 /** @todo Recursive searching will be done later. */
636 }
637
638 return rcRet;
639}
640
641static int rtDbgCfgUnpackMsCacheFile(PRTDBGCFGINT pThis, char *pszPath, const char *pszFilename)
642{
643 rtDbgCfgLog2(pThis, "Unpacking '%s'...\n", pszPath);
644
645 /*
646 * Duplicate the source file path, just for simplicity and restore the
647 * final character in the orignal. We cheerfully ignorining any
648 * possibility of multibyte UTF-8 sequences just like the caller did when
649 * setting it to '_'.
650 */
651 char *pszSrcArchive = RTStrDup(pszPath);
652 if (!pszSrcArchive)
653 return VERR_NO_STR_MEMORY;
654
655 pszPath[strlen(pszPath) - 1] = RT_C_TO_LOWER(pszFilename[strlen(pszFilename) - 1]);
656
657
658 /*
659 * Figuring out the argument list for the platform specific unpack util.
660 */
661#ifdef RT_OS_WINDOWS
662 RTPathChangeToDosSlashes(pszSrcArchive, false /*fForce*/);
663 RTPathChangeToDosSlashes(pszPath, false /*fForce*/);
664 const char *papszArgs[] =
665 {
666 "expand.exe",
667 pszSrcArchive,
668 pszPath,
669 NULL
670 };
671
672#else
673 char szExtractDir[RTPATH_MAX];
674 strcpy(szExtractDir, pszPath);
675 RTPathStripFilename(szExtractDir);
676
677 const char *papszArgs[] =
678 {
679 "cabextract",
680 "-L", /* Lower case extracted files. */
681 "-d", szExtractDir, /* Extraction path */
682 pszSrcArchive,
683 NULL
684 };
685#endif
686
687 /*
688 * Do the unpacking.
689 */
690 RTPROCESS hChild;
691 int rc = RTProcCreate(papszArgs[0], papszArgs, RTENV_DEFAULT,
692#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
693 RTPROC_FLAGS_NO_WINDOW | RTPROC_FLAGS_HIDDEN | RTPROC_FLAGS_SEARCH_PATH,
694#else
695 RTPROC_FLAGS_SEARCH_PATH,
696#endif
697 &hChild);
698 if (RT_SUCCESS(rc))
699 {
700 RTPROCSTATUS ProcStatus;
701 rc = RTProcWait(hChild, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus);
702 if (RT_SUCCESS(rc))
703 {
704 if ( ProcStatus.enmReason == RTPROCEXITREASON_NORMAL
705 && ProcStatus.iStatus == 0)
706 {
707 if (RTPathExists(pszPath))
708 {
709 rtDbgCfgLog1(pThis, "Successfully unpacked '%s' to '%s'.\n", pszSrcArchive, pszPath);
710 rc = VINF_SUCCESS;
711 }
712 else
713 {
714 rtDbgCfgLog1(pThis, "Successfully ran unpacker on '%s', but '%s' is missing!\n", pszSrcArchive, pszPath);
715 rc = VERR_ZIP_ERROR;
716 }
717 }
718 else
719 {
720 rtDbgCfgLog2(pThis, "Unpacking '%s' failed: iStatus=%d enmReason=%d\n",
721 pszSrcArchive, ProcStatus.iStatus, ProcStatus.enmReason);
722 rc = VERR_ZIP_CORRUPTED;
723 }
724 }
725 else
726 rtDbgCfgLog1(pThis, "Error waiting for process: %Rrc\n", rc);
727
728 }
729 else
730 rtDbgCfgLog1(pThis, "Error starting unpack process '%s': %Rrc\n", papszArgs[0], rc);
731
732 return rc;
733}
734
735
736static int rtDbgCfgTryDownloadAndOpen(PRTDBGCFGINT pThis, const char *pszServer, char *pszPath,
737 const char *pszCacheSubDir, const char *pszUuidMappingSubDir,
738 PRTPATHSPLIT pSplitFn, const char *pszCacheSuffix, uint32_t fFlags,
739 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
740{
741 RT_NOREF_PV(pszUuidMappingSubDir); /** @todo do we bother trying pszUuidMappingSubDir? */
742 RT_NOREF_PV(pszCacheSuffix); /** @todo do we bother trying pszUuidMappingSubDir? */
743 RT_NOREF_PV(fFlags);
744
745 if (pThis->fFlags & RTDBGCFG_FLAGS_NO_SYM_SRV)
746 return VWRN_NOT_FOUND;
747 if (!pszCacheSubDir || !*pszCacheSubDir)
748 return VWRN_NOT_FOUND;
749
750 /*
751 * Create the path.
752 */
753 size_t cchTmp = strlen(pszPath);
754
755 int rc = RTDirCreateFullPath(pszPath, 0766);
756 if (!RTDirExists(pszPath))
757 {
758 Log(("Error creating cache dir '%s': %Rrc\n", pszPath, rc));
759 return rc;
760 }
761
762 const char *pszFilename = pSplitFn->apszComps[pSplitFn->cComps - 1];
763 rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
764 if (RT_FAILURE(rc))
765 return rc;
766 RTStrToLower(&pszPath[cchTmp]);
767 if (!RTDirExists(pszPath))
768 {
769 rc = RTDirCreate(pszPath, 0766, 0);
770 if (RT_FAILURE(rc))
771 {
772 Log(("RTDirCreate(%s) -> %Rrc\n", pszPath, rc));
773 }
774 }
775
776 rc = RTPathAppend(pszPath, RTPATH_MAX, pszCacheSubDir);
777 if (RT_FAILURE(rc))
778 return rc;
779 if (!RTDirExists(pszPath))
780 {
781 rc = RTDirCreate(pszPath, 0766, 0);
782 if (RT_FAILURE(rc))
783 {
784 Log(("RTDirCreate(%s) -> %Rrc\n", pszPath, rc));
785 }
786 }
787
788 /* Prepare the destination file name while we're here. */
789 cchTmp = strlen(pszPath);
790 RTStrToLower(&pszPath[cchTmp]);
791 rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
792 if (RT_FAILURE(rc))
793 return rc;
794
795 /*
796 * Download/copy the file.
797 */
798 char szUrl[_2K];
799 /* Download URL? */
800 if ( RTStrIStartsWith(pszServer, "http://")
801 || RTStrIStartsWith(pszServer, "https://")
802 || RTStrIStartsWith(pszServer, "ftp://") )
803 {
804#ifdef IPRT_WITH_HTTP
805 RTHTTP hHttp;
806 rc = RTHttpCreate(&hHttp);
807 if (RT_SUCCESS(rc))
808 {
809 RTHttpUseSystemProxySettings(hHttp);
810 RTHttpSetFollowRedirects(hHttp, 8);
811
812 static const char * const s_apszHeaders[] =
813 {
814 "User-Agent: Microsoft-Symbol-Server/6.6.0999.9",
815 "Pragma: no-cache",
816 };
817
818 rc = RTHttpSetHeaders(hHttp, RT_ELEMENTS(s_apszHeaders), s_apszHeaders);
819 if (RT_SUCCESS(rc))
820 {
821 RTStrPrintf(szUrl, sizeof(szUrl), "%s/%s/%s/%s", pszServer, pszFilename, pszCacheSubDir, pszFilename);
822
823 /** @todo Use some temporary file name and rename it after the operation
824 * since not all systems support read-deny file sharing
825 * settings. */
826 rtDbgCfgLog2(pThis, "Downloading '%s' to '%s'...\n", szUrl, pszPath);
827 rc = RTHttpGetFile(hHttp, szUrl, pszPath);
828 if (RT_FAILURE(rc))
829 {
830 RTFileDelete(pszPath);
831 rtDbgCfgLog1(pThis, "%Rrc on URL '%s'\n", rc, szUrl);
832 }
833 if (rc == VERR_HTTP_NOT_FOUND)
834 {
835 /* Try the compressed version of the file. */
836 pszPath[strlen(pszPath) - 1] = '_';
837 szUrl[strlen(szUrl) - 1] = '_';
838 rtDbgCfgLog2(pThis, "Downloading '%s' to '%s'...\n", szUrl, pszPath);
839 rc = RTHttpGetFile(hHttp, szUrl, pszPath);
840 if (RT_SUCCESS(rc))
841 rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename);
842 else
843 {
844 rtDbgCfgLog1(pThis, "%Rrc on URL '%s'\n", rc, pszPath);
845 RTFileDelete(pszPath);
846 }
847 }
848 }
849
850 RTHttpDestroy(hHttp);
851 }
852#else
853 rc = VWRN_NOT_FOUND;
854#endif
855 }
856 /* No download, assume dir on server share. */
857 else
858 {
859 if (RTStrIStartsWith(pszServer, "file:///"))
860 pszServer += 4 + 1 + 3 - 1;
861
862 /* Compose the path to the uncompressed file on the server. */
863 rc = RTPathJoin(szUrl, sizeof(szUrl), pszServer, pszFilename);
864 if (RT_SUCCESS(rc))
865 rc = RTPathAppend(szUrl, sizeof(szUrl), pszCacheSubDir);
866 if (RT_SUCCESS(rc))
867 rc = RTPathAppend(szUrl, sizeof(szUrl), pszFilename);
868 if (RT_SUCCESS(rc))
869 {
870 rtDbgCfgLog2(pThis, "Copying '%s' to '%s'...\n", szUrl, pszPath);
871 rc = RTFileCopy(szUrl, pszPath);
872 if (RT_FAILURE(rc))
873 {
874 RTFileDelete(pszPath);
875 rtDbgCfgLog1(pThis, "%Rrc on '%s'\n", rc, szUrl);
876
877 /* Try the compressed version. */
878 pszPath[strlen(pszPath) - 1] = '_';
879 szUrl[strlen(szUrl) - 1] = '_';
880 rtDbgCfgLog2(pThis, "Copying '%s' to '%s'...\n", szUrl, pszPath);
881 rc = RTFileCopy(szUrl, pszPath);
882 if (RT_SUCCESS(rc))
883 rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename);
884 else
885 {
886 rtDbgCfgLog1(pThis, "%Rrc on '%s'\n", rc, pszPath);
887 RTFileDelete(pszPath);
888 }
889 }
890 }
891 }
892 if (RT_SUCCESS(rc))
893 {
894 /*
895 * Succeeded in downloading it. Add UUID mapping?
896 */
897 if (pszUuidMappingSubDir)
898 {
899 /** @todo UUID mapping when downloading. */
900 }
901
902 /*
903 * Give the file a try.
904 */
905 Assert(RTFileExists(pszPath));
906 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
907 rc = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
908 if (rc == VINF_CALLBACK_RETURN)
909 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
910 else if (rc == VERR_CALLBACK_RETURN)
911 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
912 else
913 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc, pszPath);
914 }
915
916 return rc;
917}
918
919
920static int rtDbgCfgCopyFileToCache(PRTDBGCFGINT pThis, char const *pszSrc, const char *pchCache, size_t cchCache,
921 const char *pszCacheSubDir, const char *pszUuidMappingSubDir, PRTPATHSPLIT pSplitFn)
922{
923 RT_NOREF_PV(pThis); RT_NOREF_PV(pszSrc); RT_NOREF_PV(pchCache); RT_NOREF_PV(cchCache);
924 RT_NOREF_PV(pszUuidMappingSubDir); RT_NOREF_PV(pSplitFn);
925
926 if (!pszCacheSubDir || !*pszCacheSubDir)
927 return VINF_SUCCESS;
928
929 /** @todo copy to cache */
930 return VINF_SUCCESS;
931}
932
933
934static int rtDbgCfgTryOpenCache(PRTDBGCFGINT pThis, char *pszPath, size_t cchCachePath,
935 const char *pszCacheSubDir, const char *pszUuidMappingSubDir,
936 PCRTPATHSPLIT pSplitFn, const char *pszCacheSuffix, uint32_t fFlags,
937 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
938{
939 Assert(pszPath[cchCachePath] == '\0');
940
941 /*
942 * If the cache doesn't exist, fail right away.
943 */
944 if (!pszCacheSubDir || !*pszCacheSubDir)
945 return VWRN_NOT_FOUND;
946 if (!RTDirExists(pszPath))
947 {
948 rtDbgCfgLog2(pThis, "Cache does not exist: '%s'\n", pszPath);
949 return VWRN_NOT_FOUND;
950 }
951
952 /*
953 * If we got a UUID mapping option, try it first as we can hopefully
954 * dispense with case folding.
955 */
956 if (pszUuidMappingSubDir)
957 {
958 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszUuidMappingSubDir);
959 if ( RT_SUCCESS(rc)
960 && RTFileExists(pszPath))
961 {
962 /* Try resolve the path before presenting it to the client, a
963 12 digit filename is of little worth. */
964 char szBackup[RTPATH_MAX];
965 strcpy(szBackup, pszPath);
966 rc = RTPathAbs(szBackup, pszPath, RTPATH_MAX);
967 if (RT_FAILURE(rc))
968 strcpy(pszPath, szBackup);
969
970 /* Do the callback thing. */
971 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
972 int rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
973 if (rc2 == VINF_CALLBACK_RETURN)
974 rtDbgCfgLog1(pThis, "Found '%s' via uuid mapping.\n", pszPath);
975 else if (rc2 == VERR_CALLBACK_RETURN)
976 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
977 else
978 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
979 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
980 return rc2;
981
982 /* Failed, restore the cache path. */
983 memcpy(pszPath, szBackup, cchCachePath);
984 }
985 pszPath[cchCachePath] = '\0';
986 }
987
988 /*
989 * Carefully construct the cache path with case insensitivity in mind.
990 */
991 bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
992 && !rtDbgCfgIsFsCaseInsensitive(pszPath);
993 const char *pszFilename = pSplitFn->apszComps[pSplitFn->cComps - 1];
994
995 if (!rtDbgCfgIsDirAndFixCase(pszPath, pszFilename, fCaseInsensitive))
996 return VWRN_NOT_FOUND;
997
998 if (!rtDbgCfgIsDirAndFixCase(pszPath, pszCacheSubDir, fCaseInsensitive))
999 return VWRN_NOT_FOUND;
1000
1001 bool fProbablyCompressed = false;
1002 if (!rtDbgCfgIsFileAndFixCase(pszPath, pszFilename, pszCacheSuffix, fCaseInsensitive,
1003 RT_BOOL(fFlags & RTDBGCFG_O_MAYBE_COMPRESSED_MS), &fProbablyCompressed))
1004 return VWRN_NOT_FOUND;
1005 if (fProbablyCompressed)
1006 {
1007 int rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename);
1008 if (RT_FAILURE(rc))
1009 return VWRN_NOT_FOUND;
1010 }
1011
1012 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
1013 int rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
1014 if (rc2 == VINF_CALLBACK_RETURN)
1015 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
1016 else if (rc2 == VERR_CALLBACK_RETURN)
1017 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
1018 else
1019 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
1020 return rc2;
1021}
1022
1023
1024static int rtDbgCfgTryOpenList(PRTDBGCFGINT pThis, PRTLISTANCHOR pList, PRTPATHSPLIT pSplitFn, const char *pszCacheSubDir,
1025 const char *pszUuidMappingSubDir, uint32_t fFlags, char *pszPath,
1026 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1027{
1028 int rcRet = VWRN_NOT_FOUND;
1029 int rc2 = VINF_SUCCESS;
1030
1031 const char *pchCache = NULL;
1032 size_t cchCache = 0;
1033 int rcCache = VWRN_NOT_FOUND;
1034
1035 PRTDBGCFGSTR pCur;
1036 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
1037 {
1038 size_t cchDir = pCur->cch;
1039 const char *pszDir = pCur->sz;
1040 rtDbgCfgLog2(pThis, "Path list entry: '%s'\n", pszDir);
1041
1042 /* This is very simplistic, but we have a unreasonably large path
1043 buffer, so it'll work just fine and simplify things greatly below. */
1044 if (cchDir >= RTPATH_MAX - 8U)
1045 {
1046 if (RT_SUCCESS_NP(rcRet))
1047 rcRet = VERR_FILENAME_TOO_LONG;
1048 continue;
1049 }
1050
1051 /*
1052 * Process the path according to it's type.
1053 */
1054 if (!RTStrNICmp(pszDir, RT_STR_TUPLE("srv*")))
1055 {
1056 /*
1057 * Symbol server.
1058 */
1059 pszDir += sizeof("srv*") - 1;
1060 cchDir -= sizeof("srv*") - 1;
1061 bool fSearchCache = false;
1062 const char *pszServer = (const char *)memchr(pszDir, '*', cchDir);
1063 if (!pszServer)
1064 pszServer = pszDir;
1065 else if (pszServer == pszDir)
1066 continue;
1067 else
1068 {
1069 fSearchCache = true;
1070 pchCache = pszDir;
1071 cchCache = pszServer - pszDir;
1072 pszServer++;
1073 }
1074
1075 /* We don't have any default cache directory, so skip if the cache is missing. */
1076 if (cchCache == 0)
1077 continue;
1078
1079 /* Search the cache first (if we haven't already done so). */
1080 if (fSearchCache)
1081 {
1082 memcpy(pszPath, pchCache, cchCache);
1083 pszPath[cchCache] = '\0';
1084 RTPathChangeToUnixSlashes(pszPath, false);
1085
1086 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
1087 pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2);
1088 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1089 return rc2;
1090 }
1091
1092 /* Try downloading the file. */
1093 if (rcCache == VWRN_NOT_FOUND)
1094 {
1095 memcpy(pszPath, pchCache, cchCache);
1096 pszPath[cchCache] = '\0';
1097 RTPathChangeToUnixSlashes(pszPath, false);
1098
1099 rc2 = rtDbgCfgTryDownloadAndOpen(pThis, pszServer, pszPath, pszCacheSubDir, pszUuidMappingSubDir,
1100 pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2);
1101 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1102 return rc2;
1103 }
1104 }
1105 else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("cache*")))
1106 {
1107 /*
1108 * Cache directory.
1109 */
1110 pszDir += sizeof("cache*") - 1;
1111 cchDir -= sizeof("cache*") - 1;
1112 if (!cchDir)
1113 continue;
1114 pchCache = pszDir;
1115 cchCache = cchDir;
1116
1117 memcpy(pszPath, pchCache, cchCache);
1118 pszPath[cchCache] = '\0';
1119 RTPathChangeToUnixSlashes(pszPath, false);
1120
1121 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
1122 pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2);
1123 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1124 return rc2;
1125 }
1126 else
1127 {
1128 /*
1129 * Normal directory. Check for our own 'rec*' and 'norec*' prefix
1130 * flags governing recursive searching.
1131 */
1132 uint32_t fFlagsDir = fFlags;
1133 if (!RTStrNICmp(pszDir, RT_STR_TUPLE("rec*")))
1134 {
1135 pszDir += sizeof("rec*") - 1;
1136 cchDir -= sizeof("rec*") - 1;
1137 fFlagsDir |= RTDBGCFG_O_RECURSIVE;
1138 }
1139 else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("norec*")))
1140 {
1141 pszDir += sizeof("norec*") - 1;
1142 cchDir -= sizeof("norec*") - 1;
1143 fFlagsDir &= ~RTDBGCFG_O_RECURSIVE;
1144 }
1145
1146 /* Copy the path into the buffer and do the searching. */
1147 memcpy(pszPath, pszDir, cchDir);
1148 pszPath[cchDir] = '\0';
1149 RTPathChangeToUnixSlashes(pszPath, false);
1150
1151 rc2 = rtDbgCfgTryOpenDir(pThis, pszPath, pSplitFn, fFlagsDir, pfnCallback, pvUser1, pvUser2);
1152 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1153 {
1154 if ( rc2 == VINF_CALLBACK_RETURN
1155 && cchCache > 0)
1156 rtDbgCfgCopyFileToCache(pThis, pszPath, pchCache, cchCache,
1157 pszCacheSubDir, pszUuidMappingSubDir, pSplitFn);
1158 return rc2;
1159 }
1160 }
1161
1162 /* Propagate errors. */
1163 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1164 rcRet = rc2;
1165 }
1166
1167 return rcRet;
1168}
1169
1170
1171/**
1172 * Common worker routine for Image and debug info opening.
1173 *
1174 * This will not search using for suffixes.
1175 *
1176 * @returns IPRT status code.
1177 * @param hDbgCfg The debugging configuration handle.
1178 * NIL_RTDBGCFG is accepted, but the result is
1179 * that no paths will be searched beyond the
1180 * given and the current directory.
1181 * @param pszFilename The filename to search for. This may or may
1182 * not include a full or partial path.
1183 * @param pszCacheSubDir The cache subdirectory to look in.
1184 * @param pszUuidMappingSubDir UUID mapping subdirectory to check, NULL if
1185 * no mapping wanted.
1186 * @param fFlags Flags and hints.
1187 * @param pfnCallback The open callback routine.
1188 * @param pvUser1 User parameter 1.
1189 * @param pvUser2 User parameter 2.
1190 */
1191static int rtDbgCfgOpenWithSubDir(RTDBGCFG hDbgCfg, const char *pszFilename, const char *pszCacheSubDir,
1192 const char *pszUuidMappingSubDir, uint32_t fFlags,
1193 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1194{
1195 int rcRet = VINF_SUCCESS;
1196 int rc2;
1197
1198 /*
1199 * Do a little validating first.
1200 */
1201 PRTDBGCFGINT pThis = hDbgCfg;
1202 if (pThis != NIL_RTDBGCFG)
1203 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1204 else
1205 pThis = NULL;
1206 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
1207 AssertPtrReturn(pszCacheSubDir, VERR_INVALID_POINTER);
1208 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
1209
1210 /*
1211 * Do some guessing as to the way we should parse the filename and whether
1212 * it's case exact or not.
1213 */
1214 bool fDosPath = strchr(pszFilename, ':') != NULL
1215 || strchr(pszFilename, '\\') != NULL
1216 || RT_OPSYS_USES_DOS_PATHS(fFlags & RTDBGCFG_O_OPSYS_MASK)
1217 || (fFlags & RTDBGCFG_O_CASE_INSENSITIVE);
1218 if (fDosPath)
1219 fFlags |= RTDBGCFG_O_CASE_INSENSITIVE;
1220
1221 rtDbgCfgLog2(pThis, "Looking for '%s' w/ cache subdir '%s' and %#x flags...\n", pszFilename, pszCacheSubDir, fFlags);
1222
1223 PRTPATHSPLIT pSplitFn;
1224 rc2 = RTPathSplitA(pszFilename, &pSplitFn, fDosPath ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
1225 if (RT_FAILURE(rc2))
1226 return rc2;
1227 AssertReturnStmt(pSplitFn->fProps & RTPATH_PROP_FILENAME, RTPathSplitFree(pSplitFn), VERR_IS_A_DIRECTORY);
1228
1229 /*
1230 * Try the stored file name first if it has a kind of absolute path.
1231 */
1232 char szPath[RTPATH_MAX];
1233 if (RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps))
1234 {
1235 rc2 = RTPathSplitReassemble(pSplitFn, RTPATH_STR_F_STYLE_HOST, szPath, sizeof(szPath));
1236 if (RT_SUCCESS(rc2) && RTFileExists(szPath))
1237 {
1238 RTPathChangeToUnixSlashes(szPath, false);
1239 rtDbgCfgLog1(pThis, "Trying '%s'...\n", szPath);
1240 rc2 = pfnCallback(pThis, szPath, pvUser1, pvUser2);
1241 if (rc2 == VINF_CALLBACK_RETURN)
1242 rtDbgCfgLog1(pThis, "Found '%s'.\n", szPath);
1243 else if (rc2 == VERR_CALLBACK_RETURN)
1244 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", szPath);
1245 else
1246 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, szPath);
1247 }
1248 }
1249 if ( rc2 != VINF_CALLBACK_RETURN
1250 && rc2 != VERR_CALLBACK_RETURN)
1251 {
1252 /*
1253 * Try the current directory (will take cover relative paths
1254 * skipped above).
1255 */
1256 rc2 = RTPathGetCurrent(szPath, sizeof(szPath));
1257 if (RT_FAILURE(rc2))
1258 strcpy(szPath, ".");
1259 RTPathChangeToUnixSlashes(szPath, false);
1260
1261 rc2 = rtDbgCfgTryOpenDir(pThis, szPath, pSplitFn, fFlags, pfnCallback, pvUser1, pvUser2);
1262 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1263 rcRet = rc2;
1264
1265 if ( rc2 != VINF_CALLBACK_RETURN
1266 && rc2 != VERR_CALLBACK_RETURN
1267 && pThis)
1268 {
1269 rc2 = RTCritSectRwEnterShared(&pThis->CritSect);
1270 if (RT_SUCCESS(rc2))
1271 {
1272 /*
1273 * Run the applicable lists.
1274 */
1275 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->PathList, pSplitFn, pszCacheSubDir,
1276 pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2);
1277 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1278 rcRet = rc2;
1279
1280#ifdef RT_OS_WINDOWS
1281 if ( rc2 != VINF_CALLBACK_RETURN
1282 && rc2 != VERR_CALLBACK_RETURN
1283 && (fFlags & RTDBGCFG_O_EXECUTABLE_IMAGE)
1284 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) )
1285 {
1286 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtExecutablePathList, pSplitFn, pszCacheSubDir,
1287 pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2);
1288 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1289 rcRet = rc2;
1290 }
1291
1292 if ( rc2 != VINF_CALLBACK_RETURN
1293 && rc2 != VERR_CALLBACK_RETURN
1294 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) )
1295 {
1296 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtSymbolPathList, pSplitFn, pszCacheSubDir,
1297 pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2);
1298 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1299 rcRet = rc2;
1300 }
1301#endif
1302 RTCritSectRwLeaveShared(&pThis->CritSect);
1303 }
1304 else if (RT_SUCCESS(rcRet))
1305 rcRet = rc2;
1306 }
1307 }
1308
1309 RTPathSplitFree(pSplitFn);
1310 if ( rc2 == VINF_CALLBACK_RETURN
1311 || rc2 == VERR_CALLBACK_RETURN)
1312 rcRet = rc2;
1313 else if (RT_SUCCESS(rcRet))
1314 rcRet = VERR_NOT_FOUND;
1315 return rcRet;
1316}
1317
1318
1319RTDECL(int) RTDbgCfgOpenPeImage(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp,
1320 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1321{
1322 char szSubDir[32];
1323 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage);
1324 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1325 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1326 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXECUTABLE_IMAGE,
1327 pfnCallback, pvUser1, pvUser2);
1328}
1329
1330
1331RTDECL(int) RTDbgCfgOpenPdb70(RTDBGCFG hDbgCfg, const char *pszFilename, PCRTUUID pUuid, uint32_t uAge,
1332 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1333{
1334 char szSubDir[64];
1335 if (!pUuid)
1336 szSubDir[0] = '\0';
1337 else
1338 {
1339 /* Stringify the UUID and remove the dashes. */
1340 int rc2 = RTUuidToStr(pUuid, szSubDir, sizeof(szSubDir));
1341 AssertRCReturn(rc2, rc2);
1342
1343 char *pszSrc = szSubDir;
1344 char *pszDst = szSubDir;
1345 char ch;
1346 while ((ch = *pszSrc++))
1347 if (ch != '-')
1348 *pszDst++ = RT_C_TO_UPPER(ch);
1349
1350 RTStrPrintf(pszDst, &szSubDir[sizeof(szSubDir)] - pszDst, "%X", uAge);
1351 }
1352
1353 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1354 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1355 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE,
1356 pfnCallback, pvUser1, pvUser2);
1357}
1358
1359
1360RTDECL(int) RTDbgCfgOpenPdb20(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp, uint32_t uAge,
1361 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1362{
1363 RT_NOREF_PV(cbImage);
1364 /** @todo test this! */
1365 char szSubDir[32];
1366 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, uAge);
1367 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1368 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1369 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE,
1370 pfnCallback, pvUser1, pvUser2);
1371}
1372
1373
1374RTDECL(int) RTDbgCfgOpenDbg(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp,
1375 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1376{
1377 char szSubDir[32];
1378 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage);
1379 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1380 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1381 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE,
1382 pfnCallback, pvUser1, pvUser2);
1383}
1384
1385
1386RTDECL(int) RTDbgCfgOpenDwo(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t uCrc32,
1387 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1388{
1389 char szSubDir[32];
1390 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08x", uCrc32);
1391 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1392 RT_OPSYS_UNKNOWN | RTDBGCFG_O_EXT_DEBUG_FILE,
1393 pfnCallback, pvUser1, pvUser2);
1394}
1395
1396
1397
1398/*
1399 *
1400 * D a r w i n . d S Y M b u n d l e s
1401 * D a r w i n . d S Y M b u n d l e s
1402 * D a r w i n . d S Y M b u n d l e s
1403 *
1404 */
1405
1406/**
1407 * Very similar to rtDbgCfgTryOpenDir.
1408 */
1409static int rtDbgCfgTryOpenDsymBundleInDir(PRTDBGCFGINT pThis, char *pszPath, PRTPATHSPLIT pSplitFn,
1410 const char * const *papszSuffixes, uint32_t fFlags,
1411 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1412{
1413 int rcRet = VWRN_NOT_FOUND;
1414 int rc2;
1415
1416 /* If the directory doesn't exist, just quit immediately.
1417 Note! Our case insensitivity doesn't extend to the search dirs themselfs,
1418 only to the bits under neath them. */
1419 if (!RTDirExists(pszPath))
1420 {
1421 rtDbgCfgLog2(pThis, "Dir does not exist: '%s'\n", pszPath);
1422 return rcRet;
1423 }
1424
1425 /* Figure out whether we have to do a case sensitive search or not.
1426 Note! As a simplification, we don't ask for case settings in each
1427 directory under the user specified path, we assume the file
1428 systems that mounted there have compatible settings. Faster
1429 that way. */
1430 bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
1431 && !rtDbgCfgIsFsCaseInsensitive(pszPath);
1432
1433 size_t const cchPath = strlen(pszPath);
1434
1435 /*
1436 * Look for the file with less and less of the original path given.
1437 * Also try out typical bundle extension variations.
1438 */
1439 const char *pszName = pSplitFn->apszComps[pSplitFn->cComps - 1];
1440 for (unsigned i = RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps); i < pSplitFn->cComps; i++)
1441 {
1442 pszPath[cchPath] = '\0';
1443
1444 rc2 = VINF_SUCCESS;
1445 for (unsigned j = i; j < pSplitFn->cComps - 1U && RT_SUCCESS(rc2); j++)
1446 if (!rtDbgCfgIsDirAndFixCase(pszPath, pSplitFn->apszComps[i], fCaseInsensitive))
1447 rc2 = VERR_FILE_NOT_FOUND;
1448 if (RT_SUCCESS(rc2))
1449 {
1450 for (uint32_t iSuffix = 0; papszSuffixes[iSuffix]; iSuffix++)
1451 {
1452 if ( !rtDbgCfgIsDirAndFixCase2(pszPath, pszName, papszSuffixes[iSuffix], fCaseInsensitive)
1453 && !rtDbgCfgIsDirAndFixCase(pszPath, "Contents", fCaseInsensitive)
1454 && !rtDbgCfgIsDirAndFixCase(pszPath, "Resources", fCaseInsensitive)
1455 && !rtDbgCfgIsDirAndFixCase(pszPath, "DWARF", fCaseInsensitive))
1456 {
1457 if (rtDbgCfgIsFileAndFixCase(pszPath, pszName, NULL /*pszSuffix*/, fCaseInsensitive, false, NULL))
1458 {
1459 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
1460 rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
1461 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1462 {
1463 if (rc2 == VINF_CALLBACK_RETURN)
1464 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
1465 else
1466 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
1467 return rc2;
1468 }
1469 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
1470 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1471 rcRet = rc2;
1472 }
1473 }
1474 }
1475 }
1476 rc2 = VERR_FILE_NOT_FOUND;
1477 }
1478
1479 /*
1480 * Do a recursive search if requested.
1481 */
1482 if ( (fFlags & RTDBGCFG_O_RECURSIVE)
1483 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH) )
1484 {
1485 /** @todo Recursive searching will be done later. */
1486 }
1487
1488 return rcRet;
1489}
1490
1491
1492/**
1493 * Very similar to rtDbgCfgTryOpenList.
1494 */
1495static int rtDbgCfgTryOpenBundleInList(PRTDBGCFGINT pThis, PRTLISTANCHOR pList, PRTPATHSPLIT pSplitFn,
1496 const char * const *papszSuffixes, const char *pszCacheSubDir,
1497 const char *pszCacheSuffix, const char *pszUuidMappingSubDir,
1498 uint32_t fFlags, char *pszPath,
1499 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1500{
1501 int rcRet = VWRN_NOT_FOUND;
1502 int rc2;
1503
1504 const char *pchCache = NULL;
1505 size_t cchCache = 0;
1506 int rcCache = VWRN_NOT_FOUND;
1507
1508 PRTDBGCFGSTR pCur;
1509 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
1510 {
1511 size_t cchDir = pCur->cch;
1512 const char *pszDir = pCur->sz;
1513 rtDbgCfgLog2(pThis, "Path list entry: '%s'\n", pszDir);
1514
1515 /* This is very simplistic, but we have a unreasonably large path
1516 buffer, so it'll work just fine and simplify things greatly below. */
1517 if (cchDir >= RTPATH_MAX - 8U)
1518 {
1519 if (RT_SUCCESS_NP(rcRet))
1520 rcRet = VERR_FILENAME_TOO_LONG;
1521 continue;
1522 }
1523
1524 /*
1525 * Process the path according to it's type.
1526 */
1527 rc2 = VINF_SUCCESS;
1528 if (!RTStrNICmp(pszDir, RT_STR_TUPLE("srv*")))
1529 {
1530 /*
1531 * Symbol server.
1532 */
1533 pszDir += sizeof("srv*") - 1;
1534 cchDir -= sizeof("srv*") - 1;
1535 bool fSearchCache = false;
1536 const char *pszServer = (const char *)memchr(pszDir, '*', cchDir);
1537 if (!pszServer)
1538 pszServer = pszDir;
1539 else if (pszServer == pszDir)
1540 continue;
1541 else
1542 {
1543 fSearchCache = true;
1544 pchCache = pszDir;
1545 cchCache = pszServer - pszDir;
1546 pszServer++;
1547 }
1548
1549 /* We don't have any default cache directory, so skip if the cache is missing. */
1550 if (cchCache == 0)
1551 continue;
1552
1553 /* Search the cache first (if we haven't already done so). */
1554 if (fSearchCache)
1555 {
1556 memcpy(pszPath, pchCache, cchCache);
1557 pszPath[cchCache] = '\0';
1558 RTPathChangeToUnixSlashes(pszPath, false);
1559
1560 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
1561 pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2);
1562 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1563 return rc2;
1564 }
1565
1566 /* Try downloading the file. */
1567 if (rcCache == VWRN_NOT_FOUND)
1568 {
1569 memcpy(pszPath, pchCache, cchCache);
1570 pszPath[cchCache] = '\0';
1571 RTPathChangeToUnixSlashes(pszPath, false);
1572
1573 rc2 = rtDbgCfgTryDownloadAndOpen(pThis, pszServer, pszPath, pszCacheSubDir, pszUuidMappingSubDir,
1574 pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2);
1575 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1576 return rc2;
1577 }
1578 }
1579 else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("cache*")))
1580 {
1581 /*
1582 * Cache directory.
1583 */
1584 pszDir += sizeof("cache*") - 1;
1585 cchDir -= sizeof("cache*") - 1;
1586 if (!cchDir)
1587 continue;
1588 pchCache = pszDir;
1589 cchCache = cchDir;
1590
1591 memcpy(pszPath, pchCache, cchCache);
1592 pszPath[cchCache] = '\0';
1593 RTPathChangeToUnixSlashes(pszPath, false);
1594
1595 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
1596 pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2);
1597 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1598 return rc2;
1599 }
1600 else
1601 {
1602 /*
1603 * Normal directory. Check for our own 'rec*' and 'norec*' prefix
1604 * flags governing recursive searching.
1605 */
1606 uint32_t fFlagsDir = fFlags;
1607 if (!RTStrNICmp(pszDir, RT_STR_TUPLE("rec*")))
1608 {
1609 pszDir += sizeof("rec*") - 1;
1610 cchDir -= sizeof("rec*") - 1;
1611 fFlagsDir |= RTDBGCFG_O_RECURSIVE;
1612 }
1613 else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("norec*")))
1614 {
1615 pszDir += sizeof("norec*") - 1;
1616 cchDir -= sizeof("norec*") - 1;
1617 fFlagsDir &= ~RTDBGCFG_O_RECURSIVE;
1618 }
1619
1620 /* Copy the path into the buffer and do the searching. */
1621 memcpy(pszPath, pszDir, cchDir);
1622 pszPath[cchDir] = '\0';
1623 RTPathChangeToUnixSlashes(pszPath, false);
1624
1625 rc2 = rtDbgCfgTryOpenDsymBundleInDir(pThis, pszPath, pSplitFn, papszSuffixes, fFlagsDir,
1626 pfnCallback, pvUser1, pvUser2);
1627 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1628 {
1629 if ( rc2 == VINF_CALLBACK_RETURN
1630 && cchCache > 0)
1631 rtDbgCfgCopyFileToCache(pThis, pszPath, pchCache, cchCache,
1632 pszCacheSubDir, pszUuidMappingSubDir, pSplitFn);
1633 return rc2;
1634 }
1635 }
1636
1637 /* Propagate errors. */
1638 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1639 rcRet = rc2;
1640 }
1641
1642 return rcRet;
1643}
1644
1645
1646/**
1647 * Creating a UUID mapping subdirectory path for use in caches.
1648 *
1649 * @returns IPRT status code.
1650 * @param pszSubDir The output buffer.
1651 * @param cbSubDir The size of the output buffer. (Top dir length +
1652 * slash + UUID string len + extra dash.)
1653 * @param pszTopDir The top level cache directory name. No slashes
1654 * or other directory separators, please.
1655 * @param pUuid The UUID.
1656 */
1657static int rtDbgCfgConstructUuidMappingSubDir(char *pszSubDir, size_t cbSubDir, const char *pszTopDir, PCRTUUID pUuid)
1658{
1659 Assert(!strpbrk(pszTopDir, ":/\\"));
1660
1661 size_t cchTopDir = strlen(pszTopDir);
1662 if (cchTopDir + 1 + 1 + RTUUID_STR_LENGTH + 1 > cbSubDir)
1663 return VERR_BUFFER_OVERFLOW;
1664 memcpy(pszSubDir, pszTopDir, cchTopDir);
1665
1666 pszSubDir += cchTopDir;
1667 *pszSubDir++ = RTPATH_SLASH;
1668 cbSubDir -= cchTopDir + 1;
1669
1670 /* ed5a8336-35c2-4892-9122-21d5572924a3 -> ED5A/8336/35C2/4892/9122/21D5572924A3 */
1671 int rc = RTUuidToStr(pUuid, pszSubDir + 1, cbSubDir - 1); AssertRCReturn(rc, rc);
1672 RTStrToUpper(pszSubDir + 1);
1673 memmove(pszSubDir, pszSubDir + 1, 4);
1674 pszSubDir += 4;
1675 *pszSubDir = RTPATH_SLASH;
1676 pszSubDir += 5;
1677 *pszSubDir = RTPATH_SLASH;
1678 pszSubDir += 5;
1679 *pszSubDir = RTPATH_SLASH;
1680 pszSubDir += 5;
1681 *pszSubDir = RTPATH_SLASH;
1682 pszSubDir += 5;
1683 *pszSubDir = RTPATH_SLASH;
1684
1685 return VINF_SUCCESS;
1686}
1687
1688
1689static int rtDbgCfgOpenBundleFile(RTDBGCFG hDbgCfg, const char *pszImage, const char * const *papszSuffixes,
1690 const char *pszBundleSubDir, PCRTUUID pUuid, const char *pszUuidMapDirName,
1691 const char *pszCacheSuffix, bool fOpenImage,
1692 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1693{
1694 /*
1695 * Bundles are directories, means we can forget about sharing code much
1696 * with the other RTDbgCfgOpenXXX methods. Thus we're duplicating a lot of
1697 * code from rtDbgCfgOpenWithSubDir with .dSYM/.kext/.dylib/.app/.* related
1698 * adjustments, so, a bug found here or there probably means the other
1699 * version needs updating.
1700 */
1701 int rcRet = VINF_SUCCESS;
1702 int rc2;
1703
1704 /*
1705 * Do a little validating first.
1706 */
1707 PRTDBGCFGINT pThis = hDbgCfg;
1708 if (pThis != NIL_RTDBGCFG)
1709 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1710 else
1711 pThis = NULL;
1712 AssertPtrReturn(pszImage, VERR_INVALID_POINTER);
1713 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
1714
1715 /*
1716 * Set up rtDbgCfgOpenWithSubDir and uuid map parameters.
1717 */
1718 uint32_t fFlags = RTDBGCFG_O_EXT_DEBUG_FILE | RT_OPSYS_DARWIN;
1719 const char *pszCacheSubDir = NULL;
1720 char szCacheSubDir[RTUUID_STR_LENGTH];
1721 const char *pszUuidMappingSubDir = NULL;
1722 char szUuidMappingSubDir[RTUUID_STR_LENGTH + 16];
1723 if (pUuid)
1724 {
1725 /* Since Mac debuggers uses UUID mappings, we just the slashing default
1726 UUID string representation instead of stripping dashes like for PDB. */
1727 RTUuidToStr(pUuid, szCacheSubDir, sizeof(szCacheSubDir));
1728 pszCacheSubDir = szCacheSubDir;
1729
1730 rc2 = rtDbgCfgConstructUuidMappingSubDir(szUuidMappingSubDir, sizeof(szUuidMappingSubDir), pszUuidMapDirName, pUuid);
1731 AssertRCReturn(rc2, rc2);
1732 pszUuidMappingSubDir = szUuidMappingSubDir;
1733 }
1734
1735 /*
1736 * Do some guessing as to the way we should parse the filename and whether
1737 * it's case exact or not.
1738 */
1739 bool fDosPath = strchr(pszImage, ':') != NULL
1740 || strchr(pszImage, '\\') != NULL
1741 || RT_OPSYS_USES_DOS_PATHS(fFlags & RTDBGCFG_O_OPSYS_MASK)
1742 || (fFlags & RTDBGCFG_O_CASE_INSENSITIVE);
1743 if (fDosPath)
1744 fFlags |= RTDBGCFG_O_CASE_INSENSITIVE;
1745
1746 rtDbgCfgLog2(pThis, "Looking for '%s' with %#x flags...\n", pszImage, fFlags);
1747
1748 PRTPATHSPLIT pSplitFn;
1749 rc2 = RTPathSplitA(pszImage, &pSplitFn, fDosPath ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
1750 if (RT_FAILURE(rc2))
1751 return rc2;
1752 AssertReturnStmt(pSplitFn->fProps & RTPATH_PROP_FILENAME, RTPathSplitFree(pSplitFn), VERR_IS_A_DIRECTORY);
1753
1754 /*
1755 * Try the image directory first.
1756 */
1757 char szPath[RTPATH_MAX];
1758 if (pSplitFn->cComps > 0)
1759 {
1760 rc2 = RTPathSplitReassemble(pSplitFn, RTPATH_STR_F_STYLE_HOST, szPath, sizeof(szPath));
1761 if (fOpenImage && RT_SUCCESS(rc2))
1762 {
1763 rc2 = RTStrCat(szPath, sizeof(szPath), papszSuffixes[0]);
1764 if (RT_SUCCESS(rc2))
1765 rc2 = RTStrCat(szPath, sizeof(szPath), pszBundleSubDir);
1766 if (RT_SUCCESS(rc2))
1767 rc2 = RTPathAppend(szPath, sizeof(szPath), pSplitFn->apszComps[pSplitFn->cComps - 1]);
1768 }
1769 if (RT_SUCCESS(rc2) && RTPathExists(szPath))
1770 {
1771 RTPathChangeToUnixSlashes(szPath, false);
1772 rtDbgCfgLog1(pThis, "Trying '%s'...\n", szPath);
1773 rc2 = pfnCallback(pThis, szPath, pvUser1, pvUser2);
1774 if (rc2 == VINF_CALLBACK_RETURN)
1775 rtDbgCfgLog1(pThis, "Found '%s'.\n", szPath);
1776 else if (rc2 == VERR_CALLBACK_RETURN)
1777 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", szPath);
1778 else
1779 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, szPath);
1780 }
1781 }
1782 if ( rc2 != VINF_CALLBACK_RETURN
1783 && rc2 != VERR_CALLBACK_RETURN)
1784 {
1785 /*
1786 * Try the current directory (will take cover relative paths
1787 * skipped above).
1788 */
1789 rc2 = RTPathGetCurrent(szPath, sizeof(szPath));
1790 if (RT_FAILURE(rc2))
1791 strcpy(szPath, ".");
1792 RTPathChangeToUnixSlashes(szPath, false);
1793
1794 rc2 = rtDbgCfgTryOpenDsymBundleInDir(pThis, szPath, pSplitFn, g_apszDSymBundleSuffixes,
1795 fFlags, pfnCallback, pvUser1, pvUser2);
1796 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1797 rcRet = rc2;
1798
1799 if ( rc2 != VINF_CALLBACK_RETURN
1800 && rc2 != VERR_CALLBACK_RETURN
1801 && pThis)
1802 {
1803 rc2 = RTCritSectRwEnterShared(&pThis->CritSect);
1804 if (RT_SUCCESS(rc2))
1805 {
1806 /*
1807 * Run the applicable lists.
1808 */
1809 rc2 = rtDbgCfgTryOpenBundleInList(pThis, &pThis->PathList, pSplitFn, g_apszDSymBundleSuffixes,
1810 pszCacheSubDir, pszCacheSuffix,
1811 pszUuidMappingSubDir, fFlags, szPath,
1812 pfnCallback, pvUser1, pvUser2);
1813 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1814 rcRet = rc2;
1815
1816 RTCritSectRwLeaveShared(&pThis->CritSect);
1817 }
1818 else if (RT_SUCCESS(rcRet))
1819 rcRet = rc2;
1820 }
1821 }
1822
1823 RTPathSplitFree(pSplitFn);
1824 if ( rc2 == VINF_CALLBACK_RETURN
1825 || rc2 == VERR_CALLBACK_RETURN)
1826 rcRet = rc2;
1827 else if (RT_SUCCESS(rcRet))
1828 rcRet = VERR_NOT_FOUND;
1829 return rcRet;
1830}
1831
1832
1833RTDECL(int) RTDbgCfgOpenDsymBundle(RTDBGCFG hDbgCfg, const char *pszImage, PCRTUUID pUuid,
1834 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1835{
1836 return rtDbgCfgOpenBundleFile(hDbgCfg, pszImage, g_apszDSymBundleSuffixes,
1837 "Contents" RTPATH_SLASH_STR "Resources" RTPATH_SLASH_STR "DWARF",
1838 pUuid, RTDBG_CACHE_UUID_MAP_DIR_DSYMS, RTDBG_CACHE_DSYM_FILE_SUFFIX, false /* fOpenImage */,
1839 pfnCallback, pvUser1, pvUser2);
1840}
1841
1842
1843RTDECL(int) RTDbgCfgOpenMachOImage(RTDBGCFG hDbgCfg, const char *pszImage, PCRTUUID pUuid,
1844 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1845{
1846 return rtDbgCfgOpenBundleFile(hDbgCfg, pszImage, g_apszBundleSuffixes,
1847 "Contents" RTPATH_SLASH_STR "MacOS",
1848 pUuid, RTDBG_CACHE_UUID_MAP_DIR_IMAGES, NULL /*pszCacheSuffix*/, true /* fOpenImage */,
1849 pfnCallback, pvUser1, pvUser2);
1850}
1851
1852
1853
1854RTDECL(int) RTDbgCfgSetLogCallback(RTDBGCFG hDbgCfg, PFNRTDBGCFGLOG pfnCallback, void *pvUser)
1855{
1856 PRTDBGCFGINT pThis = hDbgCfg;
1857 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1858 AssertPtrNullReturn(pfnCallback, VERR_INVALID_POINTER);
1859
1860 int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
1861 if (RT_SUCCESS(rc))
1862 {
1863 if ( pThis->pfnLogCallback == NULL
1864 || pfnCallback == NULL
1865 || pfnCallback == pThis->pfnLogCallback)
1866 {
1867 pThis->pfnLogCallback = NULL;
1868 pThis->pvLogUser = NULL;
1869 ASMCompilerBarrier(); /* paranoia */
1870 pThis->pvLogUser = pvUser;
1871 pThis->pfnLogCallback = pfnCallback;
1872 rc = VINF_SUCCESS;
1873 }
1874 else
1875 rc = VERR_ACCESS_DENIED;
1876 RTCritSectRwLeaveExcl(&pThis->CritSect);
1877 }
1878
1879 return rc;
1880}
1881
1882
1883/**
1884 * Frees a string list.
1885 *
1886 * @param pList The list to free.
1887 */
1888static void rtDbgCfgFreeStrList(PRTLISTANCHOR pList)
1889{
1890 PRTDBGCFGSTR pCur;
1891 PRTDBGCFGSTR pNext;
1892 RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry)
1893 {
1894 RTListNodeRemove(&pCur->ListEntry);
1895 RTMemFree(pCur);
1896 }
1897}
1898
1899
1900/**
1901 * Make changes to a string list, given a semicolon separated input string.
1902 *
1903 * @returns VINF_SUCCESS, VERR_FILENAME_TOO_LONG, VERR_NO_MEMORY
1904 * @param pThis The config instance.
1905 * @param enmOp The change operation.
1906 * @param pszValue The input strings separated by semicolon.
1907 * @param fPaths Indicates that this is a path list and that we
1908 * should look for srv and cache prefixes.
1909 * @param pList The string list anchor.
1910 */
1911static int rtDbgCfgChangeStringList(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue, bool fPaths,
1912 PRTLISTANCHOR pList)
1913{
1914 RT_NOREF_PV(pThis); RT_NOREF_PV(fPaths);
1915
1916 if (enmOp == RTDBGCFGOP_SET)
1917 rtDbgCfgFreeStrList(pList);
1918
1919 while (*pszValue)
1920 {
1921 /* Skip separators. */
1922 while (*pszValue == ';')
1923 pszValue++;
1924 if (!*pszValue)
1925 break;
1926
1927 /* Find the end of this path. */
1928 const char *pchPath = pszValue++;
1929 char ch;
1930 while ((ch = *pszValue) && ch != ';')
1931 pszValue++;
1932 size_t cchPath = pszValue - pchPath;
1933 if (cchPath >= UINT16_MAX)
1934 return VERR_FILENAME_TOO_LONG;
1935
1936 if (enmOp == RTDBGCFGOP_REMOVE)
1937 {
1938 /*
1939 * Remove all occurences.
1940 */
1941 PRTDBGCFGSTR pCur;
1942 PRTDBGCFGSTR pNext;
1943 RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry)
1944 {
1945 if ( pCur->cch == cchPath
1946 && !memcmp(pCur->sz, pchPath, cchPath))
1947 {
1948 RTListNodeRemove(&pCur->ListEntry);
1949 RTMemFree(pCur);
1950 }
1951 }
1952 }
1953 else
1954 {
1955 /*
1956 * We're adding a new one.
1957 */
1958 PRTDBGCFGSTR pNew = (PRTDBGCFGSTR)RTMemAlloc(RT_UOFFSETOF_DYN(RTDBGCFGSTR, sz[cchPath + 1]));
1959 if (!pNew)
1960 return VERR_NO_MEMORY;
1961 pNew->cch = (uint16_t)cchPath;
1962 pNew->fFlags = 0;
1963 memcpy(pNew->sz, pchPath, cchPath);
1964 pNew->sz[cchPath] = '\0';
1965
1966 if (enmOp == RTDBGCFGOP_PREPEND)
1967 RTListPrepend(pList, &pNew->ListEntry);
1968 else
1969 RTListAppend(pList, &pNew->ListEntry);
1970 }
1971 }
1972
1973 return VINF_SUCCESS;
1974}
1975
1976
1977/**
1978 * Make changes to a 64-bit value
1979 *
1980 * @returns VINF_SUCCESS, VERR_DBG_CFG_INVALID_VALUE.
1981 * @param pThis The config instance.
1982 * @param enmOp The change operation.
1983 * @param pszValue The input value.
1984 * @param paMnemonics The mnemonics map for this value.
1985 * @param puValue The value to change.
1986 */
1987static int rtDbgCfgChangeStringU64(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue,
1988 PCRTDBGCFGU64MNEMONIC paMnemonics, uint64_t *puValue)
1989{
1990 RT_NOREF_PV(pThis);
1991
1992 uint64_t uNew = enmOp == RTDBGCFGOP_SET ? 0 : *puValue;
1993 char ch;
1994 while ((ch = *pszValue))
1995 {
1996 /* skip whitespace and separators */
1997 while (RT_C_IS_SPACE(ch) || RT_C_IS_CNTRL(ch) || ch == ';' || ch == ':')
1998 ch = *++pszValue;
1999 if (!ch)
2000 break;
2001
2002 if (RT_C_IS_DIGIT(ch))
2003 {
2004 uint64_t uTmp;
2005 int rc = RTStrToUInt64Ex(pszValue, (char **)&pszValue, 0, &uTmp);
2006 if (RT_FAILURE(rc) || rc == VWRN_NUMBER_TOO_BIG)
2007 return VERR_DBG_CFG_INVALID_VALUE;
2008
2009 if (enmOp != RTDBGCFGOP_REMOVE)
2010 uNew |= uTmp;
2011 else
2012 uNew &= ~uTmp;
2013 }
2014 else
2015 {
2016 /* A mnemonic, find the end of it. */
2017 const char *pszMnemonic = pszValue - 1;
2018 do
2019 ch = *++pszValue;
2020 while (ch && !RT_C_IS_SPACE(ch) && !RT_C_IS_CNTRL(ch) && ch != ';' && ch != ':');
2021 size_t cchMnemonic = pszValue - pszMnemonic;
2022
2023 /* Look it up in the map and apply it. */
2024 unsigned i = 0;
2025 while (paMnemonics[i].pszMnemonic)
2026 {
2027 if ( cchMnemonic == paMnemonics[i].cchMnemonic
2028 && !memcmp(pszMnemonic, paMnemonics[i].pszMnemonic, cchMnemonic))
2029 {
2030 if (paMnemonics[i].fSet ? enmOp != RTDBGCFGOP_REMOVE : enmOp == RTDBGCFGOP_REMOVE)
2031 uNew |= paMnemonics[i].fFlags;
2032 else
2033 uNew &= ~paMnemonics[i].fFlags;
2034 break;
2035 }
2036 i++;
2037 }
2038
2039 if (!paMnemonics[i].pszMnemonic)
2040 return VERR_DBG_CFG_INVALID_VALUE;
2041 }
2042 }
2043
2044 *puValue = uNew;
2045 return VINF_SUCCESS;
2046}
2047
2048
2049RTDECL(int) RTDbgCfgChangeString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, const char *pszValue)
2050{
2051 PRTDBGCFGINT pThis = hDbgCfg;
2052 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
2053 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
2054 AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER);
2055 if (!pszValue)
2056 pszValue = "";
2057 else
2058 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
2059
2060 int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
2061 if (RT_SUCCESS(rc))
2062 {
2063 switch (enmProp)
2064 {
2065 case RTDBGCFGPROP_FLAGS:
2066 rc = rtDbgCfgChangeStringU64(pThis, enmOp, pszValue, g_aDbgCfgFlags, &pThis->fFlags);
2067 break;
2068 case RTDBGCFGPROP_PATH:
2069 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->PathList);
2070 break;
2071 case RTDBGCFGPROP_SUFFIXES:
2072 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, false, &pThis->SuffixList);
2073 break;
2074 case RTDBGCFGPROP_SRC_PATH:
2075 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->SrcPathList);
2076 break;
2077 default:
2078 AssertFailed();
2079 rc = VERR_INTERNAL_ERROR_3;
2080 }
2081
2082 RTCritSectRwLeaveExcl(&pThis->CritSect);
2083 }
2084
2085 return rc;
2086}
2087
2088
2089RTDECL(int) RTDbgCfgChangeUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, uint64_t uValue)
2090{
2091 PRTDBGCFGINT pThis = hDbgCfg;
2092 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
2093 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
2094 AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER);
2095
2096 int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
2097 if (RT_SUCCESS(rc))
2098 {
2099 uint64_t *puValue = NULL;
2100 switch (enmProp)
2101 {
2102 case RTDBGCFGPROP_FLAGS:
2103 puValue = &pThis->fFlags;
2104 break;
2105 default:
2106 rc = VERR_DBG_CFG_NOT_UINT_PROP;
2107 }
2108 if (RT_SUCCESS(rc))
2109 {
2110 switch (enmOp)
2111 {
2112 case RTDBGCFGOP_SET:
2113 *puValue = uValue;
2114 break;
2115 case RTDBGCFGOP_APPEND:
2116 case RTDBGCFGOP_PREPEND:
2117 *puValue |= uValue;
2118 break;
2119 case RTDBGCFGOP_REMOVE:
2120 *puValue &= ~uValue;
2121 break;
2122 default:
2123 AssertFailed();
2124 rc = VERR_INTERNAL_ERROR_2;
2125 }
2126 }
2127
2128 RTCritSectRwLeaveExcl(&pThis->CritSect);
2129 }
2130
2131 return rc;
2132}
2133
2134
2135/**
2136 * Querys a string list as a single string (semicolon separators).
2137 *
2138 * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW.
2139 * @param hDbgCfg The config instance handle.
2140 * @param pList The string list anchor.
2141 * @param pszValue The output buffer.
2142 * @param cbValue The size of the output buffer.
2143 */
2144static int rtDbgCfgQueryStringList(RTDBGCFG hDbgCfg, PRTLISTANCHOR pList,
2145 char *pszValue, size_t cbValue)
2146{
2147 RT_NOREF_PV(hDbgCfg);
2148
2149 /*
2150 * Check the length first.
2151 */
2152 size_t cbReq = 1;
2153 PRTDBGCFGSTR pCur;
2154 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
2155 cbReq += pCur->cch + 1;
2156 if (cbReq > cbValue)
2157 return VERR_BUFFER_OVERFLOW;
2158
2159 /*
2160 * Construct the string list in the buffer.
2161 */
2162 char *psz = pszValue;
2163 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
2164 {
2165 if (psz != pszValue)
2166 *psz++ = ';';
2167 memcpy(psz, pCur->sz, pCur->cch);
2168 psz += pCur->cch;
2169 }
2170 *psz = '\0';
2171
2172 return VINF_SUCCESS;
2173}
2174
2175
2176/**
2177 * Querys the string value of a 64-bit unsigned int.
2178 *
2179 * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW.
2180 * @param hDbgCfg The config instance handle.
2181 * @param uValue The value to query.
2182 * @param paMnemonics The mnemonics map for this value.
2183 * @param pszValue The output buffer.
2184 * @param cbValue The size of the output buffer.
2185 */
2186static int rtDbgCfgQueryStringU64(RTDBGCFG hDbgCfg, uint64_t uValue, PCRTDBGCFGU64MNEMONIC paMnemonics,
2187 char *pszValue, size_t cbValue)
2188{
2189 RT_NOREF_PV(hDbgCfg);
2190
2191 /*
2192 * If no mnemonics, just return the hex value.
2193 */
2194 if (!paMnemonics || paMnemonics[0].pszMnemonic)
2195 {
2196 char szTmp[64];
2197 size_t cch = RTStrPrintf(szTmp, sizeof(szTmp), "%#x", uValue);
2198 if (cch + 1 > cbValue)
2199 return VERR_BUFFER_OVERFLOW;
2200 memcpy(pszValue, szTmp, cbValue);
2201 return VINF_SUCCESS;
2202 }
2203
2204 /*
2205 * Check that there is sufficient buffer space first.
2206 */
2207 size_t cbReq = 1;
2208 for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++)
2209 if ( paMnemonics[i].fSet
2210 ? (paMnemonics[i].fFlags & uValue)
2211 : !(paMnemonics[i].fFlags & uValue))
2212 cbReq += (cbReq != 1) + paMnemonics[i].cchMnemonic;
2213 if (cbReq > cbValue)
2214 return VERR_BUFFER_OVERFLOW;
2215
2216 /*
2217 * Construct the string.
2218 */
2219 char *psz = pszValue;
2220 for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++)
2221 if ( paMnemonics[i].fSet
2222 ? (paMnemonics[i].fFlags & uValue)
2223 : !(paMnemonics[i].fFlags & uValue))
2224 {
2225 if (psz != pszValue)
2226 *psz++ = ' ';
2227 memcpy(psz, paMnemonics[i].pszMnemonic, paMnemonics[i].cchMnemonic);
2228 psz += paMnemonics[i].cchMnemonic;
2229 }
2230 *psz = '\0';
2231 return VINF_SUCCESS;
2232}
2233
2234
2235RTDECL(int) RTDbgCfgQueryString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, char *pszValue, size_t cbValue)
2236{
2237 PRTDBGCFGINT pThis = hDbgCfg;
2238 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
2239 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
2240 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
2241
2242 int rc = RTCritSectRwEnterShared(&pThis->CritSect);
2243 if (RT_SUCCESS(rc))
2244 {
2245 switch (enmProp)
2246 {
2247 case RTDBGCFGPROP_FLAGS:
2248 rc = rtDbgCfgQueryStringU64(pThis, pThis->fFlags, g_aDbgCfgFlags, pszValue, cbValue);
2249 break;
2250 case RTDBGCFGPROP_PATH:
2251 rc = rtDbgCfgQueryStringList(pThis, &pThis->PathList, pszValue, cbValue);
2252 break;
2253 case RTDBGCFGPROP_SUFFIXES:
2254 rc = rtDbgCfgQueryStringList(pThis, &pThis->SuffixList, pszValue, cbValue);
2255 break;
2256 case RTDBGCFGPROP_SRC_PATH:
2257 rc = rtDbgCfgQueryStringList(pThis, &pThis->SrcPathList, pszValue, cbValue);
2258 break;
2259 default:
2260 AssertFailed();
2261 rc = VERR_INTERNAL_ERROR_3;
2262 }
2263
2264 RTCritSectRwLeaveShared(&pThis->CritSect);
2265 }
2266
2267 return rc;
2268}
2269
2270
2271RTDECL(int) RTDbgCfgQueryUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, uint64_t *puValue)
2272{
2273 PRTDBGCFGINT pThis = hDbgCfg;
2274 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
2275 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
2276 AssertPtrReturn(puValue, VERR_INVALID_POINTER);
2277
2278 int rc = RTCritSectRwEnterShared(&pThis->CritSect);
2279 if (RT_SUCCESS(rc))
2280 {
2281 switch (enmProp)
2282 {
2283 case RTDBGCFGPROP_FLAGS:
2284 *puValue = pThis->fFlags;
2285 break;
2286 default:
2287 rc = VERR_DBG_CFG_NOT_UINT_PROP;
2288 }
2289
2290 RTCritSectRwLeaveShared(&pThis->CritSect);
2291 }
2292
2293 return rc;
2294}
2295
2296RTDECL(uint32_t) RTDbgCfgRetain(RTDBGCFG hDbgCfg)
2297{
2298 PRTDBGCFGINT pThis = hDbgCfg;
2299 RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX);
2300
2301 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
2302 Assert(cRefs < UINT32_MAX / 2);
2303 return cRefs;
2304}
2305
2306
2307RTDECL(uint32_t) RTDbgCfgRelease(RTDBGCFG hDbgCfg)
2308{
2309 if (hDbgCfg == NIL_RTDBGCFG)
2310 return 0;
2311
2312 PRTDBGCFGINT pThis = hDbgCfg;
2313 RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX);
2314
2315 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
2316 if (!cRefs)
2317 {
2318 /*
2319 * Last reference - free all memory.
2320 */
2321 ASMAtomicWriteU32(&pThis->u32Magic, ~RTDBGCFG_MAGIC);
2322 rtDbgCfgFreeStrList(&pThis->PathList);
2323 rtDbgCfgFreeStrList(&pThis->SuffixList);
2324 rtDbgCfgFreeStrList(&pThis->SrcPathList);
2325#ifdef RT_OS_WINDOWS
2326 rtDbgCfgFreeStrList(&pThis->NtSymbolPathList);
2327 rtDbgCfgFreeStrList(&pThis->NtExecutablePathList);
2328 rtDbgCfgFreeStrList(&pThis->NtSourcePath);
2329#endif
2330 RTCritSectRwDelete(&pThis->CritSect);
2331 RTMemFree(pThis);
2332 }
2333 else
2334 Assert(cRefs < UINT32_MAX / 2);
2335 return cRefs;
2336}
2337
2338
2339RTDECL(int) RTDbgCfgCreate(PRTDBGCFG phDbgCfg, const char *pszEnvVarPrefix, bool fNativePaths)
2340{
2341 /*
2342 * Validate input.
2343 */
2344 AssertPtrReturn(phDbgCfg, VERR_INVALID_POINTER);
2345 if (pszEnvVarPrefix)
2346 {
2347 AssertPtrReturn(pszEnvVarPrefix, VERR_INVALID_POINTER);
2348 AssertReturn(*pszEnvVarPrefix, VERR_INVALID_PARAMETER);
2349 }
2350
2351 /*
2352 * Allocate and initialize a new instance.
2353 */
2354 PRTDBGCFGINT pThis = (PRTDBGCFGINT)RTMemAllocZ(sizeof(*pThis));
2355 if (!pThis)
2356 return VERR_NO_MEMORY;
2357
2358 pThis->u32Magic = RTDBGCFG_MAGIC;
2359 pThis->cRefs = 1;
2360 RTListInit(&pThis->PathList);
2361 RTListInit(&pThis->SuffixList);
2362 RTListInit(&pThis->SrcPathList);
2363#ifdef RT_OS_WINDOWS
2364 RTListInit(&pThis->NtSymbolPathList);
2365 RTListInit(&pThis->NtExecutablePathList);
2366 RTListInit(&pThis->NtSourcePath);
2367#endif
2368
2369 int rc = RTCritSectRwInit(&pThis->CritSect);
2370 if (RT_FAILURE(rc))
2371 {
2372 RTMemFree(pThis);
2373 return rc;
2374 }
2375
2376 /*
2377 * Read configurtion from the environment if requested to do so.
2378 */
2379 if (pszEnvVarPrefix || fNativePaths)
2380 {
2381 const size_t cbEnvVar = 256;
2382 const size_t cbEnvVal = 65536 - cbEnvVar;
2383 char *pszEnvVar = (char *)RTMemTmpAlloc(cbEnvVar + cbEnvVal);
2384 if (pszEnvVar)
2385 {
2386 char *pszEnvVal = pszEnvVar + cbEnvVar;
2387
2388 if (pszEnvVarPrefix)
2389 {
2390 static struct
2391 {
2392 RTDBGCFGPROP enmProp;
2393 const char *pszVar;
2394 } const s_aProps[] =
2395 {
2396 { RTDBGCFGPROP_FLAGS, "FLAGS" },
2397 { RTDBGCFGPROP_PATH, "PATH" },
2398 { RTDBGCFGPROP_SUFFIXES, "SUFFIXES" },
2399 { RTDBGCFGPROP_SRC_PATH, "SRC_PATH" },
2400 };
2401
2402 for (unsigned i = 0; i < RT_ELEMENTS(s_aProps); i++)
2403 {
2404 size_t cchEnvVar = RTStrPrintf(pszEnvVar, cbEnvVar, "%s_%s", pszEnvVarPrefix, s_aProps[i].pszVar);
2405 if (cchEnvVar >= cbEnvVar - 1)
2406 {
2407 rc = VERR_BUFFER_OVERFLOW;
2408 break;
2409 }
2410
2411 rc = RTEnvGetEx(RTENV_DEFAULT, pszEnvVar, pszEnvVal, cbEnvVal, NULL);
2412 if (RT_SUCCESS(rc))
2413 {
2414 rc = RTDbgCfgChangeString(pThis, s_aProps[i].enmProp, RTDBGCFGOP_SET, pszEnvVal);
2415 if (RT_FAILURE(rc))
2416 break;
2417 }
2418 else if (rc != VERR_ENV_VAR_NOT_FOUND)
2419 break;
2420 else
2421 rc = VINF_SUCCESS;
2422 }
2423 }
2424
2425 /*
2426 * Pick up system specific search paths.
2427 */
2428 if (RT_SUCCESS(rc) && fNativePaths)
2429 {
2430 struct
2431 {
2432 PRTLISTANCHOR pList;
2433 const char *pszVar;
2434 char chSep;
2435 } aNativePaths[] =
2436 {
2437#ifdef RT_OS_WINDOWS
2438 { &pThis->NtExecutablePathList, "_NT_EXECUTABLE_PATH", ';' },
2439 { &pThis->NtSymbolPathList, "_NT_ALT_SYMBOL_PATH", ';' },
2440 { &pThis->NtSymbolPathList, "_NT_SYMBOL_PATH", ';' },
2441 { &pThis->NtSourcePath, "_NT_SOURCE_PATH", ';' },
2442#endif
2443 { NULL, NULL, 0 }
2444 };
2445 for (unsigned i = 0; aNativePaths[i].pList; i++)
2446 {
2447 Assert(aNativePaths[i].chSep == ';'); /* fix when needed */
2448 rc = RTEnvGetEx(RTENV_DEFAULT, aNativePaths[i].pszVar, pszEnvVal, cbEnvVal, NULL);
2449 if (RT_SUCCESS(rc))
2450 {
2451 rc = rtDbgCfgChangeStringList(pThis, RTDBGCFGOP_APPEND, pszEnvVal, true, aNativePaths[i].pList);
2452 if (RT_FAILURE(rc))
2453 break;
2454 }
2455 else if (rc != VERR_ENV_VAR_NOT_FOUND)
2456 break;
2457 else
2458 rc = VINF_SUCCESS;
2459 }
2460 }
2461 RTMemTmpFree(pszEnvVar);
2462 }
2463 else
2464 rc = VERR_NO_TMP_MEMORY;
2465 if (RT_FAILURE(rc))
2466 {
2467 /*
2468 * Error, bail out.
2469 */
2470 RTDbgCfgRelease(pThis);
2471 return rc;
2472 }
2473 }
2474
2475 /*
2476 * Returns successfully.
2477 */
2478 *phDbgCfg = pThis;
2479
2480 return VINF_SUCCESS;
2481}
2482
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette