VirtualBox

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

Last change on this file since 80585 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • 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 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * IPRT - Debugging Configuration.
4 */
5
6/*
7 * Copyright (C) 2013-2019 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 return VWRN_NOT_FOUND;
733
734 /*
735 * Create the path.
736 */
737 size_t cchTmp = strlen(pszPath);
738
739 int rc = RTDirCreateFullPath(pszPath, 0766);
740 if (!RTDirExists(pszPath))
741 {
742 Log(("Error creating cache dir '%s': %Rrc\n", pszPath, rc));
743 return rc;
744 }
745
746 const char *pszFilename = pSplitFn->apszComps[pSplitFn->cComps - 1];
747 rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
748 if (RT_FAILURE(rc))
749 return rc;
750 RTStrToLower(&pszPath[cchTmp]);
751 if (!RTDirExists(pszPath))
752 {
753 rc = RTDirCreate(pszPath, 0766, 0);
754 if (RT_FAILURE(rc))
755 {
756 Log(("RTDirCreate(%s) -> %Rrc\n", pszPath, rc));
757 }
758 }
759
760 rc = RTPathAppend(pszPath, RTPATH_MAX, pszCacheSubDir);
761 if (RT_FAILURE(rc))
762 return rc;
763 if (!RTDirExists(pszPath))
764 {
765 rc = RTDirCreate(pszPath, 0766, 0);
766 if (RT_FAILURE(rc))
767 {
768 Log(("RTDirCreate(%s) -> %Rrc\n", pszPath, rc));
769 }
770 }
771
772 /* Prepare the destination file name while we're here. */
773 cchTmp = strlen(pszPath);
774 RTStrToLower(&pszPath[cchTmp]);
775 rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
776 if (RT_FAILURE(rc))
777 return rc;
778
779 /*
780 * Download/copy the file.
781 */
782 char szUrl[_2K];
783 /* Download URL? */
784 if ( RTStrIStartsWith(pszServer, "http://")
785 || RTStrIStartsWith(pszServer, "https://")
786 || RTStrIStartsWith(pszServer, "ftp://") )
787 {
788#ifdef IPRT_WITH_HTTP
789 RTHTTP hHttp;
790 rc = RTHttpCreate(&hHttp);
791 if (RT_SUCCESS(rc))
792 {
793 RTHttpUseSystemProxySettings(hHttp);
794 RTHttpSetFollowRedirects(hHttp, 8);
795
796 static const char * const s_apszHeaders[] =
797 {
798 "User-Agent: Microsoft-Symbol-Server/6.6.0999.9",
799 "Pragma: no-cache",
800 };
801
802 rc = RTHttpSetHeaders(hHttp, RT_ELEMENTS(s_apszHeaders), s_apszHeaders);
803 if (RT_SUCCESS(rc))
804 {
805 RTStrPrintf(szUrl, sizeof(szUrl), "%s/%s/%s/%s", pszServer, pszFilename, pszCacheSubDir, pszFilename);
806
807 /** @todo Use some temporary file name and rename it after the operation
808 * since not all systems support read-deny file sharing
809 * settings. */
810 rtDbgCfgLog2(pThis, "Downloading '%s' to '%s'...\n", szUrl, pszPath);
811 rc = RTHttpGetFile(hHttp, szUrl, pszPath);
812 if (RT_FAILURE(rc))
813 {
814 RTFileDelete(pszPath);
815 rtDbgCfgLog1(pThis, "%Rrc on URL '%s'\n", rc, szUrl);
816 }
817 if (rc == VERR_HTTP_NOT_FOUND)
818 {
819 /* Try the compressed version of the file. */
820 pszPath[strlen(pszPath) - 1] = '_';
821 szUrl[strlen(szUrl) - 1] = '_';
822 rtDbgCfgLog2(pThis, "Downloading '%s' to '%s'...\n", szUrl, pszPath);
823 rc = RTHttpGetFile(hHttp, szUrl, pszPath);
824 if (RT_SUCCESS(rc))
825 rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename);
826 else
827 {
828 rtDbgCfgLog1(pThis, "%Rrc on URL '%s'\n", rc, pszPath);
829 RTFileDelete(pszPath);
830 }
831 }
832 }
833
834 RTHttpDestroy(hHttp);
835 }
836#else
837 rc = VWRN_NOT_FOUND;
838#endif
839 }
840 /* No download, assume dir on server share. */
841 else
842 {
843 if (RTStrIStartsWith(pszServer, "file:///"))
844 pszServer += 4 + 1 + 3 - 1;
845
846 /* Compose the path to the uncompressed file on the server. */
847 rc = RTPathJoin(szUrl, sizeof(szUrl), pszServer, pszFilename);
848 if (RT_SUCCESS(rc))
849 rc = RTPathAppend(szUrl, sizeof(szUrl), pszCacheSubDir);
850 if (RT_SUCCESS(rc))
851 rc = RTPathAppend(szUrl, sizeof(szUrl), pszFilename);
852 if (RT_SUCCESS(rc))
853 {
854 rtDbgCfgLog2(pThis, "Copying '%s' to '%s'...\n", szUrl, pszPath);
855 rc = RTFileCopy(szUrl, pszPath);
856 if (RT_FAILURE(rc))
857 {
858 RTFileDelete(pszPath);
859 rtDbgCfgLog1(pThis, "%Rrc on '%s'\n", rc, szUrl);
860
861 /* Try the compressed version. */
862 pszPath[strlen(pszPath) - 1] = '_';
863 szUrl[strlen(szUrl) - 1] = '_';
864 rtDbgCfgLog2(pThis, "Copying '%s' to '%s'...\n", szUrl, pszPath);
865 rc = RTFileCopy(szUrl, pszPath);
866 if (RT_SUCCESS(rc))
867 rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename);
868 else
869 {
870 rtDbgCfgLog1(pThis, "%Rrc on '%s'\n", rc, pszPath);
871 RTFileDelete(pszPath);
872 }
873 }
874 }
875 }
876 if (RT_SUCCESS(rc))
877 {
878 /*
879 * Succeeded in downloading it. Add UUID mapping?
880 */
881 if (pszUuidMappingSubDir)
882 {
883 /** @todo UUID mapping when downloading. */
884 }
885
886 /*
887 * Give the file a try.
888 */
889 Assert(RTFileExists(pszPath));
890 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
891 rc = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
892 if (rc == VINF_CALLBACK_RETURN)
893 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
894 else if (rc == VERR_CALLBACK_RETURN)
895 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
896 else
897 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc, pszPath);
898 }
899
900 return rc;
901}
902
903
904static int rtDbgCfgCopyFileToCache(PRTDBGCFGINT pThis, char const *pszSrc, const char *pchCache, size_t cchCache,
905 const char *pszCacheSubDir, const char *pszUuidMappingSubDir, PRTPATHSPLIT pSplitFn)
906{
907 RT_NOREF_PV(pThis); RT_NOREF_PV(pszSrc); RT_NOREF_PV(pchCache); RT_NOREF_PV(cchCache);
908 RT_NOREF_PV(pszUuidMappingSubDir); RT_NOREF_PV(pSplitFn);
909
910 if (!pszCacheSubDir || !*pszCacheSubDir)
911 return VINF_SUCCESS;
912
913 /** @todo copy to cache */
914 return VINF_SUCCESS;
915}
916
917
918static int rtDbgCfgTryOpenCache(PRTDBGCFGINT pThis, char *pszPath, size_t cchCachePath,
919 const char *pszCacheSubDir, const char *pszUuidMappingSubDir,
920 PCRTPATHSPLIT pSplitFn, const char *pszCacheSuffix, uint32_t fFlags,
921 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
922{
923 Assert(pszPath[cchCachePath] == '\0');
924
925 /*
926 * If the cache doesn't exist, fail right away.
927 */
928 if (!pszCacheSubDir || !*pszCacheSubDir)
929 return VWRN_NOT_FOUND;
930 if (!RTDirExists(pszPath))
931 {
932 rtDbgCfgLog2(pThis, "Cache does not exist: '%s'\n", pszPath);
933 return VWRN_NOT_FOUND;
934 }
935
936 /*
937 * If we got a UUID mapping option, try it first as we can hopefully
938 * dispense with case folding.
939 */
940 if (pszUuidMappingSubDir)
941 {
942 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszUuidMappingSubDir);
943 if ( RT_SUCCESS(rc)
944 && RTFileExists(pszPath))
945 {
946 /* Try resolve the path before presenting it to the client, a
947 12 digit filename is of little worth. */
948 char szBackup[RTPATH_MAX];
949 strcpy(szBackup, pszPath);
950 rc = RTPathAbs(szBackup, pszPath, RTPATH_MAX);
951 if (RT_FAILURE(rc))
952 strcpy(pszPath, szBackup);
953
954 /* Do the callback thing. */
955 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
956 int rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
957 if (rc2 == VINF_CALLBACK_RETURN)
958 rtDbgCfgLog1(pThis, "Found '%s' via uuid mapping.\n", pszPath);
959 else if (rc2 == VERR_CALLBACK_RETURN)
960 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
961 else
962 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
963 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
964 return rc2;
965
966 /* Failed, restore the cache path. */
967 memcpy(pszPath, szBackup, cchCachePath);
968 }
969 pszPath[cchCachePath] = '\0';
970 }
971
972 /*
973 * Carefully construct the cache path with case insensitivity in mind.
974 */
975 bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
976 && !rtDbgCfgIsFsCaseInsensitive(pszPath);
977 const char *pszFilename = pSplitFn->apszComps[pSplitFn->cComps - 1];
978
979 if (!rtDbgCfgIsDirAndFixCase(pszPath, pszFilename, fCaseInsensitive))
980 return VWRN_NOT_FOUND;
981
982 if (!rtDbgCfgIsDirAndFixCase(pszPath, pszCacheSubDir, fCaseInsensitive))
983 return VWRN_NOT_FOUND;
984
985 bool fProbablyCompressed = false;
986 if (!rtDbgCfgIsFileAndFixCase(pszPath, pszFilename, pszCacheSuffix, fCaseInsensitive,
987 RT_BOOL(fFlags & RTDBGCFG_O_MAYBE_COMPRESSED_MS), &fProbablyCompressed))
988 return VWRN_NOT_FOUND;
989 if (fProbablyCompressed)
990 {
991 int rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename);
992 if (RT_FAILURE(rc))
993 return VWRN_NOT_FOUND;
994 }
995
996 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
997 int rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
998 if (rc2 == VINF_CALLBACK_RETURN)
999 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
1000 else if (rc2 == VERR_CALLBACK_RETURN)
1001 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
1002 else
1003 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
1004 return rc2;
1005}
1006
1007
1008static int rtDbgCfgTryOpenList(PRTDBGCFGINT pThis, PRTLISTANCHOR pList, PRTPATHSPLIT pSplitFn, const char *pszCacheSubDir,
1009 const char *pszUuidMappingSubDir, uint32_t fFlags, char *pszPath,
1010 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1011{
1012 int rcRet = VWRN_NOT_FOUND;
1013 int rc2 = VINF_SUCCESS;
1014
1015 const char *pchCache = NULL;
1016 size_t cchCache = 0;
1017 int rcCache = VWRN_NOT_FOUND;
1018
1019 PRTDBGCFGSTR pCur;
1020 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
1021 {
1022 size_t cchDir = pCur->cch;
1023 const char *pszDir = pCur->sz;
1024 rtDbgCfgLog2(pThis, "Path list entry: '%s'\n", pszDir);
1025
1026 /* This is very simplistic, but we have a unreasonably large path
1027 buffer, so it'll work just fine and simplify things greatly below. */
1028 if (cchDir >= RTPATH_MAX - 8U)
1029 {
1030 if (RT_SUCCESS_NP(rcRet))
1031 rcRet = VERR_FILENAME_TOO_LONG;
1032 continue;
1033 }
1034
1035 /*
1036 * Process the path according to it's type.
1037 */
1038 if (!RTStrNICmp(pszDir, RT_STR_TUPLE("srv*")))
1039 {
1040 /*
1041 * Symbol server.
1042 */
1043 pszDir += sizeof("srv*") - 1;
1044 cchDir -= sizeof("srv*") - 1;
1045 bool fSearchCache = false;
1046 const char *pszServer = (const char *)memchr(pszDir, '*', cchDir);
1047 if (!pszServer)
1048 pszServer = pszDir;
1049 else if (pszServer == pszDir)
1050 continue;
1051 else
1052 {
1053 fSearchCache = true;
1054 pchCache = pszDir;
1055 cchCache = pszServer - pszDir;
1056 pszServer++;
1057 }
1058
1059 /* We don't have any default cache directory, so skip if the cache is missing. */
1060 if (cchCache == 0)
1061 continue;
1062
1063 /* Search the cache first (if we haven't already done so). */
1064 if (fSearchCache)
1065 {
1066 memcpy(pszPath, pchCache, cchCache);
1067 pszPath[cchCache] = '\0';
1068 RTPathChangeToUnixSlashes(pszPath, false);
1069
1070 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
1071 pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2);
1072 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1073 return rc2;
1074 }
1075
1076 /* Try downloading the file. */
1077 if (rcCache == VWRN_NOT_FOUND)
1078 {
1079 memcpy(pszPath, pchCache, cchCache);
1080 pszPath[cchCache] = '\0';
1081 RTPathChangeToUnixSlashes(pszPath, false);
1082
1083 rc2 = rtDbgCfgTryDownloadAndOpen(pThis, pszServer, pszPath, pszCacheSubDir, pszUuidMappingSubDir,
1084 pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2);
1085 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1086 return rc2;
1087 }
1088 }
1089 else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("cache*")))
1090 {
1091 /*
1092 * Cache directory.
1093 */
1094 pszDir += sizeof("cache*") - 1;
1095 cchDir -= sizeof("cache*") - 1;
1096 if (!cchDir)
1097 continue;
1098 pchCache = pszDir;
1099 cchCache = cchDir;
1100
1101 memcpy(pszPath, pchCache, cchCache);
1102 pszPath[cchCache] = '\0';
1103 RTPathChangeToUnixSlashes(pszPath, false);
1104
1105 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
1106 pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2);
1107 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1108 return rc2;
1109 }
1110 else
1111 {
1112 /*
1113 * Normal directory. Check for our own 'rec*' and 'norec*' prefix
1114 * flags governing recursive searching.
1115 */
1116 uint32_t fFlagsDir = fFlags;
1117 if (!RTStrNICmp(pszDir, RT_STR_TUPLE("rec*")))
1118 {
1119 pszDir += sizeof("rec*") - 1;
1120 cchDir -= sizeof("rec*") - 1;
1121 fFlagsDir |= RTDBGCFG_O_RECURSIVE;
1122 }
1123 else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("norec*")))
1124 {
1125 pszDir += sizeof("norec*") - 1;
1126 cchDir -= sizeof("norec*") - 1;
1127 fFlagsDir &= ~RTDBGCFG_O_RECURSIVE;
1128 }
1129
1130 /* Copy the path into the buffer and do the searching. */
1131 memcpy(pszPath, pszDir, cchDir);
1132 pszPath[cchDir] = '\0';
1133 RTPathChangeToUnixSlashes(pszPath, false);
1134
1135 rc2 = rtDbgCfgTryOpenDir(pThis, pszPath, pSplitFn, fFlagsDir, pfnCallback, pvUser1, pvUser2);
1136 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1137 {
1138 if ( rc2 == VINF_CALLBACK_RETURN
1139 && cchCache > 0)
1140 rtDbgCfgCopyFileToCache(pThis, pszPath, pchCache, cchCache,
1141 pszCacheSubDir, pszUuidMappingSubDir, pSplitFn);
1142 return rc2;
1143 }
1144 }
1145
1146 /* Propagate errors. */
1147 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1148 rcRet = rc2;
1149 }
1150
1151 return rcRet;
1152}
1153
1154
1155/**
1156 * Common worker routine for Image and debug info opening.
1157 *
1158 * This will not search using for suffixes.
1159 *
1160 * @returns IPRT status code.
1161 * @param hDbgCfg The debugging configuration handle.
1162 * NIL_RTDBGCFG is accepted, but the result is
1163 * that no paths will be searched beyond the
1164 * given and the current directory.
1165 * @param pszFilename The filename to search for. This may or may
1166 * not include a full or partial path.
1167 * @param pszCacheSubDir The cache subdirectory to look in.
1168 * @param pszUuidMappingSubDir UUID mapping subdirectory to check, NULL if
1169 * no mapping wanted.
1170 * @param fFlags Flags and hints.
1171 * @param pfnCallback The open callback routine.
1172 * @param pvUser1 User parameter 1.
1173 * @param pvUser2 User parameter 2.
1174 */
1175static int rtDbgCfgOpenWithSubDir(RTDBGCFG hDbgCfg, const char *pszFilename, const char *pszCacheSubDir,
1176 const char *pszUuidMappingSubDir, uint32_t fFlags,
1177 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1178{
1179 int rcRet = VINF_SUCCESS;
1180 int rc2;
1181
1182 /*
1183 * Do a little validating first.
1184 */
1185 PRTDBGCFGINT pThis = hDbgCfg;
1186 if (pThis != NIL_RTDBGCFG)
1187 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1188 else
1189 pThis = NULL;
1190 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
1191 AssertPtrReturn(pszCacheSubDir, VERR_INVALID_POINTER);
1192 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
1193 AssertReturn(!(fFlags & ~RTDBGCFG_O_VALID_MASK), VERR_INVALID_FLAGS);
1194
1195 /*
1196 * Do some guessing as to the way we should parse the filename and whether
1197 * it's case exact or not.
1198 */
1199 bool fDosPath = RT_OPSYS_USES_DOS_PATHS(fFlags & RTDBGCFG_O_OPSYS_MASK)
1200 || (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
1201 || strchr(pszFilename, ':') != NULL
1202 || strchr(pszFilename, '\\') != NULL;
1203 if (fDosPath)
1204 fFlags |= RTDBGCFG_O_CASE_INSENSITIVE;
1205
1206 rtDbgCfgLog2(pThis, "Looking for '%s' w/ cache subdir '%s' and %#x flags...\n", pszFilename, pszCacheSubDir, fFlags);
1207
1208 PRTPATHSPLIT pSplitFn;
1209 rc2 = RTPathSplitA(pszFilename, &pSplitFn, fDosPath ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
1210 if (RT_FAILURE(rc2))
1211 return rc2;
1212 AssertReturnStmt(pSplitFn->fProps & RTPATH_PROP_FILENAME, RTPathSplitFree(pSplitFn), VERR_IS_A_DIRECTORY);
1213
1214 /*
1215 * Try the stored file name first if it has a kind of absolute path.
1216 */
1217 char szPath[RTPATH_MAX];
1218 if (RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps))
1219 {
1220 rc2 = RTPathSplitReassemble(pSplitFn, RTPATH_STR_F_STYLE_HOST, szPath, sizeof(szPath));
1221 if (RT_SUCCESS(rc2) && RTFileExists(szPath))
1222 {
1223 RTPathChangeToUnixSlashes(szPath, false);
1224 rtDbgCfgLog1(pThis, "Trying '%s'...\n", szPath);
1225 rc2 = pfnCallback(pThis, szPath, pvUser1, pvUser2);
1226 if (rc2 == VINF_CALLBACK_RETURN)
1227 rtDbgCfgLog1(pThis, "Found '%s'.\n", szPath);
1228 else if (rc2 == VERR_CALLBACK_RETURN)
1229 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", szPath);
1230 else
1231 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, szPath);
1232 }
1233 }
1234 if ( rc2 != VINF_CALLBACK_RETURN
1235 && rc2 != VERR_CALLBACK_RETURN)
1236 {
1237 /*
1238 * Try the current directory (will take cover relative paths
1239 * skipped above).
1240 */
1241 rc2 = RTPathGetCurrent(szPath, sizeof(szPath));
1242 if (RT_FAILURE(rc2))
1243 strcpy(szPath, ".");
1244 RTPathChangeToUnixSlashes(szPath, false);
1245
1246 rc2 = rtDbgCfgTryOpenDir(pThis, szPath, pSplitFn, fFlags, pfnCallback, pvUser1, pvUser2);
1247 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1248 rcRet = rc2;
1249
1250 if ( rc2 != VINF_CALLBACK_RETURN
1251 && rc2 != VERR_CALLBACK_RETURN
1252 && pThis)
1253 {
1254 rc2 = RTCritSectRwEnterShared(&pThis->CritSect);
1255 if (RT_SUCCESS(rc2))
1256 {
1257 /*
1258 * Run the applicable lists.
1259 */
1260 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->PathList, pSplitFn, pszCacheSubDir,
1261 pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2);
1262 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1263 rcRet = rc2;
1264
1265#ifdef RT_OS_WINDOWS
1266 if ( rc2 != VINF_CALLBACK_RETURN
1267 && rc2 != VERR_CALLBACK_RETURN
1268 && (fFlags & RTDBGCFG_O_EXECUTABLE_IMAGE)
1269 && !(fFlags & RTDBGCFG_O_NO_SYSTEM_PATHS)
1270 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) )
1271 {
1272 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtExecutablePathList, pSplitFn, pszCacheSubDir,
1273 pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2);
1274 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1275 rcRet = rc2;
1276 }
1277
1278 if ( rc2 != VINF_CALLBACK_RETURN
1279 && rc2 != VERR_CALLBACK_RETURN
1280 && !(fFlags & RTDBGCFG_O_NO_SYSTEM_PATHS)
1281 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) )
1282 {
1283 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtSymbolPathList, pSplitFn, pszCacheSubDir,
1284 pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2);
1285 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1286 rcRet = rc2;
1287 }
1288#endif
1289 RTCritSectRwLeaveShared(&pThis->CritSect);
1290 }
1291 else if (RT_SUCCESS(rcRet))
1292 rcRet = rc2;
1293 }
1294 }
1295
1296 RTPathSplitFree(pSplitFn);
1297 if ( rc2 == VINF_CALLBACK_RETURN
1298 || rc2 == VERR_CALLBACK_RETURN)
1299 rcRet = rc2;
1300 else if (RT_SUCCESS(rcRet))
1301 rcRet = VERR_NOT_FOUND;
1302 return rcRet;
1303}
1304
1305
1306RTDECL(int) RTDbgCfgOpenEx(RTDBGCFG hDbgCfg, const char *pszFilename, const char *pszCacheSubDir,
1307 const char *pszUuidMappingSubDir, uint32_t fFlags,
1308 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1309{
1310 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, pszCacheSubDir, pszUuidMappingSubDir, fFlags,
1311 pfnCallback, pvUser1, pvUser2);
1312}
1313
1314
1315
1316
1317RTDECL(int) RTDbgCfgOpenPeImage(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp,
1318 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1319{
1320 char szSubDir[32];
1321 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage);
1322 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1323 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1324 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXECUTABLE_IMAGE,
1325 pfnCallback, pvUser1, pvUser2);
1326}
1327
1328
1329RTDECL(int) RTDbgCfgOpenPdb70(RTDBGCFG hDbgCfg, const char *pszFilename, PCRTUUID pUuid, uint32_t uAge,
1330 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1331{
1332 char szSubDir[64];
1333 if (!pUuid)
1334 szSubDir[0] = '\0';
1335 else
1336 {
1337 /* Stringify the UUID and remove the dashes. */
1338 int rc2 = RTUuidToStr(pUuid, szSubDir, sizeof(szSubDir));
1339 AssertRCReturn(rc2, rc2);
1340
1341 char *pszSrc = szSubDir;
1342 char *pszDst = szSubDir;
1343 char ch;
1344 while ((ch = *pszSrc++))
1345 if (ch != '-')
1346 *pszDst++ = RT_C_TO_UPPER(ch);
1347
1348 RTStrPrintf(pszDst, &szSubDir[sizeof(szSubDir)] - pszDst, "%X", uAge);
1349 }
1350
1351 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1352 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1353 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE,
1354 pfnCallback, pvUser1, pvUser2);
1355}
1356
1357
1358RTDECL(int) RTDbgCfgOpenPdb20(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp, uint32_t uAge,
1359 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1360{
1361 RT_NOREF_PV(cbImage);
1362 /** @todo test this! */
1363 char szSubDir[32];
1364 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, uAge);
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) RTDbgCfgOpenDbg(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp,
1373 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1374{
1375 char szSubDir[32];
1376 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage);
1377 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1378 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1379 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE,
1380 pfnCallback, pvUser1, pvUser2);
1381}
1382
1383
1384RTDECL(int) RTDbgCfgOpenDwo(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t uCrc32,
1385 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1386{
1387 char szSubDir[32];
1388 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08x", uCrc32);
1389 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1390 RT_OPSYS_UNKNOWN | RTDBGCFG_O_EXT_DEBUG_FILE,
1391 pfnCallback, pvUser1, pvUser2);
1392}
1393
1394
1395
1396/*
1397 *
1398 * D a r w i n . d S Y M b u n d l e s
1399 * D a r w i n . d S Y M b u n d l e s
1400 * D a r w i n . d S Y M b u n d l e s
1401 *
1402 */
1403
1404/**
1405 * Very similar to rtDbgCfgTryOpenDir.
1406 */
1407static int rtDbgCfgTryOpenDsymBundleInDir(PRTDBGCFGINT pThis, char *pszPath, PRTPATHSPLIT pSplitFn,
1408 const char * const *papszSuffixes, uint32_t fFlags,
1409 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1410{
1411 int rcRet = VWRN_NOT_FOUND;
1412 int rc2;
1413
1414 /* If the directory doesn't exist, just quit immediately.
1415 Note! Our case insensitivity doesn't extend to the search dirs themselfs,
1416 only to the bits under neath them. */
1417 if (!RTDirExists(pszPath))
1418 {
1419 rtDbgCfgLog2(pThis, "Dir does not exist: '%s'\n", pszPath);
1420 return rcRet;
1421 }
1422
1423 /* Figure out whether we have to do a case sensitive search or not.
1424 Note! As a simplification, we don't ask for case settings in each
1425 directory under the user specified path, we assume the file
1426 systems that mounted there have compatible settings. Faster
1427 that way. */
1428 bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
1429 && !rtDbgCfgIsFsCaseInsensitive(pszPath);
1430
1431 size_t const cchPath = strlen(pszPath);
1432
1433 /*
1434 * Look for the file with less and less of the original path given.
1435 * Also try out typical bundle extension variations.
1436 */
1437 const char *pszName = pSplitFn->apszComps[pSplitFn->cComps - 1];
1438 for (unsigned i = RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps); i < pSplitFn->cComps; i++)
1439 {
1440 pszPath[cchPath] = '\0';
1441
1442 rc2 = VINF_SUCCESS;
1443 for (unsigned j = i; j < pSplitFn->cComps - 1U && RT_SUCCESS(rc2); j++)
1444 if (!rtDbgCfgIsDirAndFixCase(pszPath, pSplitFn->apszComps[i], fCaseInsensitive))
1445 rc2 = VERR_FILE_NOT_FOUND;
1446 if (RT_SUCCESS(rc2))
1447 {
1448 for (uint32_t iSuffix = 0; papszSuffixes[iSuffix]; iSuffix++)
1449 {
1450 if ( !rtDbgCfgIsDirAndFixCase2(pszPath, pszName, papszSuffixes[iSuffix], fCaseInsensitive)
1451 && !rtDbgCfgIsDirAndFixCase(pszPath, "Contents", fCaseInsensitive)
1452 && !rtDbgCfgIsDirAndFixCase(pszPath, "Resources", fCaseInsensitive)
1453 && !rtDbgCfgIsDirAndFixCase(pszPath, "DWARF", fCaseInsensitive))
1454 {
1455 if (rtDbgCfgIsFileAndFixCase(pszPath, pszName, NULL /*pszSuffix*/, fCaseInsensitive, false, NULL))
1456 {
1457 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
1458 rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
1459 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1460 {
1461 if (rc2 == VINF_CALLBACK_RETURN)
1462 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
1463 else
1464 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
1465 return rc2;
1466 }
1467 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
1468 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1469 rcRet = rc2;
1470 }
1471 }
1472 }
1473 }
1474 rc2 = VERR_FILE_NOT_FOUND;
1475 }
1476
1477 /*
1478 * Do a recursive search if requested.
1479 */
1480 if ( (fFlags & RTDBGCFG_O_RECURSIVE)
1481 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH) )
1482 {
1483 /** @todo Recursive searching will be done later. */
1484 }
1485
1486 return rcRet;
1487}
1488
1489
1490/**
1491 * Very similar to rtDbgCfgTryOpenList.
1492 */
1493static int rtDbgCfgTryOpenBundleInList(PRTDBGCFGINT pThis, PRTLISTANCHOR pList, PRTPATHSPLIT pSplitFn,
1494 const char * const *papszSuffixes, const char *pszCacheSubDir,
1495 const char *pszCacheSuffix, const char *pszUuidMappingSubDir,
1496 uint32_t fFlags, char *pszPath,
1497 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1498{
1499 int rcRet = VWRN_NOT_FOUND;
1500 int rc2;
1501
1502 const char *pchCache = NULL;
1503 size_t cchCache = 0;
1504 int rcCache = VWRN_NOT_FOUND;
1505
1506 PRTDBGCFGSTR pCur;
1507 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
1508 {
1509 size_t cchDir = pCur->cch;
1510 const char *pszDir = pCur->sz;
1511 rtDbgCfgLog2(pThis, "Path list entry: '%s'\n", pszDir);
1512
1513 /* This is very simplistic, but we have a unreasonably large path
1514 buffer, so it'll work just fine and simplify things greatly below. */
1515 if (cchDir >= RTPATH_MAX - 8U)
1516 {
1517 if (RT_SUCCESS_NP(rcRet))
1518 rcRet = VERR_FILENAME_TOO_LONG;
1519 continue;
1520 }
1521
1522 /*
1523 * Process the path according to it's type.
1524 */
1525 rc2 = VINF_SUCCESS;
1526 if (!RTStrNICmp(pszDir, RT_STR_TUPLE("srv*")))
1527 {
1528 /*
1529 * Symbol server.
1530 */
1531 pszDir += sizeof("srv*") - 1;
1532 cchDir -= sizeof("srv*") - 1;
1533 bool fSearchCache = false;
1534 const char *pszServer = (const char *)memchr(pszDir, '*', cchDir);
1535 if (!pszServer)
1536 pszServer = pszDir;
1537 else if (pszServer == pszDir)
1538 continue;
1539 else
1540 {
1541 fSearchCache = true;
1542 pchCache = pszDir;
1543 cchCache = pszServer - pszDir;
1544 pszServer++;
1545 }
1546
1547 /* We don't have any default cache directory, so skip if the cache is missing. */
1548 if (cchCache == 0)
1549 continue;
1550
1551 /* Search the cache first (if we haven't already done so). */
1552 if (fSearchCache)
1553 {
1554 memcpy(pszPath, pchCache, cchCache);
1555 pszPath[cchCache] = '\0';
1556 RTPathChangeToUnixSlashes(pszPath, false);
1557
1558 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
1559 pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2);
1560 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1561 return rc2;
1562 }
1563
1564 /* Try downloading the file. */
1565 if (rcCache == VWRN_NOT_FOUND)
1566 {
1567 memcpy(pszPath, pchCache, cchCache);
1568 pszPath[cchCache] = '\0';
1569 RTPathChangeToUnixSlashes(pszPath, false);
1570
1571 rc2 = rtDbgCfgTryDownloadAndOpen(pThis, pszServer, pszPath, pszCacheSubDir, pszUuidMappingSubDir,
1572 pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2);
1573 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1574 return rc2;
1575 }
1576 }
1577 else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("cache*")))
1578 {
1579 /*
1580 * Cache directory.
1581 */
1582 pszDir += sizeof("cache*") - 1;
1583 cchDir -= sizeof("cache*") - 1;
1584 if (!cchDir)
1585 continue;
1586 pchCache = pszDir;
1587 cchCache = cchDir;
1588
1589 memcpy(pszPath, pchCache, cchCache);
1590 pszPath[cchCache] = '\0';
1591 RTPathChangeToUnixSlashes(pszPath, false);
1592
1593 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
1594 pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2);
1595 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1596 return rc2;
1597 }
1598 else
1599 {
1600 /*
1601 * Normal directory. Check for our own 'rec*' and 'norec*' prefix
1602 * flags governing recursive searching.
1603 */
1604 uint32_t fFlagsDir = fFlags;
1605 if (!RTStrNICmp(pszDir, RT_STR_TUPLE("rec*")))
1606 {
1607 pszDir += sizeof("rec*") - 1;
1608 cchDir -= sizeof("rec*") - 1;
1609 fFlagsDir |= RTDBGCFG_O_RECURSIVE;
1610 }
1611 else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("norec*")))
1612 {
1613 pszDir += sizeof("norec*") - 1;
1614 cchDir -= sizeof("norec*") - 1;
1615 fFlagsDir &= ~RTDBGCFG_O_RECURSIVE;
1616 }
1617
1618 /* Copy the path into the buffer and do the searching. */
1619 memcpy(pszPath, pszDir, cchDir);
1620 pszPath[cchDir] = '\0';
1621 RTPathChangeToUnixSlashes(pszPath, false);
1622
1623 rc2 = rtDbgCfgTryOpenDsymBundleInDir(pThis, pszPath, pSplitFn, papszSuffixes, fFlagsDir,
1624 pfnCallback, pvUser1, pvUser2);
1625 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1626 {
1627 if ( rc2 == VINF_CALLBACK_RETURN
1628 && cchCache > 0)
1629 rtDbgCfgCopyFileToCache(pThis, pszPath, pchCache, cchCache,
1630 pszCacheSubDir, pszUuidMappingSubDir, pSplitFn);
1631 return rc2;
1632 }
1633 }
1634
1635 /* Propagate errors. */
1636 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1637 rcRet = rc2;
1638 }
1639
1640 return rcRet;
1641}
1642
1643
1644/**
1645 * Creating a UUID mapping subdirectory path for use in caches.
1646 *
1647 * @returns IPRT status code.
1648 * @param pszSubDir The output buffer.
1649 * @param cbSubDir The size of the output buffer. (Top dir length +
1650 * slash + UUID string len + extra dash.)
1651 * @param pszTopDir The top level cache directory name. No slashes
1652 * or other directory separators, please.
1653 * @param pUuid The UUID.
1654 */
1655static int rtDbgCfgConstructUuidMappingSubDir(char *pszSubDir, size_t cbSubDir, const char *pszTopDir, PCRTUUID pUuid)
1656{
1657 Assert(!strpbrk(pszTopDir, ":/\\"));
1658
1659 size_t cchTopDir = strlen(pszTopDir);
1660 if (cchTopDir + 1 + 1 + RTUUID_STR_LENGTH + 1 > cbSubDir)
1661 return VERR_BUFFER_OVERFLOW;
1662 memcpy(pszSubDir, pszTopDir, cchTopDir);
1663
1664 pszSubDir += cchTopDir;
1665 *pszSubDir++ = RTPATH_SLASH;
1666 cbSubDir -= cchTopDir + 1;
1667
1668 /* ed5a8336-35c2-4892-9122-21d5572924a3 -> ED5A/8336/35C2/4892/9122/21D5572924A3 */
1669 int rc = RTUuidToStr(pUuid, pszSubDir + 1, cbSubDir - 1); AssertRCReturn(rc, rc);
1670 RTStrToUpper(pszSubDir + 1);
1671 memmove(pszSubDir, pszSubDir + 1, 4);
1672 pszSubDir += 4;
1673 *pszSubDir = RTPATH_SLASH;
1674 pszSubDir += 5;
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
1683 return VINF_SUCCESS;
1684}
1685
1686
1687static int rtDbgCfgOpenBundleFile(RTDBGCFG hDbgCfg, const char *pszImage, const char * const *papszSuffixes,
1688 const char *pszBundleSubDir, PCRTUUID pUuid, const char *pszUuidMapDirName,
1689 const char *pszCacheSuffix, bool fOpenImage,
1690 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1691{
1692 /*
1693 * Bundles are directories, means we can forget about sharing code much
1694 * with the other RTDbgCfgOpenXXX methods. Thus we're duplicating a lot of
1695 * code from rtDbgCfgOpenWithSubDir with .dSYM/.kext/.dylib/.app/.* related
1696 * adjustments, so, a bug found here or there probably means the other
1697 * version needs updating.
1698 */
1699 int rcRet = VINF_SUCCESS;
1700 int rc2;
1701
1702 /*
1703 * Do a little validating first.
1704 */
1705 PRTDBGCFGINT pThis = hDbgCfg;
1706 if (pThis != NIL_RTDBGCFG)
1707 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1708 else
1709 pThis = NULL;
1710 AssertPtrReturn(pszImage, VERR_INVALID_POINTER);
1711 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
1712
1713 /*
1714 * Set up rtDbgCfgOpenWithSubDir and uuid map parameters.
1715 */
1716 uint32_t fFlags = RTDBGCFG_O_EXT_DEBUG_FILE | RT_OPSYS_DARWIN;
1717 const char *pszCacheSubDir = NULL;
1718 char szCacheSubDir[RTUUID_STR_LENGTH];
1719 const char *pszUuidMappingSubDir = NULL;
1720 char szUuidMappingSubDir[RTUUID_STR_LENGTH + 16];
1721 if (pUuid)
1722 {
1723 /* Since Mac debuggers uses UUID mappings, we just the slashing default
1724 UUID string representation instead of stripping dashes like for PDB. */
1725 RTUuidToStr(pUuid, szCacheSubDir, sizeof(szCacheSubDir));
1726 pszCacheSubDir = szCacheSubDir;
1727
1728 rc2 = rtDbgCfgConstructUuidMappingSubDir(szUuidMappingSubDir, sizeof(szUuidMappingSubDir), pszUuidMapDirName, pUuid);
1729 AssertRCReturn(rc2, rc2);
1730 pszUuidMappingSubDir = szUuidMappingSubDir;
1731 }
1732
1733 /*
1734 * Do some guessing as to the way we should parse the filename and whether
1735 * it's case exact or not.
1736 */
1737 bool fDosPath = strchr(pszImage, ':') != NULL
1738 || strchr(pszImage, '\\') != NULL
1739 || RT_OPSYS_USES_DOS_PATHS(fFlags & RTDBGCFG_O_OPSYS_MASK)
1740 || (fFlags & RTDBGCFG_O_CASE_INSENSITIVE);
1741 if (fDosPath)
1742 fFlags |= RTDBGCFG_O_CASE_INSENSITIVE;
1743
1744 rtDbgCfgLog2(pThis, "Looking for '%s' with %#x flags...\n", pszImage, fFlags);
1745
1746 PRTPATHSPLIT pSplitFn;
1747 rc2 = RTPathSplitA(pszImage, &pSplitFn, fDosPath ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
1748 if (RT_FAILURE(rc2))
1749 return rc2;
1750 AssertReturnStmt(pSplitFn->fProps & RTPATH_PROP_FILENAME, RTPathSplitFree(pSplitFn), VERR_IS_A_DIRECTORY);
1751
1752 /*
1753 * Try the image directory first.
1754 */
1755 char szPath[RTPATH_MAX];
1756 if (pSplitFn->cComps > 0)
1757 {
1758 rc2 = RTPathSplitReassemble(pSplitFn, RTPATH_STR_F_STYLE_HOST, szPath, sizeof(szPath));
1759 if (fOpenImage && RT_SUCCESS(rc2))
1760 {
1761 rc2 = RTStrCat(szPath, sizeof(szPath), papszSuffixes[0]);
1762 if (RT_SUCCESS(rc2))
1763 rc2 = RTStrCat(szPath, sizeof(szPath), pszBundleSubDir);
1764 if (RT_SUCCESS(rc2))
1765 rc2 = RTPathAppend(szPath, sizeof(szPath), pSplitFn->apszComps[pSplitFn->cComps - 1]);
1766 }
1767 if (RT_SUCCESS(rc2) && RTPathExists(szPath))
1768 {
1769 RTPathChangeToUnixSlashes(szPath, false);
1770 rtDbgCfgLog1(pThis, "Trying '%s'...\n", szPath);
1771 rc2 = pfnCallback(pThis, szPath, pvUser1, pvUser2);
1772 if (rc2 == VINF_CALLBACK_RETURN)
1773 rtDbgCfgLog1(pThis, "Found '%s'.\n", szPath);
1774 else if (rc2 == VERR_CALLBACK_RETURN)
1775 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", szPath);
1776 else
1777 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, szPath);
1778 }
1779 }
1780 if ( rc2 != VINF_CALLBACK_RETURN
1781 && rc2 != VERR_CALLBACK_RETURN)
1782 {
1783 /*
1784 * Try the current directory (will take cover relative paths
1785 * skipped above).
1786 */
1787 rc2 = RTPathGetCurrent(szPath, sizeof(szPath));
1788 if (RT_FAILURE(rc2))
1789 strcpy(szPath, ".");
1790 RTPathChangeToUnixSlashes(szPath, false);
1791
1792 rc2 = rtDbgCfgTryOpenDsymBundleInDir(pThis, szPath, pSplitFn, g_apszDSymBundleSuffixes,
1793 fFlags, pfnCallback, pvUser1, pvUser2);
1794 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1795 rcRet = rc2;
1796
1797 if ( rc2 != VINF_CALLBACK_RETURN
1798 && rc2 != VERR_CALLBACK_RETURN
1799 && pThis)
1800 {
1801 rc2 = RTCritSectRwEnterShared(&pThis->CritSect);
1802 if (RT_SUCCESS(rc2))
1803 {
1804 /*
1805 * Run the applicable lists.
1806 */
1807 rc2 = rtDbgCfgTryOpenBundleInList(pThis, &pThis->PathList, pSplitFn, g_apszDSymBundleSuffixes,
1808 pszCacheSubDir, pszCacheSuffix,
1809 pszUuidMappingSubDir, fFlags, szPath,
1810 pfnCallback, pvUser1, pvUser2);
1811 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1812 rcRet = rc2;
1813
1814 RTCritSectRwLeaveShared(&pThis->CritSect);
1815 }
1816 else if (RT_SUCCESS(rcRet))
1817 rcRet = rc2;
1818 }
1819 }
1820
1821 RTPathSplitFree(pSplitFn);
1822 if ( rc2 == VINF_CALLBACK_RETURN
1823 || rc2 == VERR_CALLBACK_RETURN)
1824 rcRet = rc2;
1825 else if (RT_SUCCESS(rcRet))
1826 rcRet = VERR_NOT_FOUND;
1827 return rcRet;
1828}
1829
1830
1831RTDECL(int) RTDbgCfgOpenDsymBundle(RTDBGCFG hDbgCfg, const char *pszImage, PCRTUUID pUuid,
1832 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1833{
1834 return rtDbgCfgOpenBundleFile(hDbgCfg, pszImage, g_apszDSymBundleSuffixes,
1835 "Contents" RTPATH_SLASH_STR "Resources" RTPATH_SLASH_STR "DWARF",
1836 pUuid, RTDBG_CACHE_UUID_MAP_DIR_DSYMS, RTDBG_CACHE_DSYM_FILE_SUFFIX, false /* fOpenImage */,
1837 pfnCallback, pvUser1, pvUser2);
1838}
1839
1840
1841RTDECL(int) RTDbgCfgOpenMachOImage(RTDBGCFG hDbgCfg, const char *pszImage, PCRTUUID pUuid,
1842 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1843{
1844 return rtDbgCfgOpenBundleFile(hDbgCfg, pszImage, g_apszBundleSuffixes,
1845 "Contents" RTPATH_SLASH_STR "MacOS",
1846 pUuid, RTDBG_CACHE_UUID_MAP_DIR_IMAGES, NULL /*pszCacheSuffix*/, true /* fOpenImage */,
1847 pfnCallback, pvUser1, pvUser2);
1848}
1849
1850
1851
1852RTDECL(int) RTDbgCfgSetLogCallback(RTDBGCFG hDbgCfg, PFNRTDBGCFGLOG pfnCallback, void *pvUser)
1853{
1854 PRTDBGCFGINT pThis = hDbgCfg;
1855 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1856 AssertPtrNullReturn(pfnCallback, VERR_INVALID_POINTER);
1857
1858 int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
1859 if (RT_SUCCESS(rc))
1860 {
1861 if ( pThis->pfnLogCallback == NULL
1862 || pfnCallback == NULL
1863 || pfnCallback == pThis->pfnLogCallback)
1864 {
1865 pThis->pfnLogCallback = NULL;
1866 pThis->pvLogUser = NULL;
1867 ASMCompilerBarrier(); /* paranoia */
1868 pThis->pvLogUser = pvUser;
1869 pThis->pfnLogCallback = pfnCallback;
1870 rc = VINF_SUCCESS;
1871 }
1872 else
1873 rc = VERR_ACCESS_DENIED;
1874 RTCritSectRwLeaveExcl(&pThis->CritSect);
1875 }
1876
1877 return rc;
1878}
1879
1880
1881/**
1882 * Frees a string list.
1883 *
1884 * @param pList The list to free.
1885 */
1886static void rtDbgCfgFreeStrList(PRTLISTANCHOR pList)
1887{
1888 PRTDBGCFGSTR pCur;
1889 PRTDBGCFGSTR pNext;
1890 RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry)
1891 {
1892 RTListNodeRemove(&pCur->ListEntry);
1893 RTMemFree(pCur);
1894 }
1895}
1896
1897
1898/**
1899 * Make changes to a string list, given a semicolon separated input string.
1900 *
1901 * @returns VINF_SUCCESS, VERR_FILENAME_TOO_LONG, VERR_NO_MEMORY
1902 * @param pThis The config instance.
1903 * @param enmOp The change operation.
1904 * @param pszValue The input strings separated by semicolon.
1905 * @param fPaths Indicates that this is a path list and that we
1906 * should look for srv and cache prefixes.
1907 * @param pList The string list anchor.
1908 */
1909static int rtDbgCfgChangeStringList(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue, bool fPaths,
1910 PRTLISTANCHOR pList)
1911{
1912 RT_NOREF_PV(pThis); RT_NOREF_PV(fPaths);
1913
1914 if (enmOp == RTDBGCFGOP_SET)
1915 rtDbgCfgFreeStrList(pList);
1916
1917 PRTLISTNODE pPrependTo = pList;
1918 while (*pszValue)
1919 {
1920 /* Skip separators. */
1921 while (*pszValue == ';')
1922 pszValue++;
1923 if (!*pszValue)
1924 break;
1925
1926 /* Find the end of this path. */
1927 const char *pchPath = pszValue++;
1928 char ch;
1929 while ((ch = *pszValue) && ch != ';')
1930 pszValue++;
1931 size_t cchPath = pszValue - pchPath;
1932 if (cchPath >= UINT16_MAX)
1933 return VERR_FILENAME_TOO_LONG;
1934
1935 if (enmOp == RTDBGCFGOP_REMOVE)
1936 {
1937 /*
1938 * Remove all occurences.
1939 */
1940 PRTDBGCFGSTR pCur;
1941 PRTDBGCFGSTR pNext;
1942 RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry)
1943 {
1944 if ( pCur->cch == cchPath
1945 && !memcmp(pCur->sz, pchPath, cchPath))
1946 {
1947 RTListNodeRemove(&pCur->ListEntry);
1948 RTMemFree(pCur);
1949 }
1950 }
1951 }
1952 else
1953 {
1954 /*
1955 * We're adding a new one.
1956 */
1957 PRTDBGCFGSTR pNew = (PRTDBGCFGSTR)RTMemAlloc(RT_UOFFSETOF_DYN(RTDBGCFGSTR, sz[cchPath + 1]));
1958 if (!pNew)
1959 return VERR_NO_MEMORY;
1960 pNew->cch = (uint16_t)cchPath;
1961 pNew->fFlags = 0;
1962 memcpy(pNew->sz, pchPath, cchPath);
1963 pNew->sz[cchPath] = '\0';
1964
1965 if (enmOp == RTDBGCFGOP_PREPEND)
1966 {
1967 RTListNodeInsertAfter(pPrependTo, &pNew->ListEntry);
1968 pPrependTo = &pNew->ListEntry;
1969 }
1970 else
1971 RTListAppend(pList, &pNew->ListEntry);
1972 }
1973 }
1974
1975 return VINF_SUCCESS;
1976}
1977
1978
1979/**
1980 * Make changes to a 64-bit value
1981 *
1982 * @returns VINF_SUCCESS, VERR_DBG_CFG_INVALID_VALUE.
1983 * @param pThis The config instance.
1984 * @param enmOp The change operation.
1985 * @param pszValue The input value.
1986 * @param paMnemonics The mnemonics map for this value.
1987 * @param puValue The value to change.
1988 */
1989static int rtDbgCfgChangeStringU64(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue,
1990 PCRTDBGCFGU64MNEMONIC paMnemonics, uint64_t *puValue)
1991{
1992 RT_NOREF_PV(pThis);
1993
1994 uint64_t uNew = enmOp == RTDBGCFGOP_SET ? 0 : *puValue;
1995 char ch;
1996 while ((ch = *pszValue))
1997 {
1998 /* skip whitespace and separators */
1999 while (RT_C_IS_SPACE(ch) || RT_C_IS_CNTRL(ch) || ch == ';' || ch == ':')
2000 ch = *++pszValue;
2001 if (!ch)
2002 break;
2003
2004 if (RT_C_IS_DIGIT(ch))
2005 {
2006 uint64_t uTmp;
2007 int rc = RTStrToUInt64Ex(pszValue, (char **)&pszValue, 0, &uTmp);
2008 if (RT_FAILURE(rc) || rc == VWRN_NUMBER_TOO_BIG)
2009 return VERR_DBG_CFG_INVALID_VALUE;
2010
2011 if (enmOp != RTDBGCFGOP_REMOVE)
2012 uNew |= uTmp;
2013 else
2014 uNew &= ~uTmp;
2015 }
2016 else
2017 {
2018 /* A mnemonic, find the end of it. */
2019 const char *pszMnemonic = pszValue - 1;
2020 do
2021 ch = *++pszValue;
2022 while (ch && !RT_C_IS_SPACE(ch) && !RT_C_IS_CNTRL(ch) && ch != ';' && ch != ':');
2023 size_t cchMnemonic = pszValue - pszMnemonic;
2024
2025 /* Look it up in the map and apply it. */
2026 unsigned i = 0;
2027 while (paMnemonics[i].pszMnemonic)
2028 {
2029 if ( cchMnemonic == paMnemonics[i].cchMnemonic
2030 && !memcmp(pszMnemonic, paMnemonics[i].pszMnemonic, cchMnemonic))
2031 {
2032 if (paMnemonics[i].fSet ? enmOp != RTDBGCFGOP_REMOVE : enmOp == RTDBGCFGOP_REMOVE)
2033 uNew |= paMnemonics[i].fFlags;
2034 else
2035 uNew &= ~paMnemonics[i].fFlags;
2036 break;
2037 }
2038 i++;
2039 }
2040
2041 if (!paMnemonics[i].pszMnemonic)
2042 return VERR_DBG_CFG_INVALID_VALUE;
2043 }
2044 }
2045
2046 *puValue = uNew;
2047 return VINF_SUCCESS;
2048}
2049
2050
2051RTDECL(int) RTDbgCfgChangeString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, const char *pszValue)
2052{
2053 PRTDBGCFGINT pThis = hDbgCfg;
2054 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
2055 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
2056 AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER);
2057 if (!pszValue)
2058 pszValue = "";
2059 else
2060 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
2061
2062 int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
2063 if (RT_SUCCESS(rc))
2064 {
2065 switch (enmProp)
2066 {
2067 case RTDBGCFGPROP_FLAGS:
2068 rc = rtDbgCfgChangeStringU64(pThis, enmOp, pszValue, g_aDbgCfgFlags, &pThis->fFlags);
2069 break;
2070 case RTDBGCFGPROP_PATH:
2071 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->PathList);
2072 break;
2073 case RTDBGCFGPROP_SUFFIXES:
2074 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, false, &pThis->SuffixList);
2075 break;
2076 case RTDBGCFGPROP_SRC_PATH:
2077 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->SrcPathList);
2078 break;
2079 default:
2080 AssertFailed();
2081 rc = VERR_INTERNAL_ERROR_3;
2082 }
2083
2084 RTCritSectRwLeaveExcl(&pThis->CritSect);
2085 }
2086
2087 return rc;
2088}
2089
2090
2091RTDECL(int) RTDbgCfgChangeUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, uint64_t uValue)
2092{
2093 PRTDBGCFGINT pThis = hDbgCfg;
2094 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
2095 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
2096 AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER);
2097
2098 int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
2099 if (RT_SUCCESS(rc))
2100 {
2101 uint64_t *puValue = NULL;
2102 switch (enmProp)
2103 {
2104 case RTDBGCFGPROP_FLAGS:
2105 puValue = &pThis->fFlags;
2106 break;
2107 default:
2108 rc = VERR_DBG_CFG_NOT_UINT_PROP;
2109 }
2110 if (RT_SUCCESS(rc))
2111 {
2112 switch (enmOp)
2113 {
2114 case RTDBGCFGOP_SET:
2115 *puValue = uValue;
2116 break;
2117 case RTDBGCFGOP_APPEND:
2118 case RTDBGCFGOP_PREPEND:
2119 *puValue |= uValue;
2120 break;
2121 case RTDBGCFGOP_REMOVE:
2122 *puValue &= ~uValue;
2123 break;
2124 default:
2125 AssertFailed();
2126 rc = VERR_INTERNAL_ERROR_2;
2127 }
2128 }
2129
2130 RTCritSectRwLeaveExcl(&pThis->CritSect);
2131 }
2132
2133 return rc;
2134}
2135
2136
2137/**
2138 * Querys a string list as a single string (semicolon separators).
2139 *
2140 * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW.
2141 * @param hDbgCfg The config instance handle.
2142 * @param pList The string list anchor.
2143 * @param pszValue The output buffer.
2144 * @param cbValue The size of the output buffer.
2145 */
2146static int rtDbgCfgQueryStringList(RTDBGCFG hDbgCfg, PRTLISTANCHOR pList,
2147 char *pszValue, size_t cbValue)
2148{
2149 RT_NOREF_PV(hDbgCfg);
2150
2151 /*
2152 * Check the length first.
2153 */
2154 size_t cbReq = 1;
2155 PRTDBGCFGSTR pCur;
2156 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
2157 cbReq += pCur->cch + 1;
2158 if (cbReq > cbValue)
2159 return VERR_BUFFER_OVERFLOW;
2160
2161 /*
2162 * Construct the string list in the buffer.
2163 */
2164 char *psz = pszValue;
2165 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
2166 {
2167 if (psz != pszValue)
2168 *psz++ = ';';
2169 memcpy(psz, pCur->sz, pCur->cch);
2170 psz += pCur->cch;
2171 }
2172 *psz = '\0';
2173
2174 return VINF_SUCCESS;
2175}
2176
2177
2178/**
2179 * Querys the string value of a 64-bit unsigned int.
2180 *
2181 * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW.
2182 * @param hDbgCfg The config instance handle.
2183 * @param uValue The value to query.
2184 * @param paMnemonics The mnemonics map for this value.
2185 * @param pszValue The output buffer.
2186 * @param cbValue The size of the output buffer.
2187 */
2188static int rtDbgCfgQueryStringU64(RTDBGCFG hDbgCfg, uint64_t uValue, PCRTDBGCFGU64MNEMONIC paMnemonics,
2189 char *pszValue, size_t cbValue)
2190{
2191 RT_NOREF_PV(hDbgCfg);
2192
2193 /*
2194 * If no mnemonics, just return the hex value.
2195 */
2196 if (!paMnemonics || paMnemonics[0].pszMnemonic)
2197 {
2198 char szTmp[64];
2199 size_t cch = RTStrPrintf(szTmp, sizeof(szTmp), "%#x", uValue);
2200 if (cch + 1 > cbValue)
2201 return VERR_BUFFER_OVERFLOW;
2202 memcpy(pszValue, szTmp, cbValue);
2203 return VINF_SUCCESS;
2204 }
2205
2206 /*
2207 * Check that there is sufficient buffer space first.
2208 */
2209 size_t cbReq = 1;
2210 for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++)
2211 if ( paMnemonics[i].fSet
2212 ? (paMnemonics[i].fFlags & uValue)
2213 : !(paMnemonics[i].fFlags & uValue))
2214 cbReq += (cbReq != 1) + paMnemonics[i].cchMnemonic;
2215 if (cbReq > cbValue)
2216 return VERR_BUFFER_OVERFLOW;
2217
2218 /*
2219 * Construct the string.
2220 */
2221 char *psz = pszValue;
2222 for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++)
2223 if ( paMnemonics[i].fSet
2224 ? (paMnemonics[i].fFlags & uValue)
2225 : !(paMnemonics[i].fFlags & uValue))
2226 {
2227 if (psz != pszValue)
2228 *psz++ = ' ';
2229 memcpy(psz, paMnemonics[i].pszMnemonic, paMnemonics[i].cchMnemonic);
2230 psz += paMnemonics[i].cchMnemonic;
2231 }
2232 *psz = '\0';
2233 return VINF_SUCCESS;
2234}
2235
2236
2237RTDECL(int) RTDbgCfgQueryString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, char *pszValue, size_t cbValue)
2238{
2239 PRTDBGCFGINT pThis = hDbgCfg;
2240 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
2241 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
2242 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
2243
2244 int rc = RTCritSectRwEnterShared(&pThis->CritSect);
2245 if (RT_SUCCESS(rc))
2246 {
2247 switch (enmProp)
2248 {
2249 case RTDBGCFGPROP_FLAGS:
2250 rc = rtDbgCfgQueryStringU64(pThis, pThis->fFlags, g_aDbgCfgFlags, pszValue, cbValue);
2251 break;
2252 case RTDBGCFGPROP_PATH:
2253 rc = rtDbgCfgQueryStringList(pThis, &pThis->PathList, pszValue, cbValue);
2254 break;
2255 case RTDBGCFGPROP_SUFFIXES:
2256 rc = rtDbgCfgQueryStringList(pThis, &pThis->SuffixList, pszValue, cbValue);
2257 break;
2258 case RTDBGCFGPROP_SRC_PATH:
2259 rc = rtDbgCfgQueryStringList(pThis, &pThis->SrcPathList, pszValue, cbValue);
2260 break;
2261 default:
2262 AssertFailed();
2263 rc = VERR_INTERNAL_ERROR_3;
2264 }
2265
2266 RTCritSectRwLeaveShared(&pThis->CritSect);
2267 }
2268
2269 return rc;
2270}
2271
2272
2273RTDECL(int) RTDbgCfgQueryUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, uint64_t *puValue)
2274{
2275 PRTDBGCFGINT pThis = hDbgCfg;
2276 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
2277 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
2278 AssertPtrReturn(puValue, VERR_INVALID_POINTER);
2279
2280 int rc = RTCritSectRwEnterShared(&pThis->CritSect);
2281 if (RT_SUCCESS(rc))
2282 {
2283 switch (enmProp)
2284 {
2285 case RTDBGCFGPROP_FLAGS:
2286 *puValue = pThis->fFlags;
2287 break;
2288 default:
2289 rc = VERR_DBG_CFG_NOT_UINT_PROP;
2290 }
2291
2292 RTCritSectRwLeaveShared(&pThis->CritSect);
2293 }
2294
2295 return rc;
2296}
2297
2298RTDECL(uint32_t) RTDbgCfgRetain(RTDBGCFG hDbgCfg)
2299{
2300 PRTDBGCFGINT pThis = hDbgCfg;
2301 RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX);
2302
2303 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
2304 Assert(cRefs < UINT32_MAX / 2);
2305 return cRefs;
2306}
2307
2308
2309RTDECL(uint32_t) RTDbgCfgRelease(RTDBGCFG hDbgCfg)
2310{
2311 if (hDbgCfg == NIL_RTDBGCFG)
2312 return 0;
2313
2314 PRTDBGCFGINT pThis = hDbgCfg;
2315 RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX);
2316
2317 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
2318 if (!cRefs)
2319 {
2320 /*
2321 * Last reference - free all memory.
2322 */
2323 ASMAtomicWriteU32(&pThis->u32Magic, ~RTDBGCFG_MAGIC);
2324 rtDbgCfgFreeStrList(&pThis->PathList);
2325 rtDbgCfgFreeStrList(&pThis->SuffixList);
2326 rtDbgCfgFreeStrList(&pThis->SrcPathList);
2327#ifdef RT_OS_WINDOWS
2328 rtDbgCfgFreeStrList(&pThis->NtSymbolPathList);
2329 rtDbgCfgFreeStrList(&pThis->NtExecutablePathList);
2330 rtDbgCfgFreeStrList(&pThis->NtSourcePath);
2331#endif
2332 RTCritSectRwDelete(&pThis->CritSect);
2333 RTMemFree(pThis);
2334 }
2335 else
2336 Assert(cRefs < UINT32_MAX / 2);
2337 return cRefs;
2338}
2339
2340
2341RTDECL(int) RTDbgCfgCreate(PRTDBGCFG phDbgCfg, const char *pszEnvVarPrefix, bool fNativePaths)
2342{
2343 /*
2344 * Validate input.
2345 */
2346 AssertPtrReturn(phDbgCfg, VERR_INVALID_POINTER);
2347 if (pszEnvVarPrefix)
2348 {
2349 AssertPtrReturn(pszEnvVarPrefix, VERR_INVALID_POINTER);
2350 AssertReturn(*pszEnvVarPrefix, VERR_INVALID_PARAMETER);
2351 }
2352
2353 /*
2354 * Allocate and initialize a new instance.
2355 */
2356 PRTDBGCFGINT pThis = (PRTDBGCFGINT)RTMemAllocZ(sizeof(*pThis));
2357 if (!pThis)
2358 return VERR_NO_MEMORY;
2359
2360 pThis->u32Magic = RTDBGCFG_MAGIC;
2361 pThis->cRefs = 1;
2362 RTListInit(&pThis->PathList);
2363 RTListInit(&pThis->SuffixList);
2364 RTListInit(&pThis->SrcPathList);
2365#ifdef RT_OS_WINDOWS
2366 RTListInit(&pThis->NtSymbolPathList);
2367 RTListInit(&pThis->NtExecutablePathList);
2368 RTListInit(&pThis->NtSourcePath);
2369#endif
2370
2371 int rc = RTCritSectRwInit(&pThis->CritSect);
2372 if (RT_FAILURE(rc))
2373 {
2374 RTMemFree(pThis);
2375 return rc;
2376 }
2377
2378 /*
2379 * Read configurtion from the environment if requested to do so.
2380 */
2381 if (pszEnvVarPrefix || fNativePaths)
2382 {
2383 const size_t cbEnvVar = 256;
2384 const size_t cbEnvVal = 65536 - cbEnvVar;
2385 char *pszEnvVar = (char *)RTMemTmpAlloc(cbEnvVar + cbEnvVal);
2386 if (pszEnvVar)
2387 {
2388 char *pszEnvVal = pszEnvVar + cbEnvVar;
2389
2390 if (pszEnvVarPrefix)
2391 {
2392 static struct
2393 {
2394 RTDBGCFGPROP enmProp;
2395 const char *pszVar;
2396 } const s_aProps[] =
2397 {
2398 { RTDBGCFGPROP_FLAGS, "FLAGS" },
2399 { RTDBGCFGPROP_PATH, "PATH" },
2400 { RTDBGCFGPROP_SUFFIXES, "SUFFIXES" },
2401 { RTDBGCFGPROP_SRC_PATH, "SRC_PATH" },
2402 };
2403
2404 for (unsigned i = 0; i < RT_ELEMENTS(s_aProps); i++)
2405 {
2406 size_t cchEnvVar = RTStrPrintf(pszEnvVar, cbEnvVar, "%s_%s", pszEnvVarPrefix, s_aProps[i].pszVar);
2407 if (cchEnvVar >= cbEnvVar - 1)
2408 {
2409 rc = VERR_BUFFER_OVERFLOW;
2410 break;
2411 }
2412
2413 rc = RTEnvGetEx(RTENV_DEFAULT, pszEnvVar, pszEnvVal, cbEnvVal, NULL);
2414 if (RT_SUCCESS(rc))
2415 {
2416 rc = RTDbgCfgChangeString(pThis, s_aProps[i].enmProp, RTDBGCFGOP_SET, pszEnvVal);
2417 if (RT_FAILURE(rc))
2418 break;
2419 }
2420 else if (rc != VERR_ENV_VAR_NOT_FOUND)
2421 break;
2422 else
2423 rc = VINF_SUCCESS;
2424 }
2425 }
2426
2427 /*
2428 * Pick up system specific search paths.
2429 */
2430 if (RT_SUCCESS(rc) && fNativePaths)
2431 {
2432 struct
2433 {
2434 PRTLISTANCHOR pList;
2435 const char *pszVar;
2436 char chSep;
2437 } aNativePaths[] =
2438 {
2439#ifdef RT_OS_WINDOWS
2440 { &pThis->NtExecutablePathList, "_NT_EXECUTABLE_PATH", ';' },
2441 { &pThis->NtSymbolPathList, "_NT_ALT_SYMBOL_PATH", ';' },
2442 { &pThis->NtSymbolPathList, "_NT_SYMBOL_PATH", ';' },
2443 { &pThis->NtSourcePath, "_NT_SOURCE_PATH", ';' },
2444#endif
2445 { NULL, NULL, 0 }
2446 };
2447 for (unsigned i = 0; aNativePaths[i].pList; i++)
2448 {
2449 Assert(aNativePaths[i].chSep == ';'); /* fix when needed */
2450 rc = RTEnvGetEx(RTENV_DEFAULT, aNativePaths[i].pszVar, pszEnvVal, cbEnvVal, NULL);
2451 if (RT_SUCCESS(rc))
2452 {
2453 rc = rtDbgCfgChangeStringList(pThis, RTDBGCFGOP_APPEND, pszEnvVal, true, aNativePaths[i].pList);
2454 if (RT_FAILURE(rc))
2455 break;
2456 }
2457 else if (rc != VERR_ENV_VAR_NOT_FOUND)
2458 break;
2459 else
2460 rc = VINF_SUCCESS;
2461 }
2462 }
2463 RTMemTmpFree(pszEnvVar);
2464 }
2465 else
2466 rc = VERR_NO_TMP_MEMORY;
2467 if (RT_FAILURE(rc))
2468 {
2469 /*
2470 * Error, bail out.
2471 */
2472 RTDbgCfgRelease(pThis);
2473 return rc;
2474 }
2475 }
2476
2477 /*
2478 * Returns successfully.
2479 */
2480 *phDbgCfg = pThis;
2481
2482 return VINF_SUCCESS;
2483}
2484
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