diff options
Diffstat (limited to 'src/VBox/VMM/VMMR3/CFGM.cpp')
-rw-r--r-- | src/VBox/VMM/VMMR3/CFGM.cpp | 3428 |
1 files changed, 3428 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMR3/CFGM.cpp b/src/VBox/VMM/VMMR3/CFGM.cpp new file mode 100644 index 00000000..71492b16 --- /dev/null +++ b/src/VBox/VMM/VMMR3/CFGM.cpp @@ -0,0 +1,3428 @@ +/* $Id: CFGM.cpp $ */ +/** @file + * CFGM - Configuration Manager. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +/** @page pg_cfgm CFGM - The Configuration Manager + * + * The configuration manager is a directory containing the VM configuration at + * run time. It works in a manner similar to the windows registry - it's like a + * file system hierarchy, but the files (values) live in a separate name space + * and can include the path separators. + * + * The configuration is normally created via a callback passed to VMR3Create() + * via the pfnCFGMConstructor parameter. To make testcase writing a bit simpler, + * we allow the callback to be NULL, in which case a simple default + * configuration will be created by CFGMR3ConstructDefaultTree(). The + * Console::configConstructor() method in Main/ConsoleImpl2.cpp creates the + * configuration from the XML. + * + * Devices, drivers, services and other PDM stuff are given their own subtree + * where they are protected from accessing information of any parents. This is + * is implemented via the CFGMR3SetRestrictedRoot() API. + * + * Data validation beyond the basic primitives is left to the caller. The caller + * is in a better position to know the proper validation rules of the individual + * properties. + * + * @see grp_cfgm + * + * + * @section sec_cfgm_primitives Data Primitives + * + * CFGM supports the following data primitives: + * - Integers. Representation is unsigned 64-bit. Boolean, unsigned and + * small integers, and pointers are all represented using this primitive. + * - Zero terminated character strings. These are of course UTF-8. + * - Variable length byte strings. This can be used to get/put binary + * objects like for instance RTMAC. + * + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_CFGM +#include <VBox/vmm/cfgm.h> +#include <VBox/vmm/dbgf.h> +#include <VBox/vmm/mm.h> +#include <VBox/vmm/vmm.h> +#include "CFGMInternal.h" +#include <VBox/vmm/vm.h> +#include <VBox/vmm/uvm.h> +#include <VBox/err.h> + +#include <VBox/log.h> +#include <iprt/assert.h> +#include <iprt/mem.h> +#include <iprt/memsafer.h> +#include <iprt/param.h> +#include <iprt/string.h> +#include <iprt/utf16.h> +#include <iprt/uuid.h> + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static void cfgmR3DumpPath(PCFGMNODE pNode, PCDBGFINFOHLP pHlp); +static void cfgmR3Dump(PCFGMNODE pRoot, unsigned iLevel, PCDBGFINFOHLP pHlp); +static DECLCALLBACK(void) cfgmR3Info(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs); +static int cfgmR3ResolveNode(PCFGMNODE pNode, const char *pszPath, PCFGMNODE *ppChild); +static int cfgmR3ResolveLeaf(PCFGMNODE pNode, const char *pszName, PCFGMLEAF *ppLeaf); +static int cfgmR3InsertLeaf(PCFGMNODE pNode, const char *pszName, PCFGMLEAF *ppLeaf); +static void cfgmR3RemoveLeaf(PCFGMNODE pNode, PCFGMLEAF pLeaf); +static void cfgmR3FreeValue(PVM pVM, PCFGMLEAF pLeaf); + + +/** @todo replace pVM for pUVM !*/ + +/** + * Allocator wrapper. + * + * @returns Pointer to the allocated memory, NULL on failure. + * @param pVM The cross context VM structure, if the tree + * is associated with one. + * @param enmTag The allocation tag. + * @param cb The size of the allocation. + */ +static void *cfgmR3MemAlloc(PVM pVM, MMTAG enmTag, size_t cb) +{ + if (pVM) + return MMR3HeapAlloc(pVM, enmTag, cb); + return RTMemAlloc(cb); +} + + +/** + * Free wrapper. + * + * @param pVM The cross context VM structure, if the tree + * is associated with one. + * @param pv The memory block to free. + */ +static void cfgmR3MemFree(PVM pVM, void *pv) +{ + if (pVM) + MMR3HeapFree(pv); + else + RTMemFree(pv); +} + + +/** + * String allocator wrapper. + * + * @returns Pointer to the allocated memory, NULL on failure. + * @param pVM The cross context VM structure, if the tree + * is associated with one. + * @param enmTag The allocation tag. + * @param cbString The size of the allocation, terminator included. + */ +static char *cfgmR3StrAlloc(PVM pVM, MMTAG enmTag, size_t cbString) +{ + if (pVM) + return (char *)MMR3HeapAlloc(pVM, enmTag, cbString); + return (char *)RTStrAlloc(cbString); +} + + +/** + * String free wrapper. + * + * @param pVM The cross context VM structure, if the tree + * is associated with one. + * @param pszString The memory block to free. + */ +static void cfgmR3StrFree(PVM pVM, char *pszString) +{ + if (pVM) + MMR3HeapFree(pszString); + else + RTStrFree(pszString); +} + + +/** + * Frees one node, leaving any children or leaves to the caller. + * + * @param pNode The node structure to free. + */ +static void cfgmR3FreeNodeOnly(PCFGMNODE pNode) +{ + pNode->pFirstLeaf = NULL; + pNode->pFirstChild = NULL; + pNode->pNext = NULL; + pNode->pPrev = NULL; + if (!pNode->pVM) + RTMemFree(pNode); + else + { + pNode->pVM = NULL; + MMR3HeapFree(pNode); + } +} + + + + +/** + * Constructs the configuration for the VM. + * + * This should only be called used once. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @param pfnCFGMConstructor Pointer to callback function for constructing + * the VM configuration tree. This is called on + * the EMT. + * @param pvUser The user argument passed to pfnCFGMConstructor. + * @thread EMT. + * @internal + */ +VMMR3DECL(int) CFGMR3Init(PVM pVM, PFNCFGMCONSTRUCTOR pfnCFGMConstructor, void *pvUser) +{ + LogFlow(("CFGMR3Init: pfnCFGMConstructor=%p pvUser=%p\n", pfnCFGMConstructor, pvUser)); + + /* + * Init data members. + */ + pVM->cfgm.s.pRoot = NULL; + + /* + * Register DBGF into item. + */ + int rc = DBGFR3InfoRegisterInternal(pVM, "cfgm", "Dumps a part of the CFGM tree. The argument indicates where to start.", + cfgmR3Info); + AssertRCReturn(rc,rc); + + /* + * Root Node. + */ + PCFGMNODE pRoot = (PCFGMNODE)MMR3HeapAllocZ(pVM, MM_TAG_CFGM, sizeof(*pRoot)); + if (!pRoot) + return VERR_NO_MEMORY; + pRoot->pVM = pVM; + pRoot->cchName = 0; + pVM->cfgm.s.pRoot = pRoot; + + /* + * Call the constructor if specified, if not use the default one. + */ + if (pfnCFGMConstructor) + rc = pfnCFGMConstructor(pVM->pUVM, pVM, VMMR3GetVTable(), pvUser); + else + rc = CFGMR3ConstructDefaultTree(pVM); + if (RT_SUCCESS(rc)) + { + Log(("CFGMR3Init: Successfully constructed the configuration\n")); + CFGMR3Dump(CFGMR3GetRoot(pVM)); + } + else + LogRel(("Constructor failed with rc=%Rrc pfnCFGMConstructor=%p\n", rc, pfnCFGMConstructor)); + + return rc; +} + + +/** + * Terminates the configuration manager. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @internal + */ +VMMR3DECL(int) CFGMR3Term(PVM pVM) +{ + CFGMR3RemoveNode(pVM->cfgm.s.pRoot); + pVM->cfgm.s.pRoot = NULL; + return 0; +} + + +/** + * Gets the root node for the VM. + * + * @returns Pointer to root node. + * @param pVM The cross context VM structure. + */ +VMMR3DECL(PCFGMNODE) CFGMR3GetRoot(PVM pVM) +{ + return pVM->cfgm.s.pRoot; +} + + +/** + * Gets the root node for the VM. + * + * @returns Pointer to root node. + * @param pUVM The user mode VM structure. + */ +VMMR3DECL(PCFGMNODE) CFGMR3GetRootU(PUVM pUVM) +{ + UVM_ASSERT_VALID_EXT_RETURN(pUVM, NULL); + PVM pVM = pUVM->pVM; + AssertReturn(pVM, NULL); + return pVM->cfgm.s.pRoot; +} + + +/** + * Gets the parent of a CFGM node. + * + * @returns Pointer to the parent node. + * @returns NULL if pNode is Root or pNode is the start of a + * restricted subtree (use CFGMR3GetParentEx() for that). + * + * @param pNode The node which parent we query. + */ +VMMR3DECL(PCFGMNODE) CFGMR3GetParent(PCFGMNODE pNode) +{ + if (pNode && !pNode->fRestrictedRoot) + return pNode->pParent; + return NULL; +} + + +/** + * Gets the parent of a CFGM node. + * + * @returns Pointer to the parent node. + * @returns NULL if pNode is Root or pVM is not correct. + * + * @param pVM The cross context VM structure. Used as token that + * the caller is trusted. + * @param pNode The node which parent we query. + */ +VMMR3DECL(PCFGMNODE) CFGMR3GetParentEx(PVM pVM, PCFGMNODE pNode) +{ + if (pNode && pNode->pVM == pVM) + return pNode->pParent; + return NULL; +} + + +/** + * Query a child node. + * + * @returns Pointer to the specified node. + * @returns NULL if node was not found or pNode is NULL. + * @param pNode Node pszPath is relative to. + * @param pszPath Path to the child node or pNode. + * It's good style to end this with '/'. + */ +VMMR3DECL(PCFGMNODE) CFGMR3GetChild(PCFGMNODE pNode, const char *pszPath) +{ + PCFGMNODE pChild; + int rc = cfgmR3ResolveNode(pNode, pszPath, &pChild); + if (RT_SUCCESS(rc)) + return pChild; + return NULL; +} + + +/** + * Query a child node by a format string. + * + * @returns Pointer to the specified node. + * @returns NULL if node was not found or pNode is NULL. + * @param pNode Node pszPath is relative to. + * @param pszPathFormat Path to the child node or pNode. + * It's good style to end this with '/'. + * @param ... Arguments to pszPathFormat. + */ +VMMR3DECL(PCFGMNODE) CFGMR3GetChildF(PCFGMNODE pNode, const char *pszPathFormat, ...) +{ + va_list Args; + va_start(Args, pszPathFormat); + PCFGMNODE pRet = CFGMR3GetChildFV(pNode, pszPathFormat, Args); + va_end(Args); + return pRet; +} + + +/** + * Query a child node by a format string. + * + * @returns Pointer to the specified node. + * @returns NULL if node was not found or pNode is NULL. + * @param pNode Node pszPath is relative to. + * @param pszPathFormat Path to the child node or pNode. + * It's good style to end this with '/'. + * @param Args Arguments to pszPathFormat. + */ +VMMR3DECL(PCFGMNODE) CFGMR3GetChildFV(PCFGMNODE pNode, const char *pszPathFormat, va_list Args) +{ + char *pszPath; + RTStrAPrintfV(&pszPath, pszPathFormat, Args); + if (pszPath) + { + PCFGMNODE pChild; + int rc = cfgmR3ResolveNode(pNode, pszPath, &pChild); + RTStrFree(pszPath); + if (RT_SUCCESS(rc)) + return pChild; + } + return NULL; +} + + +/** + * Gets the first child node. + * Use this to start an enumeration of child nodes. + * + * @returns Pointer to the first child. + * @returns NULL if no children. + * @param pNode Node to enumerate children for. + */ +VMMR3DECL(PCFGMNODE) CFGMR3GetFirstChild(PCFGMNODE pNode) +{ + return pNode ? pNode->pFirstChild : NULL; +} + + +/** + * Gets the next sibling node. + * Use this to continue an enumeration. + * + * @returns Pointer to the first child. + * @returns NULL if no children. + * @param pCur Node to returned by a call to CFGMR3GetFirstChild() + * or successive calls to this function. + */ +VMMR3DECL(PCFGMNODE) CFGMR3GetNextChild(PCFGMNODE pCur) +{ + return pCur ? pCur->pNext : NULL; +} + + +/** + * Gets the name of the current node. + * (Needed for enumeration.) + * + * @returns VBox status code. + * @param pCur Node to returned by a call to CFGMR3GetFirstChild() + * or successive calls to CFGMR3GetNextChild(). + * @param pszName Where to store the node name. + * @param cchName Size of the buffer pointed to by pszName (with terminator). + */ +VMMR3DECL(int) CFGMR3GetName(PCFGMNODE pCur, char *pszName, size_t cchName) +{ + int rc; + if (pCur) + { + if (cchName > pCur->cchName) + { + rc = VINF_SUCCESS; + memcpy(pszName, pCur->szName, pCur->cchName + 1); + } + else + rc = VERR_CFGM_NOT_ENOUGH_SPACE; + } + else + rc = VERR_CFGM_NO_NODE; + return rc; +} + + +/** + * Gets the length of the current node's name. + * (Needed for enumeration.) + * + * @returns Node name length in bytes including the terminating null char. + * @returns 0 if pCur is NULL. + * @param pCur Node to returned by a call to CFGMR3GetFirstChild() + * or successive calls to CFGMR3GetNextChild(). + */ +VMMR3DECL(size_t) CFGMR3GetNameLen(PCFGMNODE pCur) +{ + return pCur ? pCur->cchName + 1 : 0; +} + + +/** + * Validates that the child nodes are within a set of valid names. + * + * @returns true if all names are found in pszzAllowed. + * @returns false if not. + * @param pNode The node which children should be examined. + * @param pszzValid List of valid names separated by '\\0' and ending with + * a double '\\0'. + * + * @deprecated Use CFGMR3ValidateConfig. + */ +VMMR3DECL(bool) CFGMR3AreChildrenValid(PCFGMNODE pNode, const char *pszzValid) +{ + if (pNode) + { + for (PCFGMNODE pChild = pNode->pFirstChild; pChild; pChild = pChild->pNext) + { + /* search pszzValid for the name */ + const char *psz = pszzValid; + while (*psz) + { + size_t cch = strlen(psz); + if ( cch == pChild->cchName + && !memcmp(psz, pChild->szName, cch)) + break; + + /* next */ + psz += cch + 1; + } + + /* if at end of pszzValid we didn't find it => failure */ + if (!*psz) + { + AssertMsgFailed(("Couldn't find '%s' in the valid values\n", pChild->szName)); + return false; + } + } + } + + /* all ok. */ + return true; +} + + +/** + * Gets the first value of a node. + * Use this to start an enumeration of values. + * + * @returns Pointer to the first value. + * @param pCur The node (Key) which values to enumerate. + */ +VMMR3DECL(PCFGMLEAF) CFGMR3GetFirstValue(PCFGMNODE pCur) +{ + return pCur ? pCur->pFirstLeaf : NULL; +} + +/** + * Gets the next value in enumeration. + * + * @returns Pointer to the next value. + * @param pCur The current value as returned by this function or CFGMR3GetFirstValue(). + */ +VMMR3DECL(PCFGMLEAF) CFGMR3GetNextValue(PCFGMLEAF pCur) +{ + return pCur ? pCur->pNext : NULL; +} + +/** + * Get the value name. + * (Needed for enumeration.) + * + * @returns VBox status code. + * @param pCur Value returned by a call to CFGMR3GetFirstValue() + * or successive calls to CFGMR3GetNextValue(). + * @param pszName Where to store the value name. + * @param cchName Size of the buffer pointed to by pszName (with terminator). + */ +VMMR3DECL(int) CFGMR3GetValueName(PCFGMLEAF pCur, char *pszName, size_t cchName) +{ + int rc; + if (pCur) + { + if (cchName > pCur->cchName) + { + rc = VINF_SUCCESS; + memcpy(pszName, pCur->szName, pCur->cchName + 1); + } + else + rc = VERR_CFGM_NOT_ENOUGH_SPACE; + } + else + rc = VERR_CFGM_NO_NODE; + return rc; +} + + +/** + * Gets the length of the current node's name. + * (Needed for enumeration.) + * + * @returns Value name length in bytes including the terminating null char. + * @returns 0 if pCur is NULL. + * @param pCur Value returned by a call to CFGMR3GetFirstValue() + * or successive calls to CFGMR3GetNextValue(). + */ +VMMR3DECL(size_t) CFGMR3GetValueNameLen(PCFGMLEAF pCur) +{ + return pCur ? pCur->cchName + 1 : 0; +} + + +/** + * Gets the value type. + * (For enumeration.) + * + * @returns VBox status code. + * @param pCur Value returned by a call to CFGMR3GetFirstValue() + * or successive calls to CFGMR3GetNextValue(). + */ +VMMR3DECL(CFGMVALUETYPE) CFGMR3GetValueType(PCFGMLEAF pCur) +{ + Assert(pCur); + return pCur->enmType; +} + + +/** + * Validates that the values are within a set of valid names. + * + * @returns true if all names are found in pszzValid. + * @returns false if not. + * @param pNode The node which values should be examined. + * @param pszzValid List of valid names separated by '\\0' and ending with + * a double '\\0'. + * @deprecated Use CFGMR3ValidateConfig. + */ +VMMR3DECL(bool) CFGMR3AreValuesValid(PCFGMNODE pNode, const char *pszzValid) +{ + if (pNode) + { + for (PCFGMLEAF pLeaf = pNode->pFirstLeaf; pLeaf; pLeaf = pLeaf->pNext) + { + /* search pszzValid for the name */ + const char *psz = pszzValid; + while (*psz) + { + size_t cch = strlen(psz); + if ( cch == pLeaf->cchName + && !memcmp(psz, pLeaf->szName, cch)) + break; + + /* next */ + psz += cch + 1; + } + + /* if at end of pszzValid we didn't find it => failure */ + if (!*psz) + { + AssertMsgFailed(("Couldn't find '%s' in the valid values\n", pLeaf->szName)); + return false; + } + } + } + + /* all ok. */ + return true; +} + + +/** + * Checks if the given value exists. + * + * @returns true if it exists, false if not. + * @param pNode Which node to search for pszName in. + * @param pszName The name of the value we seek. + */ +VMMR3DECL(bool) CFGMR3Exists(PCFGMNODE pNode, const char *pszName) +{ + PCFGMLEAF pLeaf; + int rc = cfgmR3ResolveLeaf(pNode, pszName, &pLeaf); + return RT_SUCCESS_NP(rc); +} + + +/** + * Query value type. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param penmType Where to store the type. + */ +VMMR3DECL(int) CFGMR3QueryType(PCFGMNODE pNode, const char *pszName, PCFGMVALUETYPE penmType) +{ + PCFGMLEAF pLeaf; + int rc = cfgmR3ResolveLeaf(pNode, pszName, &pLeaf); + if (RT_SUCCESS(rc)) + { + if (penmType) + *penmType = pLeaf->enmType; + } + return rc; +} + + +/** + * Query value size. + * This works on all types of values. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pcb Where to store the value size. + */ +VMMR3DECL(int) CFGMR3QuerySize(PCFGMNODE pNode, const char *pszName, size_t *pcb) +{ + PCFGMLEAF pLeaf; + int rc = cfgmR3ResolveLeaf(pNode, pszName, &pLeaf); + if (RT_SUCCESS(rc)) + { + switch (pLeaf->enmType) + { + case CFGMVALUETYPE_INTEGER: + *pcb = sizeof(pLeaf->Value.Integer.u64); + break; + + case CFGMVALUETYPE_STRING: + case CFGMVALUETYPE_PASSWORD: + *pcb = pLeaf->Value.String.cb; + break; + + case CFGMVALUETYPE_BYTES: + *pcb = pLeaf->Value.Bytes.cb; + break; + + default: + rc = VERR_CFGM_IPE_1; + AssertMsgFailed(("Invalid value type %d\n", pLeaf->enmType)); + break; + } + } + return rc; +} + + +/** + * Query integer value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pu64 Where to store the integer value. + */ +VMMR3DECL(int) CFGMR3QueryInteger(PCFGMNODE pNode, const char *pszName, uint64_t *pu64) +{ + PCFGMLEAF pLeaf; + int rc = cfgmR3ResolveLeaf(pNode, pszName, &pLeaf); + if (RT_SUCCESS(rc)) + { + if (pLeaf->enmType == CFGMVALUETYPE_INTEGER) + *pu64 = pLeaf->Value.Integer.u64; + else + rc = VERR_CFGM_NOT_INTEGER; + } + return rc; +} + + +/** + * Query integer value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pu64 Where to store the integer value. This is set to the default on failure. + * @param u64Def The default value. This is always set. + */ +VMMR3DECL(int) CFGMR3QueryIntegerDef(PCFGMNODE pNode, const char *pszName, uint64_t *pu64, uint64_t u64Def) +{ + PCFGMLEAF pLeaf; + int rc = cfgmR3ResolveLeaf(pNode, pszName, &pLeaf); + if (RT_SUCCESS(rc)) + { + if (pLeaf->enmType == CFGMVALUETYPE_INTEGER) + *pu64 = pLeaf->Value.Integer.u64; + else + rc = VERR_CFGM_NOT_INTEGER; + } + + if (RT_FAILURE(rc)) + { + *pu64 = u64Def; + if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT) + rc = VINF_SUCCESS; + } + + return rc; +} + + +/** + * Query zero terminated character value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of a zero terminate character value. + * @param pszString Where to store the string. + * @param cchString Size of the string buffer. (Includes terminator.) + */ +VMMR3DECL(int) CFGMR3QueryString(PCFGMNODE pNode, const char *pszName, char *pszString, size_t cchString) +{ + PCFGMLEAF pLeaf; + int rc = cfgmR3ResolveLeaf(pNode, pszName, &pLeaf); + if (RT_SUCCESS(rc)) + { + if (pLeaf->enmType == CFGMVALUETYPE_STRING) + { + size_t cbSrc = pLeaf->Value.String.cb; + if (cchString >= cbSrc) + { + memcpy(pszString, pLeaf->Value.String.psz, cbSrc); + memset(pszString + cbSrc, 0, cchString - cbSrc); + } + else + rc = VERR_CFGM_NOT_ENOUGH_SPACE; + } + else + rc = VERR_CFGM_NOT_STRING; + } + return rc; +} + + +/** + * Query zero terminated character value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of a zero terminate character value. + * @param pszString Where to store the string. This will not be set on overflow error. + * @param cchString Size of the string buffer. (Includes terminator.) + * @param pszDef The default value. + */ +VMMR3DECL(int) CFGMR3QueryStringDef(PCFGMNODE pNode, const char *pszName, char *pszString, size_t cchString, const char *pszDef) +{ + PCFGMLEAF pLeaf; + int rc = cfgmR3ResolveLeaf(pNode, pszName, &pLeaf); + if (RT_SUCCESS(rc)) + { + if (pLeaf->enmType == CFGMVALUETYPE_STRING) + { + size_t cbSrc = pLeaf->Value.String.cb; + if (cchString >= cbSrc) + { + memcpy(pszString, pLeaf->Value.String.psz, cbSrc); + memset(pszString + cbSrc, 0, cchString - cbSrc); + } + else + rc = VERR_CFGM_NOT_ENOUGH_SPACE; + } + else + rc = VERR_CFGM_NOT_STRING; + } + + if (RT_FAILURE(rc) && rc != VERR_CFGM_NOT_ENOUGH_SPACE) + { + size_t cchDef = strlen(pszDef); + if (cchString > cchDef) + { + memcpy(pszString, pszDef, cchDef); + memset(pszString + cchDef, 0, cchString - cchDef); + if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT) + rc = VINF_SUCCESS; + } + else if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT) + rc = VERR_CFGM_NOT_ENOUGH_SPACE; + } + + return rc; +} + + +/** + * Query byte string value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of a byte string value. + * @param pvData Where to store the binary data. + * @param cbData Size of buffer pvData points too. + */ +VMMR3DECL(int) CFGMR3QueryBytes(PCFGMNODE pNode, const char *pszName, void *pvData, size_t cbData) +{ + PCFGMLEAF pLeaf; + int rc = cfgmR3ResolveLeaf(pNode, pszName, &pLeaf); + if (RT_SUCCESS(rc)) + { + if (pLeaf->enmType == CFGMVALUETYPE_BYTES) + { + if (cbData >= pLeaf->Value.Bytes.cb) + { + memcpy(pvData, pLeaf->Value.Bytes.pau8, pLeaf->Value.Bytes.cb); + memset((char *)pvData + pLeaf->Value.Bytes.cb, 0, cbData - pLeaf->Value.Bytes.cb); + } + else + rc = VERR_CFGM_NOT_ENOUGH_SPACE; + } + else + rc = VERR_CFGM_NOT_BYTES; + } + return rc; +} + + +/** + * Query zero terminated character value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of a zero terminate character value. + * @param pszString Where to store the string. + * @param cchString Size of the string buffer. (Includes terminator.) + * + * @note Concurrent calls to this function and CFGMR3QueryPasswordDef are not + * supported. + */ +VMMR3DECL(int) CFGMR3QueryPassword(PCFGMNODE pNode, const char *pszName, char *pszString, size_t cchString) +{ + PCFGMLEAF pLeaf; + int rc = cfgmR3ResolveLeaf(pNode, pszName, &pLeaf); + if (RT_SUCCESS(rc)) + { + if (pLeaf->enmType == CFGMVALUETYPE_PASSWORD) + { + size_t cbSrc = pLeaf->Value.String.cb; + if (cchString >= cbSrc) + { + RTMemSaferUnscramble(pLeaf->Value.String.psz, cbSrc); + memcpy(pszString, pLeaf->Value.String.psz, cbSrc); + memset(pszString + cbSrc, 0, cchString - cbSrc); + RTMemSaferScramble(pLeaf->Value.String.psz, cbSrc); + + Assert(pszString[cbSrc - 1] == '\0'); + } + else + rc = VERR_CFGM_NOT_ENOUGH_SPACE; + } + else + rc = VERR_CFGM_NOT_PASSWORD; + } + return rc; +} + + +/** + * Query zero terminated character value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of a zero terminate character value. + * @param pszString Where to store the string. This will not be set on overflow error. + * @param cchString Size of the string buffer. (Includes terminator.) + * @param pszDef The default value. + * + * @note Concurrent calls to this function and CFGMR3QueryPassword are not + * supported. + */ +VMMR3DECL(int) CFGMR3QueryPasswordDef(PCFGMNODE pNode, const char *pszName, char *pszString, size_t cchString, const char *pszDef) +{ + PCFGMLEAF pLeaf; + int rc = cfgmR3ResolveLeaf(pNode, pszName, &pLeaf); + if (RT_SUCCESS(rc)) + { + if (pLeaf->enmType == CFGMVALUETYPE_PASSWORD) + { + size_t cbSrc = pLeaf->Value.String.cb; + if (cchString >= cbSrc) + { + RTMemSaferUnscramble(pLeaf->Value.String.psz, cbSrc); + memcpy(pszString, pLeaf->Value.String.psz, cbSrc); + memset(pszString + cbSrc, 0, cchString - cbSrc); + RTMemSaferScramble(pLeaf->Value.String.psz, cbSrc); + + Assert(pszString[cbSrc - 1] == '\0'); + } + else + rc = VERR_CFGM_NOT_ENOUGH_SPACE; + } + else + rc = VERR_CFGM_NOT_PASSWORD; + } + + if (RT_FAILURE(rc) && rc != VERR_CFGM_NOT_ENOUGH_SPACE) + { + size_t cchDef = strlen(pszDef); + if (cchString > cchDef) + { + memcpy(pszString, pszDef, cchDef); + memset(pszString + cchDef, 0, cchString - cchDef); + if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT) + rc = VINF_SUCCESS; + } + else if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT) + rc = VERR_CFGM_NOT_ENOUGH_SPACE; + } + + return rc; +} + + +/** + * Validate one level of a configuration node. + * + * This replaces the CFGMR3AreChildrenValid and CFGMR3AreValuesValid APIs. + * + * @returns VBox status code. + * + * When an error is returned, both VMSetError and AssertLogRelMsgFailed + * have been called. So, all the caller needs to do is to propagate + * the error status up to PDM. + * + * @param pNode The node to validate. + * @param pszNode The node path, always ends with a slash. Use + * "/" for the root config node. + * @param pszValidValues Patterns describing the valid value names. See + * RTStrSimplePatternMultiMatch for details on the + * pattern syntax. + * @param pszValidNodes Patterns describing the valid node (key) names. + * See RTStrSimplePatternMultiMatch for details on + * the pattern syntax. + * @param pszWho Who is calling. + * @param uInstance The instance number of the caller. + */ +VMMR3DECL(int) CFGMR3ValidateConfig(PCFGMNODE pNode, const char *pszNode, + const char *pszValidValues, const char *pszValidNodes, + const char *pszWho, uint32_t uInstance) +{ + /* Input validation. */ + AssertPtrNullReturn(pNode, VERR_INVALID_POINTER); + AssertPtrReturn(pszNode, VERR_INVALID_POINTER); + Assert(*pszNode && pszNode[strlen(pszNode) - 1] == '/'); + AssertPtrReturn(pszValidValues, VERR_INVALID_POINTER); + AssertPtrReturn(pszValidNodes, VERR_INVALID_POINTER); + AssertPtrReturn(pszWho, VERR_INVALID_POINTER); + + if (pNode) + { + /* + * Enumerate the leaves and check them against pszValidValues. + */ + for (PCFGMLEAF pLeaf = pNode->pFirstLeaf; pLeaf; pLeaf = pLeaf->pNext) + { + if (!RTStrSimplePatternMultiMatch(pszValidValues, RTSTR_MAX, + pLeaf->szName, pLeaf->cchName, + NULL)) + { + AssertLogRelMsgFailed(("%s/%u: Value '%s%s' didn't match '%s'\n", + pszWho, uInstance, pszNode, pLeaf->szName, pszValidValues)); + return VMSetError(pNode->pVM, VERR_CFGM_CONFIG_UNKNOWN_VALUE, RT_SRC_POS, + N_("Unknown configuration value '%s%s' found in the configuration of %s instance #%u"), + pszNode, pLeaf->szName, pszWho, uInstance); + } + + } + + /* + * Enumerate the child nodes and check them against pszValidNodes. + */ + for (PCFGMNODE pChild = pNode->pFirstChild; pChild; pChild = pChild->pNext) + { + if (!RTStrSimplePatternMultiMatch(pszValidNodes, RTSTR_MAX, + pChild->szName, pChild->cchName, + NULL)) + { + AssertLogRelMsgFailed(("%s/%u: Node '%s%s' didn't match '%s'\n", + pszWho, uInstance, pszNode, pChild->szName, pszValidNodes)); + return VMSetError(pNode->pVM, VERR_CFGM_CONFIG_UNKNOWN_NODE, RT_SRC_POS, + N_("Unknown configuration node '%s%s' found in the configuration of %s instance #%u"), + pszNode, pChild->szName, pszWho, uInstance); + } + } + } + + /* All is well. */ + return VINF_SUCCESS; +} + + + +/** + * Populates the CFGM tree with the default configuration. + * + * This assumes an empty tree and is intended for testcases and such that only + * need to do very small adjustments to the config. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @internal + */ +VMMR3DECL(int) CFGMR3ConstructDefaultTree(PVM pVM) +{ + int rc; + int rcAll = VINF_SUCCESS; +#define UPDATERC() do { if (RT_FAILURE(rc) && RT_SUCCESS(rcAll)) rcAll = rc; } while (0) + + PCFGMNODE pRoot = CFGMR3GetRoot(pVM); + AssertReturn(pRoot, VERR_WRONG_ORDER); + + /* + * Create VM default values. + */ + rc = CFGMR3InsertString(pRoot, "Name", "Default VM"); + UPDATERC(); + rc = CFGMR3InsertInteger(pRoot, "RamSize", 128U * _1M); + UPDATERC(); + rc = CFGMR3InsertInteger(pRoot, "RamHoleSize", 512U * _1M); + UPDATERC(); + rc = CFGMR3InsertInteger(pRoot, "TimerMillies", 10); + UPDATERC(); + + /* + * HM. + */ + PCFGMNODE pHm; + rc = CFGMR3InsertNode(pRoot, "HM", &pHm); + UPDATERC(); + rc = CFGMR3InsertInteger(pHm, "FallbackToIEM", 1); /* boolean */ + UPDATERC(); + + + /* + * PDM. + */ + PCFGMNODE pPdm; + rc = CFGMR3InsertNode(pRoot, "PDM", &pPdm); + UPDATERC(); + PCFGMNODE pDevices = NULL; + rc = CFGMR3InsertNode(pPdm, "Devices", &pDevices); + UPDATERC(); + rc = CFGMR3InsertInteger(pDevices, "LoadBuiltin", 1); /* boolean */ + UPDATERC(); + PCFGMNODE pDrivers = NULL; + rc = CFGMR3InsertNode(pPdm, "Drivers", &pDrivers); + UPDATERC(); + rc = CFGMR3InsertInteger(pDrivers, "LoadBuiltin", 1); /* boolean */ + UPDATERC(); + + + /* + * Devices + */ + pDevices = NULL; + rc = CFGMR3InsertNode(pRoot, "Devices", &pDevices); + UPDATERC(); + /* device */ + PCFGMNODE pDev = NULL; + PCFGMNODE pInst = NULL; + PCFGMNODE pCfg = NULL; +#if 0 + PCFGMNODE pLunL0 = NULL; + PCFGMNODE pLunL1 = NULL; +#endif + + /* + * PC Arch. + */ + rc = CFGMR3InsertNode(pDevices, "pcarch", &pDev); + UPDATERC(); + rc = CFGMR3InsertNode(pDev, "0", &pInst); + UPDATERC(); + rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ + UPDATERC(); + rc = CFGMR3InsertNode(pInst, "Config", &pCfg); + UPDATERC(); + + /* + * PC Bios. + */ + rc = CFGMR3InsertNode(pDevices, "pcbios", &pDev); + UPDATERC(); + rc = CFGMR3InsertNode(pDev, "0", &pInst); + UPDATERC(); + rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ + UPDATERC(); + rc = CFGMR3InsertNode(pInst, "Config", &pCfg); + UPDATERC(); + rc = CFGMR3InsertString(pCfg, "BootDevice0", "IDE"); + UPDATERC(); + rc = CFGMR3InsertString(pCfg, "BootDevice1", "NONE"); + UPDATERC(); + rc = CFGMR3InsertString(pCfg, "BootDevice2", "NONE"); + UPDATERC(); + rc = CFGMR3InsertString(pCfg, "BootDevice3", "NONE"); + UPDATERC(); + rc = CFGMR3InsertString(pCfg, "HardDiskDevice", "piix3ide"); + UPDATERC(); + rc = CFGMR3InsertString(pCfg, "FloppyDevice", ""); + UPDATERC(); + RTUUID Uuid; + RTUuidClear(&Uuid); + rc = CFGMR3InsertBytes(pCfg, "UUID", &Uuid, sizeof(Uuid)); + UPDATERC(); + + /* + * PCI bus. + */ + rc = CFGMR3InsertNode(pDevices, "pci", &pDev); /* piix3 */ + UPDATERC(); + rc = CFGMR3InsertNode(pDev, "0", &pInst); + UPDATERC(); + rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ + UPDATERC(); + rc = CFGMR3InsertNode(pInst, "Config", &pCfg); + UPDATERC(); + + /* + * PS/2 keyboard & mouse + */ + rc = CFGMR3InsertNode(pDevices, "pckbd", &pDev); + UPDATERC(); + rc = CFGMR3InsertNode(pDev, "0", &pInst); + UPDATERC(); + rc = CFGMR3InsertNode(pInst, "Config", &pCfg); + UPDATERC(); +#if 0 + rc = CFGMR3InsertNode(pInst, "LUN#0", &pLunL0); + UPDATERC(); + rc = CFGMR3InsertString(pLunL0, "Driver", "KeyboardQueue"); + UPDATERC(); + rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); + UPDATERC(); + rc = CFGMR3InsertInteger(pCfg, "QueueSize", 64); + UPDATERC(); + rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1); + UPDATERC(); + rc = CFGMR3InsertString(pLunL1, "Driver", "MainKeyboard"); + UPDATERC(); + rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); + UPDATERC(); +#endif +#if 0 + rc = CFGMR3InsertNode(pInst, "LUN#1", &pLunL0); + UPDATERC(); + rc = CFGMR3InsertString(pLunL0, "Driver", "MouseQueue"); + UPDATERC(); + rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); + UPDATERC(); + rc = CFGMR3InsertInteger(pCfg, "QueueSize", 128); + UPDATERC(); + rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1); + UPDATERC(); + rc = CFGMR3InsertString(pLunL1, "Driver", "MainMouse"); + UPDATERC(); + rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); + UPDATERC(); +#endif + + /* + * i8254 Programmable Interval Timer And Dummy Speaker + */ + rc = CFGMR3InsertNode(pDevices, "i8254", &pDev); + UPDATERC(); + rc = CFGMR3InsertNode(pDev, "0", &pInst); + UPDATERC(); +#ifdef DEBUG + rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ + UPDATERC(); +#endif + rc = CFGMR3InsertNode(pInst, "Config", &pCfg); + UPDATERC(); + + /* + * i8259 Programmable Interrupt Controller. + */ + rc = CFGMR3InsertNode(pDevices, "i8259", &pDev); + UPDATERC(); + rc = CFGMR3InsertNode(pDev, "0", &pInst); + UPDATERC(); + rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ + UPDATERC(); + rc = CFGMR3InsertNode(pInst, "Config", &pCfg); + UPDATERC(); + + /* + * RTC MC146818. + */ + rc = CFGMR3InsertNode(pDevices, "mc146818", &pDev); + UPDATERC(); + rc = CFGMR3InsertNode(pDev, "0", &pInst); + UPDATERC(); + rc = CFGMR3InsertNode(pInst, "Config", &pCfg); + UPDATERC(); + + /* + * VGA. + */ + rc = CFGMR3InsertNode(pDevices, "vga", &pDev); + UPDATERC(); + rc = CFGMR3InsertNode(pDev, "0", &pInst); + UPDATERC(); + rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ + UPDATERC(); + rc = CFGMR3InsertNode(pInst, "Config", &pCfg); + UPDATERC(); + rc = CFGMR3InsertInteger(pCfg, "VRamSize", 4 * _1M); + UPDATERC(); + + /* Bios logo. */ + rc = CFGMR3InsertInteger(pCfg, "FadeIn", 1); + UPDATERC(); + rc = CFGMR3InsertInteger(pCfg, "FadeOut", 1); + UPDATERC(); + rc = CFGMR3InsertInteger(pCfg, "LogoTime", 0); + UPDATERC(); + rc = CFGMR3InsertString(pCfg, "LogoFile", ""); + UPDATERC(); + +#if 0 + rc = CFGMR3InsertNode(pInst, "LUN#0", &pLunL0); + UPDATERC(); + rc = CFGMR3InsertString(pLunL0, "Driver", "MainDisplay"); + UPDATERC(); +#endif + + /* + * IDE controller. + */ + rc = CFGMR3InsertNode(pDevices, "piix3ide", &pDev); /* piix3 */ + UPDATERC(); + rc = CFGMR3InsertNode(pDev, "0", &pInst); + UPDATERC(); + rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ + UPDATERC(); + rc = CFGMR3InsertNode(pInst, "Config", &pCfg); + UPDATERC(); + + /* + * VMMDev. + */ + rc = CFGMR3InsertNode(pDevices, "VMMDev", &pDev); + UPDATERC(); + rc = CFGMR3InsertNode(pDev, "0", &pInst); + UPDATERC(); + rc = CFGMR3InsertNode(pInst, "Config", &pCfg); + UPDATERC(); + rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ + UPDATERC(); + + + /* + * ... + */ + +#undef UPDATERC + return rcAll; +} + + + + +/** + * Resolves a path reference to a child node. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszPath Path to the child node. + * @param ppChild Where to store the pointer to the child node. + */ +static int cfgmR3ResolveNode(PCFGMNODE pNode, const char *pszPath, PCFGMNODE *ppChild) +{ + *ppChild = NULL; + if (!pNode) + return VERR_CFGM_NO_PARENT; + PCFGMNODE pChild = NULL; + for (;;) + { + /* skip leading slashes. */ + while (*pszPath == '/') + pszPath++; + + /* End of path? */ + if (!*pszPath) + { + if (!pChild) + return VERR_CFGM_INVALID_CHILD_PATH; + *ppChild = pChild; + return VINF_SUCCESS; + } + + /* find end of component. */ + const char *pszNext = strchr(pszPath, '/'); + if (!pszNext) + pszNext = strchr(pszPath, '\0'); + RTUINT cchName = pszNext - pszPath; + + /* search child list. */ + pChild = pNode->pFirstChild; + for ( ; pChild; pChild = pChild->pNext) + if (pChild->cchName == cchName) + { + int iDiff = memcmp(pszPath, pChild->szName, cchName); + if (iDiff <= 0) + { + if (iDiff != 0) + pChild = NULL; + break; + } + } + if (!pChild) + return VERR_CFGM_CHILD_NOT_FOUND; + + /* next iteration */ + pNode = pChild; + pszPath = pszNext; + } + + /* won't get here */ +} + + +/** + * Resolves a path reference to a child node. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of a byte string value. + * @param ppLeaf Where to store the pointer to the leaf node. + */ +static int cfgmR3ResolveLeaf(PCFGMNODE pNode, const char *pszName, PCFGMLEAF *ppLeaf) +{ + *ppLeaf = NULL; + if (!pNode) + return VERR_CFGM_NO_PARENT; + + size_t cchName = strlen(pszName); + PCFGMLEAF pLeaf = pNode->pFirstLeaf; + while (pLeaf) + { + if (cchName == pLeaf->cchName) + { + int iDiff = memcmp(pszName, pLeaf->szName, cchName); + if (iDiff <= 0) + { + if (iDiff != 0) + break; + *ppLeaf = pLeaf; + return VINF_SUCCESS; + } + } + + /* next */ + pLeaf = pLeaf->pNext; + } + return VERR_CFGM_VALUE_NOT_FOUND; +} + + + +/** + * Creates a CFGM tree. + * + * This is intended for creating device/driver configs can be + * passed around and later attached to the main tree in the + * correct location. + * + * @returns Pointer to the root node, NULL on error (out of memory or invalid + * VM handle). + * @param pUVM The user mode VM handle. For testcase (and other + * purposes, NULL can be used. However, the resulting + * tree cannot be inserted into a tree that has a + * non-NULL value. Using NULL can be usedful for + * testcases and similar, non VMM uses. + */ +VMMR3DECL(PCFGMNODE) CFGMR3CreateTree(PUVM pUVM) +{ + if (pUVM) + { + UVM_ASSERT_VALID_EXT_RETURN(pUVM, NULL); + VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, NULL); + } + + PCFGMNODE pNew; + if (pUVM) + pNew = (PCFGMNODE)MMR3HeapAllocU(pUVM, MM_TAG_CFGM, sizeof(*pNew)); + else + pNew = (PCFGMNODE)RTMemAlloc(sizeof(*pNew)); + if (pNew) + { + pNew->pPrev = NULL; + pNew->pNext = NULL; + pNew->pParent = NULL; + pNew->pFirstChild = NULL; + pNew->pFirstLeaf = NULL; + pNew->pVM = pUVM ? pUVM->pVM : NULL; + pNew->fRestrictedRoot = false; + pNew->cchName = 0; + pNew->szName[0] = 0; + } + return pNew; +} + + +/** + * Duplicates a CFGM sub-tree or a full tree. + * + * @returns Pointer to the root node. NULL if we run out of memory or the + * input parameter is NULL. + * @param pRoot The root of the tree to duplicate. + * @param ppCopy Where to return the root of the duplicate. + */ +VMMR3DECL(int) CFGMR3DuplicateSubTree(PCFGMNODE pRoot, PCFGMNODE *ppCopy) +{ + AssertPtrReturn(pRoot, VERR_INVALID_POINTER); + + /* + * Create a new tree. + */ + PCFGMNODE pNewRoot = CFGMR3CreateTree(pRoot->pVM ? pRoot->pVM->pUVM : NULL); + if (!pNewRoot) + return VERR_NO_MEMORY; + + /* + * Duplicate the content. + */ + int rc = VINF_SUCCESS; + PCFGMNODE pSrcCur = pRoot; + PCFGMNODE pDstCur = pNewRoot; + for (;;) + { + if ( !pDstCur->pFirstChild + && !pDstCur->pFirstLeaf) + { + /* + * Values first. + */ + /** @todo this isn't the most efficient way to do it. */ + for (PCFGMLEAF pLeaf = pSrcCur->pFirstLeaf; pLeaf && RT_SUCCESS(rc); pLeaf = pLeaf->pNext) + rc = CFGMR3InsertValue(pDstCur, pLeaf); + + /* + * Insert immediate child nodes. + */ + /** @todo this isn't the most efficient way to do it. */ + for (PCFGMNODE pChild = pSrcCur->pFirstChild; pChild && RT_SUCCESS(rc); pChild = pChild->pNext) + rc = CFGMR3InsertNode(pDstCur, pChild->szName, NULL); + + AssertLogRelRCBreak(rc); + } + + /* + * Deep copy of the children. + */ + if (pSrcCur->pFirstChild) + { + Assert(pDstCur->pFirstChild && !strcmp(pDstCur->pFirstChild->szName, pSrcCur->pFirstChild->szName)); + pSrcCur = pSrcCur->pFirstChild; + pDstCur = pDstCur->pFirstChild; + } + /* + * If it's the root node, we're done. + */ + else if (pSrcCur == pRoot) + break; + else + { + /* + * Upon reaching the end of a sibling list, we must ascend and + * resume the sibiling walk on an previous level. + */ + if (!pSrcCur->pNext) + { + do + { + pSrcCur = pSrcCur->pParent; + pDstCur = pDstCur->pParent; + } while (!pSrcCur->pNext && pSrcCur != pRoot); + if (pSrcCur == pRoot) + break; + } + + /* + * Next sibling. + */ + Assert(pDstCur->pNext && !strcmp(pDstCur->pNext->szName, pSrcCur->pNext->szName)); + pSrcCur = pSrcCur->pNext; + pDstCur = pDstCur->pNext; + } + } + + if (RT_FAILURE(rc)) + { + CFGMR3RemoveNode(pNewRoot); + return rc; + } + + *ppCopy = pNewRoot; + return VINF_SUCCESS; +} + + +/** + * Insert subtree. + * + * This function inserts (no duplication) a tree created by CFGMR3CreateTree() + * into the main tree. + * + * The root node of the inserted subtree will need to be reallocated, which + * effectually means that the passed in pSubTree handle becomes invalid + * upon successful return. Use the value returned in ppChild instead + * of pSubTree. + * + * @returns VBox status code. + * @returns VERR_CFGM_NODE_EXISTS if the final child node name component exists. + * @param pNode Parent node. + * @param pszName Name or path of the new child node. + * @param pSubTree The subtree to insert. Must be returned by CFGMR3CreateTree(). + * @param ppChild Where to store the address of the new child node. (optional) + */ +VMMR3DECL(int) CFGMR3InsertSubTree(PCFGMNODE pNode, const char *pszName, PCFGMNODE pSubTree, PCFGMNODE *ppChild) +{ + /* + * Validate input. + */ + AssertPtrReturn(pNode, VERR_INVALID_POINTER); + AssertPtrReturn(pSubTree, VERR_INVALID_POINTER); + AssertReturn(pNode != pSubTree, VERR_INVALID_PARAMETER); + AssertReturn(!pSubTree->pParent, VERR_INVALID_PARAMETER); + AssertReturn(pNode->pVM == pSubTree->pVM, VERR_INVALID_PARAMETER); + Assert(!pSubTree->pNext); + Assert(!pSubTree->pPrev); + + /* + * Use CFGMR3InsertNode to create a new node and then + * re-attach the children and leaves of the subtree to it. + */ + PCFGMNODE pNewChild; + int rc = CFGMR3InsertNode(pNode, pszName, &pNewChild); + if (RT_SUCCESS(rc)) + { + Assert(!pNewChild->pFirstChild); + Assert(!pNewChild->pFirstLeaf); + + pNewChild->pFirstChild = pSubTree->pFirstChild; + pNewChild->pFirstLeaf = pSubTree->pFirstLeaf; + for (PCFGMNODE pChild = pNewChild->pFirstChild; pChild; pChild = pChild->pNext) + pChild->pParent = pNewChild; + + if (ppChild) + *ppChild = pNewChild; + + /* free the old subtree root */ + cfgmR3FreeNodeOnly(pSubTree); + } + return rc; +} + + +/** + * Replaces a (sub-)tree with new one. + * + * This function removes the exiting (sub-)tree, completely freeing it in the + * process, and inserts (no duplication) the specified tree. The tree can + * either be created by CFGMR3CreateTree or CFGMR3DuplicateSubTree. + * + * @returns VBox status code. + * @param pRoot The sub-tree to replace. This node will remain valid + * after the call. + * @param pNewRoot The tree to replace @a pRoot with. This not will + * become invalid after a successful call. + */ +VMMR3DECL(int) CFGMR3ReplaceSubTree(PCFGMNODE pRoot, PCFGMNODE pNewRoot) +{ + /* + * Validate input. + */ + AssertPtrReturn(pRoot, VERR_INVALID_POINTER); + AssertPtrReturn(pNewRoot, VERR_INVALID_POINTER); + AssertReturn(pRoot != pNewRoot, VERR_INVALID_PARAMETER); + AssertReturn(!pNewRoot->pParent, VERR_INVALID_PARAMETER); + AssertReturn(pNewRoot->pVM == pRoot->pVM, VERR_INVALID_PARAMETER); + AssertReturn(!pNewRoot->pNext, VERR_INVALID_PARAMETER); + AssertReturn(!pNewRoot->pPrev, VERR_INVALID_PARAMETER); + + /* + * Free the current properties fo pRoot. + */ + while (pRoot->pFirstChild) + CFGMR3RemoveNode(pRoot->pFirstChild); + + while (pRoot->pFirstLeaf) + cfgmR3RemoveLeaf(pRoot, pRoot->pFirstLeaf); + + /* + * Copy all the properties from the new root to the current one. + */ + pRoot->pFirstLeaf = pNewRoot->pFirstLeaf; + pRoot->pFirstChild = pNewRoot->pFirstChild; + for (PCFGMNODE pChild = pRoot->pFirstChild; pChild; pChild = pChild->pNext) + pChild->pParent = pRoot; + + cfgmR3FreeNodeOnly(pNewRoot); + + return VINF_SUCCESS; +} + + +/** + * Copies all values and keys from one tree onto another. + * + * The flags control what happens to keys and values with the same name + * existing in both source and destination. + * + * @returns VBox status code. + * @param pDstTree The destination tree. + * @param pSrcTree The source tree. + * @param fFlags Copy flags, see CFGM_COPY_FLAGS_XXX. + */ +VMMR3DECL(int) CFGMR3CopyTree(PCFGMNODE pDstTree, PCFGMNODE pSrcTree, uint32_t fFlags) +{ + /* + * Input validation. + */ + AssertPtrReturn(pSrcTree, VERR_INVALID_POINTER); + AssertPtrReturn(pDstTree, VERR_INVALID_POINTER); + AssertReturn(pDstTree != pSrcTree, VERR_INVALID_PARAMETER); + AssertReturn(!(fFlags & ~(CFGM_COPY_FLAGS_VALUE_DISP_MASK | CFGM_COPY_FLAGS_KEY_DISP_MASK)), VERR_INVALID_PARAMETER); + AssertReturn( (fFlags & CFGM_COPY_FLAGS_VALUE_DISP_MASK) != CFGM_COPY_FLAGS_RESERVED_VALUE_DISP_0 + && (fFlags & CFGM_COPY_FLAGS_VALUE_DISP_MASK) != CFGM_COPY_FLAGS_RESERVED_VALUE_DISP_1, + VERR_INVALID_PARAMETER); + AssertReturn((fFlags & CFGM_COPY_FLAGS_KEY_DISP_MASK) != CFGM_COPY_FLAGS_RESERVED_KEY_DISP, + VERR_INVALID_PARAMETER); + + /* + * Copy the values. + */ + int rc; + for (PCFGMLEAF pValue = CFGMR3GetFirstValue(pSrcTree); pValue; pValue = CFGMR3GetNextValue(pValue)) + { + rc = CFGMR3InsertValue(pDstTree, pValue); + if (rc == VERR_CFGM_LEAF_EXISTS) + { + if ((fFlags & CFGM_COPY_FLAGS_VALUE_DISP_MASK) == CFGM_COPY_FLAGS_REPLACE_VALUES) + { + rc = CFGMR3RemoveValue(pDstTree, pValue->szName); + if (RT_FAILURE(rc)) + break; + rc = CFGMR3InsertValue(pDstTree, pValue); + } + else + rc = VINF_SUCCESS; + } + AssertRCReturn(rc, rc); + } + + /* + * Copy/merge the keys - merging results in recursion. + */ + for (PCFGMNODE pSrcChild = CFGMR3GetFirstChild(pSrcTree); pSrcChild; pSrcChild = CFGMR3GetNextChild(pSrcChild)) + { + PCFGMNODE pDstChild = CFGMR3GetChild(pDstTree, pSrcChild->szName); + if ( pDstChild + && (fFlags & CFGM_COPY_FLAGS_KEY_DISP_MASK) == CFGM_COPY_FLAGS_REPLACE_KEYS) + { + CFGMR3RemoveNode(pDstChild); + pDstChild = NULL; + } + if (!pDstChild) + { + PCFGMNODE pChildCopy; + rc = CFGMR3DuplicateSubTree(pSrcChild, &pChildCopy); + AssertRCReturn(rc, rc); + rc = CFGMR3InsertSubTree(pDstTree, pSrcChild->szName, pChildCopy, NULL); + AssertRCReturnStmt(rc, CFGMR3RemoveNode(pChildCopy), rc); + } + else if ((fFlags & CFGM_COPY_FLAGS_KEY_DISP_MASK) == CFGM_COPY_FLAGS_MERGE_KEYS) + { + rc = CFGMR3CopyTree(pDstChild, pSrcChild, fFlags); + AssertRCReturn(rc, rc); + } + } + + return VINF_SUCCESS; +} + + + +/** + * Compares two names. + * + * @returns Similar to memcpy. + * @param pszName1 The first name. + * @param cchName1 The length of the first name. + * @param pszName2 The second name. + * @param cchName2 The length of the second name. + */ +DECLINLINE(int) cfgmR3CompareNames(const char *pszName1, size_t cchName1, const char *pszName2, size_t cchName2) +{ + int iDiff; + if (cchName1 <= cchName2) + { + iDiff = memcmp(pszName1, pszName2, cchName1); + if (!iDiff && cchName1 < cchName2) + iDiff = -1; + } + else + { + iDiff = memcmp(pszName1, pszName2, cchName2); + if (!iDiff) + iDiff = 1; + } + return iDiff; +} + + +/** + * Insert a node. + * + * @returns VBox status code. + * @returns VERR_CFGM_NODE_EXISTS if the final child node name component exists. + * @param pNode Parent node. + * @param pszName Name or path of the new child node. + * @param ppChild Where to store the address of the new child node. (optional) + */ +VMMR3DECL(int) CFGMR3InsertNode(PCFGMNODE pNode, const char *pszName, PCFGMNODE *ppChild) +{ + int rc; + if (pNode) + { + /* + * If given a path we have to deal with it component by component. + */ + while (*pszName == '/') + pszName++; + if (strchr(pszName, '/')) + { + char *pszDup = RTStrDup(pszName); + if (pszDup) + { + char *psz = pszDup; + for (;;) + { + /* Terminate at '/' and find the next component. */ + char *pszNext = strchr(psz, '/'); + if (pszNext) + { + *pszNext++ = '\0'; + while (*pszNext == '/') + pszNext++; + if (*pszNext == '\0') + pszNext = NULL; + } + + /* does it exist? */ + PCFGMNODE pChild = CFGMR3GetChild(pNode, psz); + if (!pChild) + { + /* no, insert it */ + rc = CFGMR3InsertNode(pNode, psz, &pChild); + if (RT_FAILURE(rc)) + break; + if (!pszNext) + { + if (ppChild) + *ppChild = pChild; + break; + } + + } + /* if last component fail */ + else if (!pszNext) + { + rc = VERR_CFGM_NODE_EXISTS; + break; + } + + /* next */ + pNode = pChild; + psz = pszNext; + } + RTStrFree(pszDup); + } + else + rc = VERR_NO_TMP_MEMORY; + } + /* + * Not multicomponent, just make sure it's a non-zero name. + */ + else if (*pszName) + { + /* + * Check if already exists and find last node in chain. + */ + size_t cchName = strlen(pszName); + PCFGMNODE pPrev = NULL; + PCFGMNODE pNext = pNode->pFirstChild; + if (pNext) + { + for ( ; pNext; pPrev = pNext, pNext = pNext->pNext) + { + int iDiff = cfgmR3CompareNames(pszName, cchName, pNext->szName, pNext->cchName); + if (iDiff <= 0) + { + if (!iDiff) + return VERR_CFGM_NODE_EXISTS; + break; + } + } + } + + /* + * Allocate and init node. + */ + PCFGMNODE pNew = (PCFGMNODE)cfgmR3MemAlloc(pNode->pVM, MM_TAG_CFGM, sizeof(*pNew) + cchName); + if (pNew) + { + pNew->pParent = pNode; + pNew->pFirstChild = NULL; + pNew->pFirstLeaf = NULL; + pNew->pVM = pNode->pVM; + pNew->fRestrictedRoot = false; + pNew->cchName = cchName; + memcpy(pNew->szName, pszName, cchName + 1); + + /* + * Insert into child list. + */ + pNew->pPrev = pPrev; + if (pPrev) + pPrev->pNext = pNew; + else + pNode->pFirstChild = pNew; + pNew->pNext = pNext; + if (pNext) + pNext->pPrev = pNew; + + if (ppChild) + *ppChild = pNew; + rc = VINF_SUCCESS; + } + else + rc = VERR_NO_MEMORY; + } + else + { + rc = VERR_CFGM_INVALID_NODE_PATH; + AssertMsgFailed(("Invalid path %s\n", pszName)); + } + } + else + { + rc = VERR_CFGM_NO_PARENT; + AssertMsgFailed(("No parent! path %s\n", pszName)); + } + + return rc; +} + + +/** + * Insert a node, format string name. + * + * @returns VBox status code. + * @param pNode Parent node. + * @param ppChild Where to store the address of the new child node. (optional) + * @param pszNameFormat Name of or path the new child node. + * @param ... Name format arguments. + */ +VMMR3DECL(int) CFGMR3InsertNodeF(PCFGMNODE pNode, PCFGMNODE *ppChild, const char *pszNameFormat, ...) +{ + va_list Args; + va_start(Args, pszNameFormat); + int rc = CFGMR3InsertNodeFV(pNode, ppChild, pszNameFormat, Args); + va_end(Args); + return rc; +} + + +/** + * Insert a node, format string name. + * + * @returns VBox status code. + * @param pNode Parent node. + * @param ppChild Where to store the address of the new child node. (optional) + * @param pszNameFormat Name or path of the new child node. + * @param Args Name format arguments. + */ +VMMR3DECL(int) CFGMR3InsertNodeFV(PCFGMNODE pNode, PCFGMNODE *ppChild, const char *pszNameFormat, va_list Args) +{ + int rc; + char *pszName; + RTStrAPrintfV(&pszName, pszNameFormat, Args); + if (pszName) + { + rc = CFGMR3InsertNode(pNode, pszName, ppChild); + RTStrFree(pszName); + } + else + rc = VERR_NO_MEMORY; + return rc; +} + + +/** + * Marks the node as the root of a restricted subtree, i.e. the end of + * a CFGMR3GetParent() journey. + * + * @param pNode The node to mark. + */ +VMMR3DECL(void) CFGMR3SetRestrictedRoot(PCFGMNODE pNode) +{ + if (pNode) + pNode->fRestrictedRoot = true; +} + + +/** + * Insert a node. + * + * @returns VBox status code. + * @param pNode Parent node. + * @param pszName Name of the new child node. + * @param ppLeaf Where to store the new leaf. + * The caller must fill in the enmType and Value fields! + */ +static int cfgmR3InsertLeaf(PCFGMNODE pNode, const char *pszName, PCFGMLEAF *ppLeaf) +{ + int rc; + if (*pszName) + { + if (pNode) + { + /* + * Check if already exists and find last node in chain. + */ + size_t cchName = strlen(pszName); + PCFGMLEAF pPrev = NULL; + PCFGMLEAF pNext = pNode->pFirstLeaf; + if (pNext) + { + for ( ; pNext; pPrev = pNext, pNext = pNext->pNext) + { + int iDiff = cfgmR3CompareNames(pszName, cchName, pNext->szName, pNext->cchName); + if (iDiff <= 0) + { + if (!iDiff) + return VERR_CFGM_LEAF_EXISTS; + break; + } + } + } + + /* + * Allocate and init node. + */ + PCFGMLEAF pNew = (PCFGMLEAF)cfgmR3MemAlloc(pNode->pVM, MM_TAG_CFGM, sizeof(*pNew) + cchName); + if (pNew) + { + pNew->cchName = cchName; + memcpy(pNew->szName, pszName, cchName + 1); + + /* + * Insert into child list. + */ + pNew->pPrev = pPrev; + if (pPrev) + pPrev->pNext = pNew; + else + pNode->pFirstLeaf = pNew; + pNew->pNext = pNext; + if (pNext) + pNext->pPrev = pNew; + + *ppLeaf = pNew; + rc = VINF_SUCCESS; + } + else + rc = VERR_NO_MEMORY; + } + else + rc = VERR_CFGM_NO_PARENT; + } + else + rc = VERR_CFGM_INVALID_CHILD_PATH; + return rc; +} + + +/** + * Removes a node. + * + * @param pNode The node to remove. + */ +VMMR3DECL(void) CFGMR3RemoveNode(PCFGMNODE pNode) +{ + if (pNode) + { + /* + * Free children. + */ + while (pNode->pFirstChild) + CFGMR3RemoveNode(pNode->pFirstChild); + + /* + * Free leaves. + */ + while (pNode->pFirstLeaf) + cfgmR3RemoveLeaf(pNode, pNode->pFirstLeaf); + + /* + * Unlink ourselves. + */ + if (pNode->pPrev) + pNode->pPrev->pNext = pNode->pNext; + else + { + if (pNode->pParent) + pNode->pParent->pFirstChild = pNode->pNext; + else if ( pNode->pVM /* might be a different tree */ + && pNode == pNode->pVM->cfgm.s.pRoot) + pNode->pVM->cfgm.s.pRoot = NULL; + } + if (pNode->pNext) + pNode->pNext->pPrev = pNode->pPrev; + + /* + * Free ourselves. + */ + cfgmR3FreeNodeOnly(pNode); + } +} + + +/** + * Removes a leaf. + * + * @param pNode Parent node. + * @param pLeaf Leaf to remove. + */ +static void cfgmR3RemoveLeaf(PCFGMNODE pNode, PCFGMLEAF pLeaf) +{ + if (pNode && pLeaf) + { + /* + * Unlink. + */ + if (pLeaf->pPrev) + pLeaf->pPrev->pNext = pLeaf->pNext; + else + pNode->pFirstLeaf = pLeaf->pNext; + if (pLeaf->pNext) + pLeaf->pNext->pPrev = pLeaf->pPrev; + + /* + * Free value and node. + */ + cfgmR3FreeValue(pNode->pVM, pLeaf); + pLeaf->pNext = NULL; + pLeaf->pPrev = NULL; + cfgmR3MemFree(pNode->pVM, pLeaf); + } +} + + +/** + * Frees whatever resources the leaf value is owning. + * + * Use this before assigning a new value to a leaf. + * The caller must either free the leaf or assign a new value to it. + * + * @param pVM The cross context VM structure, if the tree + * is associated with one. + * @param pLeaf Pointer to the leaf which value should be free. + */ +static void cfgmR3FreeValue(PVM pVM, PCFGMLEAF pLeaf) +{ + if (pLeaf) + { + switch (pLeaf->enmType) + { + case CFGMVALUETYPE_BYTES: + cfgmR3MemFree(pVM, pLeaf->Value.Bytes.pau8); + pLeaf->Value.Bytes.pau8 = NULL; + pLeaf->Value.Bytes.cb = 0; + break; + + case CFGMVALUETYPE_STRING: + cfgmR3StrFree(pVM, pLeaf->Value.String.psz); + pLeaf->Value.String.psz = NULL; + pLeaf->Value.String.cb = 0; + break; + + case CFGMVALUETYPE_PASSWORD: + RTMemSaferFree(pLeaf->Value.String.psz, pLeaf->Value.String.cb); + pLeaf->Value.String.psz = NULL; + pLeaf->Value.String.cb = 0; + break; + + case CFGMVALUETYPE_INTEGER: + break; + } + pLeaf->enmType = (CFGMVALUETYPE)0; + } +} + +/** + * Destroys a tree created with CFGMR3CreateTree or CFGMR3DuplicateSubTree. + * + * @returns VBox status code. + * @param pRoot The root node of the tree. + */ +VMMR3DECL(int) CFGMR3DestroyTree(PCFGMNODE pRoot) +{ + if (!pRoot) + return VINF_SUCCESS; + AssertReturn(!pRoot->pParent, VERR_INVALID_PARAMETER); + AssertReturn(!pRoot->pVM || pRoot != pRoot->pVM->cfgm.s.pRoot, VERR_ACCESS_DENIED); + + CFGMR3RemoveNode(pRoot); + return VINF_SUCCESS; +} + + +/** + * Inserts a new integer value. + * + * @returns VBox status code. + * @param pNode Parent node. + * @param pszName Value name. + * @param u64Integer The value. + */ +VMMR3DECL(int) CFGMR3InsertInteger(PCFGMNODE pNode, const char *pszName, uint64_t u64Integer) +{ + PCFGMLEAF pLeaf; + int rc = cfgmR3InsertLeaf(pNode, pszName, &pLeaf); + if (RT_SUCCESS(rc)) + { + pLeaf->enmType = CFGMVALUETYPE_INTEGER; + pLeaf->Value.Integer.u64 = u64Integer; + } + return rc; +} + + +/** + * Inserts a new string value. + * + * This variant expects that the caller know the length of the string already so + * we can avoid calling strlen() here. + * + * @returns VBox status code. + * @param pNode Parent node. + * @param pszName Value name. + * @param pszString The value. Must not be NULL. + * @param cchString The length of the string excluding the + * terminator. + */ +VMMR3DECL(int) CFGMR3InsertStringN(PCFGMNODE pNode, const char *pszName, const char *pszString, size_t cchString) +{ + Assert(RTStrNLen(pszString, cchString) == cchString); + + int rc; + if (pNode) + { + /* + * Allocate string object first. + */ + char *pszStringCopy = (char *)cfgmR3StrAlloc(pNode->pVM, MM_TAG_CFGM_STRING, cchString + 1); + if (pszStringCopy) + { + memcpy(pszStringCopy, pszString, cchString); + pszStringCopy[cchString] = '\0'; + + /* + * Create value leaf and set it to string type. + */ + PCFGMLEAF pLeaf; + rc = cfgmR3InsertLeaf(pNode, pszName, &pLeaf); + if (RT_SUCCESS(rc)) + { + pLeaf->enmType = CFGMVALUETYPE_STRING; + pLeaf->Value.String.psz = pszStringCopy; + pLeaf->Value.String.cb = cchString + 1; + } + else + cfgmR3StrFree(pNode->pVM, pszStringCopy); + } + else + rc = VERR_NO_MEMORY; + } + else + rc = VERR_CFGM_NO_PARENT; + + return rc; +} + + +/** + * Inserts a new string value. + * + * Calls strlen(pszString) internally; if you know the length of the string, + * CFGMR3InsertStringLengthKnown() is faster. + * + * @returns VBox status code. + * @param pNode Parent node. + * @param pszName Value name. + * @param pszString The value. + */ +VMMR3DECL(int) CFGMR3InsertString(PCFGMNODE pNode, const char *pszName, const char *pszString) +{ + return CFGMR3InsertStringN(pNode, pszName, pszString, strlen(pszString)); +} + + +/** + * Same as CFGMR3InsertString except the string value given in RTStrPrintfV + * fashion. + * + * @returns VBox status code. + * @param pNode Parent node. + * @param pszName Value name. + * @param pszFormat The value given as a format string. + * @param va Argument to pszFormat. + */ +VMMR3DECL(int) CFGMR3InsertStringFV(PCFGMNODE pNode, const char *pszName, const char *pszFormat, va_list va) +{ + int rc; + if (pNode) + { + /* + * Allocate string object first. + */ + char *pszString; + if (!pNode->pVM) + pszString = RTStrAPrintf2(pszFormat, va); + else + pszString = MMR3HeapAPrintfVU(pNode->pVM->pUVM, MM_TAG_CFGM_STRING, pszFormat, va); + if (pszString) + { + /* + * Create value leaf and set it to string type. + */ + PCFGMLEAF pLeaf; + rc = cfgmR3InsertLeaf(pNode, pszName, &pLeaf); + if (RT_SUCCESS(rc)) + { + pLeaf->enmType = CFGMVALUETYPE_STRING; + pLeaf->Value.String.psz = pszString; + pLeaf->Value.String.cb = strlen(pszString) + 1; + } + else + cfgmR3StrFree(pNode->pVM, pszString); + } + else + rc = VERR_NO_MEMORY; + } + else + rc = VERR_CFGM_NO_PARENT; + + return rc; +} + + +/** + * Same as CFGMR3InsertString except the string value given in RTStrPrintf + * fashion. + * + * @returns VBox status code. + * @param pNode Parent node. + * @param pszName Value name. + * @param pszFormat The value given as a format string. + * @param ... Argument to pszFormat. + */ +VMMR3DECL(int) CFGMR3InsertStringF(PCFGMNODE pNode, const char *pszName, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + int rc = CFGMR3InsertStringFV(pNode, pszName, pszFormat, va); + va_end(va); + return rc; +} + + +/** + * Same as CFGMR3InsertString except the string value given as a UTF-16 string. + * + * @returns VBox status code. + * @param pNode Parent node. + * @param pszName Value name. + * @param pwszValue The string value (UTF-16). + */ +VMMR3DECL(int) CFGMR3InsertStringW(PCFGMNODE pNode, const char *pszName, PCRTUTF16 pwszValue) +{ + char *pszValue; + int rc = RTUtf16ToUtf8(pwszValue, &pszValue); + if (RT_SUCCESS(rc)) + { + rc = CFGMR3InsertString(pNode, pszName, pszValue); + RTStrFree(pszValue); + } + return rc; +} + + +/** + * Inserts a new bytes value. + * + * @returns VBox status code. + * @param pNode Parent node. + * @param pszName Value name. + * @param pvBytes The value. + * @param cbBytes The value size. + */ +VMMR3DECL(int) CFGMR3InsertBytes(PCFGMNODE pNode, const char *pszName, const void *pvBytes, size_t cbBytes) +{ + int rc; + if (pNode) + { + if (cbBytes == (RTUINT)cbBytes) + { + /* + * Allocate string object first. + */ + void *pvCopy = cfgmR3MemAlloc(pNode->pVM, MM_TAG_CFGM_STRING, cbBytes); + if (pvCopy || !cbBytes) + { + memcpy(pvCopy, pvBytes, cbBytes); + + /* + * Create value leaf and set it to string type. + */ + PCFGMLEAF pLeaf; + rc = cfgmR3InsertLeaf(pNode, pszName, &pLeaf); + if (RT_SUCCESS(rc)) + { + pLeaf->enmType = CFGMVALUETYPE_BYTES; + pLeaf->Value.Bytes.cb = cbBytes; + pLeaf->Value.Bytes.pau8 = (uint8_t *)pvCopy; + } + else + cfgmR3MemFree(pNode->pVM, pvCopy); + } + else + rc = VERR_NO_MEMORY; + } + else + rc = VERR_OUT_OF_RANGE; + } + else + rc = VERR_CFGM_NO_PARENT; + + return rc; +} + + +/** + * Inserts a new password value. + * + * This variant expects that the caller know the length of the password string + * already so we can avoid calling strlen() here. + * + * @returns VBox status code. + * @param pNode Parent node. + * @param pszName Value name. + * @param pszString The value. Must not be NULL. + * @param cchString The length of the string excluding the terminator. + */ +VMMR3DECL(int) CFGMR3InsertPasswordN(PCFGMNODE pNode, const char *pszName, const char *pszString, size_t cchString) +{ + Assert(RTStrNLen(pszString, cchString) == cchString); + + int rc; + if (pNode) + { + /* + * Allocate string object first using the safer memory API since this + * is considered sensitive information. + */ + char *pszStringCopy = (char *)RTMemSaferAllocZ(cchString + 1); + if (pszStringCopy) + { + memcpy(pszStringCopy, pszString, cchString); + pszStringCopy[cchString] = '\0'; + RTMemSaferScramble(pszStringCopy, cchString + 1); + + /* + * Create value leaf and set it to string type. + */ + PCFGMLEAF pLeaf; + rc = cfgmR3InsertLeaf(pNode, pszName, &pLeaf); + if (RT_SUCCESS(rc)) + { + pLeaf->enmType = CFGMVALUETYPE_PASSWORD; + pLeaf->Value.String.psz = pszStringCopy; + pLeaf->Value.String.cb = cchString + 1; + } + else + RTMemSaferFree(pszStringCopy, cchString + 1); + } + else + rc = VERR_NO_MEMORY; + } + else + rc = VERR_CFGM_NO_PARENT; + + return rc; +} + + +/** + * Inserts a new password value. + * + * Calls strlen(pszString) internally; if you know the length of the string, + * CFGMR3InsertStringLengthKnown() is faster. + * + * @returns VBox status code. + * @param pNode Parent node. + * @param pszName Value name. + * @param pszString The value. + */ +VMMR3DECL(int) CFGMR3InsertPassword(PCFGMNODE pNode, const char *pszName, const char *pszString) +{ + return CFGMR3InsertPasswordN(pNode, pszName, pszString, strlen(pszString)); +} + + +/** + * Make a copy of the specified value under the given node. + * + * @returns VBox status code. + * @param pNode Parent node. + * @param pValue The value to copy and insert. + */ +VMMR3DECL(int) CFGMR3InsertValue(PCFGMNODE pNode, PCFGMLEAF pValue) +{ + int rc; + switch (pValue->enmType) + { + case CFGMVALUETYPE_INTEGER: + rc = CFGMR3InsertInteger(pNode, pValue->szName, pValue->Value.Integer.u64); + break; + + case CFGMVALUETYPE_BYTES: + rc = CFGMR3InsertBytes(pNode, pValue->szName, pValue->Value.Bytes.pau8, pValue->Value.Bytes.cb); + break; + + case CFGMVALUETYPE_STRING: + rc = CFGMR3InsertStringN(pNode, pValue->szName, pValue->Value.String.psz, pValue->Value.String.cb - 1); + break; + + case CFGMVALUETYPE_PASSWORD: + rc = CFGMR3InsertPasswordN(pNode, pValue->szName, pValue->Value.String.psz, pValue->Value.String.cb - 1); + break; + + default: + rc = VERR_CFGM_IPE_1; + AssertMsgFailed(("Invalid value type %d\n", pValue->enmType)); + break; + } + return rc; +} + + +/** + * Remove a value. + * + * @returns VBox status code. + * @param pNode Parent node. + * @param pszName Name of the new child node. + */ +VMMR3DECL(int) CFGMR3RemoveValue(PCFGMNODE pNode, const char *pszName) +{ + PCFGMLEAF pLeaf; + int rc = cfgmR3ResolveLeaf(pNode, pszName, &pLeaf); + if (RT_SUCCESS(rc)) + cfgmR3RemoveLeaf(pNode, pLeaf); + return rc; +} + + + +/* + * -+- helper apis -+- + */ + + +/** + * Query unsigned 64-bit integer value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pu64 Where to store the integer value. + */ +VMMR3DECL(int) CFGMR3QueryU64(PCFGMNODE pNode, const char *pszName, uint64_t *pu64) +{ + return CFGMR3QueryInteger(pNode, pszName, pu64); +} + + +/** + * Query unsigned 64-bit integer value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pu64 Where to store the integer value. Set to default on failure. + * @param u64Def The default value. + */ +VMMR3DECL(int) CFGMR3QueryU64Def(PCFGMNODE pNode, const char *pszName, uint64_t *pu64, uint64_t u64Def) +{ + return CFGMR3QueryIntegerDef(pNode, pszName, pu64, u64Def); +} + + +/** + * Query signed 64-bit integer value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pi64 Where to store the value. + */ +VMMR3DECL(int) CFGMR3QueryS64(PCFGMNODE pNode, const char *pszName, int64_t *pi64) +{ + uint64_t u64; + int rc = CFGMR3QueryInteger(pNode, pszName, &u64); + if (RT_SUCCESS(rc)) + *pi64 = (int64_t)u64; + return rc; +} + + +/** + * Query signed 64-bit integer value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pi64 Where to store the value. Set to default on failure. + * @param i64Def The default value. + */ +VMMR3DECL(int) CFGMR3QueryS64Def(PCFGMNODE pNode, const char *pszName, int64_t *pi64, int64_t i64Def) +{ + uint64_t u64; + int rc = CFGMR3QueryIntegerDef(pNode, pszName, &u64, i64Def); + *pi64 = (int64_t)u64; + return rc; +} + + +/** + * Query unsigned 32-bit integer value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pu32 Where to store the value. + */ +VMMR3DECL(int) CFGMR3QueryU32(PCFGMNODE pNode, const char *pszName, uint32_t *pu32) +{ + uint64_t u64; + int rc = CFGMR3QueryInteger(pNode, pszName, &u64); + if (RT_SUCCESS(rc)) + { + if (!(u64 & UINT64_C(0xffffffff00000000))) + *pu32 = (uint32_t)u64; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + return rc; +} + + +/** + * Query unsigned 32-bit integer value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pu32 Where to store the value. Set to default on failure. + * @param u32Def The default value. + */ +VMMR3DECL(int) CFGMR3QueryU32Def(PCFGMNODE pNode, const char *pszName, uint32_t *pu32, uint32_t u32Def) +{ + uint64_t u64; + int rc = CFGMR3QueryIntegerDef(pNode, pszName, &u64, u32Def); + if (RT_SUCCESS(rc)) + { + if (!(u64 & UINT64_C(0xffffffff00000000))) + *pu32 = (uint32_t)u64; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + if (RT_FAILURE(rc)) + *pu32 = u32Def; + return rc; +} + + +/** + * Query signed 32-bit integer value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pi32 Where to store the value. + */ +VMMR3DECL(int) CFGMR3QueryS32(PCFGMNODE pNode, const char *pszName, int32_t *pi32) +{ + uint64_t u64; + int rc = CFGMR3QueryInteger(pNode, pszName, &u64); + if (RT_SUCCESS(rc)) + { + if ( !(u64 & UINT64_C(0xffffffff80000000)) + || (u64 & UINT64_C(0xffffffff80000000)) == UINT64_C(0xffffffff80000000)) + *pi32 = (int32_t)u64; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + return rc; +} + + +/** + * Query signed 32-bit integer value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pi32 Where to store the value. Set to default on failure. + * @param i32Def The default value. + */ +VMMR3DECL(int) CFGMR3QueryS32Def(PCFGMNODE pNode, const char *pszName, int32_t *pi32, int32_t i32Def) +{ + uint64_t u64; + int rc = CFGMR3QueryIntegerDef(pNode, pszName, &u64, i32Def); + if (RT_SUCCESS(rc)) + { + if ( !(u64 & UINT64_C(0xffffffff80000000)) + || (u64 & UINT64_C(0xffffffff80000000)) == UINT64_C(0xffffffff80000000)) + *pi32 = (int32_t)u64; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + if (RT_FAILURE(rc)) + *pi32 = i32Def; + return rc; +} + + +/** + * Query unsigned 16-bit integer value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pu16 Where to store the value. + */ +VMMR3DECL(int) CFGMR3QueryU16(PCFGMNODE pNode, const char *pszName, uint16_t *pu16) +{ + uint64_t u64; + int rc = CFGMR3QueryInteger(pNode, pszName, &u64); + if (RT_SUCCESS(rc)) + { + if (!(u64 & UINT64_C(0xffffffffffff0000))) + *pu16 = (int16_t)u64; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + return rc; +} + + +/** + * Query unsigned 16-bit integer value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pu16 Where to store the value. Set to default on failure. + * @param u16Def The default value. + */ +VMMR3DECL(int) CFGMR3QueryU16Def(PCFGMNODE pNode, const char *pszName, uint16_t *pu16, uint16_t u16Def) +{ + uint64_t u64; + int rc = CFGMR3QueryIntegerDef(pNode, pszName, &u64, u16Def); + if (RT_SUCCESS(rc)) + { + if (!(u64 & UINT64_C(0xffffffffffff0000))) + *pu16 = (int16_t)u64; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + if (RT_FAILURE(rc)) + *pu16 = u16Def; + return rc; +} + + +/** + * Query signed 16-bit integer value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pi16 Where to store the value. + */ +VMMR3DECL(int) CFGMR3QueryS16(PCFGMNODE pNode, const char *pszName, int16_t *pi16) +{ + uint64_t u64; + int rc = CFGMR3QueryInteger(pNode, pszName, &u64); + if (RT_SUCCESS(rc)) + { + if ( !(u64 & UINT64_C(0xffffffffffff8000)) + || (u64 & UINT64_C(0xffffffffffff8000)) == UINT64_C(0xffffffffffff8000)) + *pi16 = (int16_t)u64; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + return rc; +} + + +/** + * Query signed 16-bit integer value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pi16 Where to store the value. Set to default on failure. + * @param i16Def The default value. + */ +VMMR3DECL(int) CFGMR3QueryS16Def(PCFGMNODE pNode, const char *pszName, int16_t *pi16, int16_t i16Def) +{ + uint64_t u64; + int rc = CFGMR3QueryIntegerDef(pNode, pszName, &u64, i16Def); + if (RT_SUCCESS(rc)) + { + if ( !(u64 & UINT64_C(0xffffffffffff8000)) + || (u64 & UINT64_C(0xffffffffffff8000)) == UINT64_C(0xffffffffffff8000)) + *pi16 = (int16_t)u64; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + if (RT_FAILURE(rc)) + *pi16 = i16Def; + return rc; +} + + +/** + * Query unsigned 8-bit integer value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pu8 Where to store the value. + */ +VMMR3DECL(int) CFGMR3QueryU8(PCFGMNODE pNode, const char *pszName, uint8_t *pu8) +{ + uint64_t u64; + int rc = CFGMR3QueryInteger(pNode, pszName, &u64); + if (RT_SUCCESS(rc)) + { + if (!(u64 & UINT64_C(0xffffffffffffff00))) + *pu8 = (uint8_t)u64; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + return rc; +} + + +/** + * Query unsigned 8-bit integer value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pu8 Where to store the value. Set to default on failure. + * @param u8Def The default value. + */ +VMMR3DECL(int) CFGMR3QueryU8Def(PCFGMNODE pNode, const char *pszName, uint8_t *pu8, uint8_t u8Def) +{ + uint64_t u64; + int rc = CFGMR3QueryIntegerDef(pNode, pszName, &u64, u8Def); + if (RT_SUCCESS(rc)) + { + if (!(u64 & UINT64_C(0xffffffffffffff00))) + *pu8 = (uint8_t)u64; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + if (RT_FAILURE(rc)) + *pu8 = u8Def; + return rc; +} + + +/** + * Query signed 8-bit integer value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pi8 Where to store the value. + */ +VMMR3DECL(int) CFGMR3QueryS8(PCFGMNODE pNode, const char *pszName, int8_t *pi8) +{ + uint64_t u64; + int rc = CFGMR3QueryInteger(pNode, pszName, &u64); + if (RT_SUCCESS(rc)) + { + if ( !(u64 & UINT64_C(0xffffffffffffff80)) + || (u64 & UINT64_C(0xffffffffffffff80)) == UINT64_C(0xffffffffffffff80)) + *pi8 = (int8_t)u64; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + return rc; +} + + +/** + * Query signed 8-bit integer value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pi8 Where to store the value. Set to default on failure. + * @param i8Def The default value. + */ +VMMR3DECL(int) CFGMR3QueryS8Def(PCFGMNODE pNode, const char *pszName, int8_t *pi8, int8_t i8Def) +{ + uint64_t u64; + int rc = CFGMR3QueryIntegerDef(pNode, pszName, &u64, i8Def); + if (RT_SUCCESS(rc)) + { + if ( !(u64 & UINT64_C(0xffffffffffffff80)) + || (u64 & UINT64_C(0xffffffffffffff80)) == UINT64_C(0xffffffffffffff80)) + *pi8 = (int8_t)u64; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + if (RT_FAILURE(rc)) + *pi8 = i8Def; + return rc; +} + + +/** + * Query boolean integer value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pf Where to store the value. + * @remark This function will interpret any non-zero value as true. + */ +VMMR3DECL(int) CFGMR3QueryBool(PCFGMNODE pNode, const char *pszName, bool *pf) +{ + uint64_t u64; + int rc = CFGMR3QueryInteger(pNode, pszName, &u64); + if (RT_SUCCESS(rc)) + *pf = u64 ? true : false; + return rc; +} + + +/** + * Query boolean integer value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pf Where to store the value. Set to default on failure. + * @param fDef The default value. + * @remark This function will interpret any non-zero value as true. + */ +VMMR3DECL(int) CFGMR3QueryBoolDef(PCFGMNODE pNode, const char *pszName, bool *pf, bool fDef) +{ + uint64_t u64; + int rc = CFGMR3QueryIntegerDef(pNode, pszName, &u64, fDef); + *pf = u64 ? true : false; + return rc; +} + + +/** + * Query I/O port address value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pPort Where to store the value. + */ +VMMR3DECL(int) CFGMR3QueryPort(PCFGMNODE pNode, const char *pszName, PRTIOPORT pPort) +{ + AssertCompileSize(RTIOPORT, 2); + return CFGMR3QueryU16(pNode, pszName, pPort); +} + + +/** + * Query I/O port address value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pPort Where to store the value. Set to default on failure. + * @param PortDef The default value. + */ +VMMR3DECL(int) CFGMR3QueryPortDef(PCFGMNODE pNode, const char *pszName, PRTIOPORT pPort, RTIOPORT PortDef) +{ + AssertCompileSize(RTIOPORT, 2); + return CFGMR3QueryU16Def(pNode, pszName, pPort, PortDef); +} + + +/** + * Query unsigned int address value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pu Where to store the value. + */ +VMMR3DECL(int) CFGMR3QueryUInt(PCFGMNODE pNode, const char *pszName, unsigned int *pu) +{ + AssertCompileSize(unsigned int, 4); + return CFGMR3QueryU32(pNode, pszName, (uint32_t *)pu); +} + + +/** + * Query unsigned int address value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pu Where to store the value. Set to default on failure. + * @param uDef The default value. + */ +VMMR3DECL(int) CFGMR3QueryUIntDef(PCFGMNODE pNode, const char *pszName, unsigned int *pu, unsigned int uDef) +{ + AssertCompileSize(unsigned int, 4); + return CFGMR3QueryU32Def(pNode, pszName, (uint32_t *)pu, uDef); +} + + +/** + * Query signed int address value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pi Where to store the value. + */ +VMMR3DECL(int) CFGMR3QuerySInt(PCFGMNODE pNode, const char *pszName, signed int *pi) +{ + AssertCompileSize(signed int, 4); + return CFGMR3QueryS32(pNode, pszName, (int32_t *)pi); +} + + +/** + * Query unsigned int address value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pi Where to store the value. Set to default on failure. + * @param iDef The default value. + */ +VMMR3DECL(int) CFGMR3QuerySIntDef(PCFGMNODE pNode, const char *pszName, signed int *pi, signed int iDef) +{ + AssertCompileSize(signed int, 4); + return CFGMR3QueryS32Def(pNode, pszName, (int32_t *)pi, iDef); +} + + +/** + * Query Guest Context pointer integer value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pGCPtr Where to store the value. + */ +VMMR3DECL(int) CFGMR3QueryGCPtr(PCFGMNODE pNode, const char *pszName, PRTGCPTR pGCPtr) +{ + uint64_t u64; + int rc = CFGMR3QueryInteger(pNode, pszName, &u64); + if (RT_SUCCESS(rc)) + { + RTGCPTR u = (RTGCPTR)u64; + if (u64 == u) + *pGCPtr = u; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + return rc; +} + + +/** + * Query Guest Context pointer integer value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pGCPtr Where to store the value. Set to default on failure. + * @param GCPtrDef The default value. + */ +VMMR3DECL(int) CFGMR3QueryGCPtrDef(PCFGMNODE pNode, const char *pszName, PRTGCPTR pGCPtr, RTGCPTR GCPtrDef) +{ + uint64_t u64; + int rc = CFGMR3QueryIntegerDef(pNode, pszName, &u64, GCPtrDef); + if (RT_SUCCESS(rc)) + { + RTGCPTR u = (RTGCPTR)u64; + if (u64 == u) + *pGCPtr = u; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + if (RT_FAILURE(rc)) + *pGCPtr = GCPtrDef; + return rc; +} + + +/** + * Query Guest Context unsigned pointer value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pGCPtr Where to store the value. + */ +VMMR3DECL(int) CFGMR3QueryGCPtrU(PCFGMNODE pNode, const char *pszName, PRTGCUINTPTR pGCPtr) +{ + uint64_t u64; + int rc = CFGMR3QueryInteger(pNode, pszName, &u64); + if (RT_SUCCESS(rc)) + { + RTGCUINTPTR u = (RTGCUINTPTR)u64; + if (u64 == u) + *pGCPtr = u; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + return rc; +} + + +/** + * Query Guest Context unsigned pointer value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pGCPtr Where to store the value. Set to default on failure. + * @param GCPtrDef The default value. + */ +VMMR3DECL(int) CFGMR3QueryGCPtrUDef(PCFGMNODE pNode, const char *pszName, PRTGCUINTPTR pGCPtr, RTGCUINTPTR GCPtrDef) +{ + uint64_t u64; + int rc = CFGMR3QueryIntegerDef(pNode, pszName, &u64, GCPtrDef); + if (RT_SUCCESS(rc)) + { + RTGCUINTPTR u = (RTGCUINTPTR)u64; + if (u64 == u) + *pGCPtr = u; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + if (RT_FAILURE(rc)) + *pGCPtr = GCPtrDef; + return rc; +} + + +/** + * Query Guest Context signed pointer value. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pGCPtr Where to store the value. + */ +VMMR3DECL(int) CFGMR3QueryGCPtrS(PCFGMNODE pNode, const char *pszName, PRTGCINTPTR pGCPtr) +{ + uint64_t u64; + int rc = CFGMR3QueryInteger(pNode, pszName, &u64); + if (RT_SUCCESS(rc)) + { + RTGCINTPTR u = (RTGCINTPTR)u64; + if (u64 == (uint64_t)u) + *pGCPtr = u; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + return rc; +} + + +/** + * Query Guest Context signed pointer value with default. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Name of an integer value. + * @param pGCPtr Where to store the value. Set to default on failure. + * @param GCPtrDef The default value. + */ +VMMR3DECL(int) CFGMR3QueryGCPtrSDef(PCFGMNODE pNode, const char *pszName, PRTGCINTPTR pGCPtr, RTGCINTPTR GCPtrDef) +{ + uint64_t u64; + int rc = CFGMR3QueryIntegerDef(pNode, pszName, &u64, GCPtrDef); + if (RT_SUCCESS(rc)) + { + RTGCINTPTR u = (RTGCINTPTR)u64; + if (u64 == (uint64_t)u) + *pGCPtr = u; + else + rc = VERR_CFGM_INTEGER_TOO_BIG; + } + if (RT_FAILURE(rc)) + *pGCPtr = GCPtrDef; + return rc; +} + + +/** + * Query zero terminated character value storing it in a + * buffer allocated from the MM heap. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. + * @param pszName Value name. This value must be of zero terminated character string type. + * @param ppszString Where to store the string pointer. + * Free this using MMR3HeapFree() (or RTStrFree if not + * associated with a pUVM - see CFGMR3CreateTree). + */ +VMMR3DECL(int) CFGMR3QueryStringAlloc(PCFGMNODE pNode, const char *pszName, char **ppszString) +{ + size_t cbString; + int rc = CFGMR3QuerySize(pNode, pszName, &cbString); + if (RT_SUCCESS(rc)) + { + char *pszString = cfgmR3StrAlloc(pNode->pVM, MM_TAG_CFGM_USER, cbString); + if (pszString) + { + rc = CFGMR3QueryString(pNode, pszName, pszString, cbString); + if (RT_SUCCESS(rc)) + *ppszString = pszString; + else + cfgmR3StrFree(pNode->pVM, pszString); + } + else + rc = VERR_NO_MEMORY; + } + return rc; +} + + +/** + * Query zero terminated character value storing it in a + * buffer allocated from the MM heap. + * + * @returns VBox status code. + * @param pNode Which node to search for pszName in. This cannot be + * NULL if @a pszDef is not NULL, because we need + * somewhere way to get to the VM in order to call + * MMR3HeapStrDup. + * @param pszName Value name. This value must be of zero terminated character string type. + * @param ppszString Where to store the string pointer. Not set on failure. + * Free this using MMR3HeapFree() (or RTStrFree if not + * associated with a pUVM - see CFGMR3CreateTree). + * @param pszDef The default return value. This can be NULL. + */ +VMMR3DECL(int) CFGMR3QueryStringAllocDef(PCFGMNODE pNode, const char *pszName, char **ppszString, const char *pszDef) +{ + Assert(pNode || !pszDef); /* We need pVM if we need to duplicate the string later. */ + + /* + * (Don't call CFGMR3QuerySize and CFGMR3QueryStringDef here as the latter + * cannot handle pszDef being NULL.) + */ + PCFGMLEAF pLeaf; + int rc = cfgmR3ResolveLeaf(pNode, pszName, &pLeaf); + if (RT_SUCCESS(rc)) + { + if (pLeaf->enmType == CFGMVALUETYPE_STRING) + { + size_t const cbSrc = pLeaf->Value.String.cb; + char *pszString = cfgmR3StrAlloc(pNode->pVM, MM_TAG_CFGM_USER, cbSrc); + if (pszString) + { + memcpy(pszString, pLeaf->Value.String.psz, cbSrc); + *ppszString = pszString; + } + else + rc = VERR_NO_MEMORY; + } + else + rc = VERR_CFGM_NOT_STRING; + } + if (RT_FAILURE(rc)) + { + if (!pszDef) + *ppszString = NULL; + else + { + size_t const cbDef = strlen(pszDef) + 1; + *ppszString = cfgmR3StrAlloc(pNode->pVM, MM_TAG_CFGM_USER, cbDef); + memcpy(*ppszString, pszDef, cbDef); + } + if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT) + rc = VINF_SUCCESS; + } + + return rc; +} + + +/** + * Dumps the configuration (sub)tree to the release log. + * + * @param pRoot The root node of the dump. + */ +VMMR3DECL(void) CFGMR3Dump(PCFGMNODE pRoot) +{ + bool fOldBuffered = RTLogRelSetBuffering(true /*fBuffered*/); + LogRel(("************************* CFGM dump *************************\n")); + cfgmR3Dump(pRoot, 0, DBGFR3InfoLogRelHlp()); +#ifdef LOG_ENABLED + if (LogIsEnabled()) + cfgmR3Dump(pRoot, 0, DBGFR3InfoLogHlp()); +#endif + LogRel(("********************* End of CFGM dump **********************\n")); + RTLogRelSetBuffering(fOldBuffered); +} + + +/** + * Info handler, internal version. + * + * @param pVM The cross context VM structure. + * @param pHlp Callback functions for doing output. + * @param pszArgs Argument string. Optional and specific to the handler. + */ +static DECLCALLBACK(void) cfgmR3Info(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs) +{ + /* + * Figure where to start. + */ + PCFGMNODE pRoot = pVM->cfgm.s.pRoot; + if (pszArgs && *pszArgs) + { + int rc = cfgmR3ResolveNode(pRoot, pszArgs, &pRoot); + if (RT_FAILURE(rc)) + { + pHlp->pfnPrintf(pHlp, "Failed to resolve CFGM path '%s', %Rrc", pszArgs, rc); + return; + } + } + + /* + * Dump the specified tree. + */ + pHlp->pfnPrintf(pHlp, "pRoot=%p:{", pRoot); + cfgmR3DumpPath(pRoot, pHlp); + pHlp->pfnPrintf(pHlp, "}\n"); + cfgmR3Dump(pRoot, 0, pHlp); +} + + +/** + * Recursively prints a path name. + */ +static void cfgmR3DumpPath(PCFGMNODE pNode, PCDBGFINFOHLP pHlp) +{ + if (pNode->pParent) + cfgmR3DumpPath(pNode->pParent, pHlp); + pHlp->pfnPrintf(pHlp, "%s/", pNode->szName); +} + + +/** + * Dumps a branch of a tree. + */ +static void cfgmR3Dump(PCFGMNODE pRoot, unsigned iLevel, PCDBGFINFOHLP pHlp) +{ + /* + * Path. + */ + pHlp->pfnPrintf(pHlp, "["); + cfgmR3DumpPath(pRoot, pHlp); + pHlp->pfnPrintf(pHlp, "] (level %d)%s\n", iLevel, pRoot->fRestrictedRoot ? " (restricted root)" : ""); + + /* + * Values. + */ + PCFGMLEAF pLeaf; + size_t cchMax = 0; + for (pLeaf = CFGMR3GetFirstValue(pRoot); pLeaf; pLeaf = CFGMR3GetNextValue(pLeaf)) + cchMax = RT_MAX(cchMax, pLeaf->cchName); + for (pLeaf = CFGMR3GetFirstValue(pRoot); pLeaf; pLeaf = CFGMR3GetNextValue(pLeaf)) + { + switch (CFGMR3GetValueType(pLeaf)) + { + case CFGMVALUETYPE_INTEGER: + { + pHlp->pfnPrintf(pHlp, " %-*s <integer> = %#018llx (%'lld", + (int)cchMax, pLeaf->szName, pLeaf->Value.Integer.u64, pLeaf->Value.Integer.u64); + if ( ( pLeaf->cchName >= 4 + && !RTStrCmp(&pLeaf->szName[pLeaf->cchName - 4], "Size")) + || ( pLeaf->cchName >= 2 + && !RTStrNCmp(pLeaf->szName, "cb", 2)) ) + pHlp->pfnPrintf(pHlp, ", %' Rhcb)\n", pLeaf->Value.Integer.u64); + else + pHlp->pfnPrintf(pHlp, ")\n"); + break; + } + + case CFGMVALUETYPE_STRING: + pHlp->pfnPrintf(pHlp, " %-*s <string> = \"%s\" (cb=%zu)\n", + (int)cchMax, pLeaf->szName, pLeaf->Value.String.psz, pLeaf->Value.String.cb); + break; + + case CFGMVALUETYPE_BYTES: + pHlp->pfnPrintf(pHlp, " %-*s <bytes> = \"%.*Rhxs\" (cb=%zu)\n", + (int)cchMax, pLeaf->szName, pLeaf->Value.Bytes.cb, pLeaf->Value.Bytes.pau8, pLeaf->Value.Bytes.cb); + break; + + case CFGMVALUETYPE_PASSWORD: + pHlp->pfnPrintf(pHlp, " %-*s <password>= \"***REDACTED***\" (cb=%zu)\n", + (int)cchMax, pLeaf->szName, pLeaf->Value.String.cb); + break; + + default: + AssertMsgFailed(("bad leaf!\n")); + break; + } + } + pHlp->pfnPrintf(pHlp, "\n"); + + /* + * Children. + */ + for (PCFGMNODE pChild = CFGMR3GetFirstChild(pRoot); pChild; pChild = CFGMR3GetNextChild(pChild)) + { + Assert(pChild->pNext != pChild); + Assert(pChild->pPrev != pChild); + Assert(pChild->pPrev != pChild->pNext || !pChild->pPrev); + Assert(pChild->pFirstChild != pChild); + Assert(pChild->pParent == pRoot); + cfgmR3Dump(pChild, iLevel + 1, pHlp); + } +} + |