VirtualBox

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

Last change on this file since 72778 was 72264, checked in by vboxsync, 7 years ago

IPRT/rtDbgCfgTryDownloadAndOpen: Support non-url servers, like UNC.

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