VirtualBox

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

Last change on this file since 93138 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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