diff options
Diffstat (limited to 'src/VBox/Main/xml')
-rw-r--r-- | src/VBox/Main/xml/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/Main/xml/SchemaDefs.xsl | 246 | ||||
-rw-r--r-- | src/VBox/Main/xml/Settings.cpp | 9719 | ||||
-rw-r--r-- | src/VBox/Main/xml/VirtualBox-settings.xsd | 1514 | ||||
-rw-r--r-- | src/VBox/Main/xml/ovfreader.cpp | 1078 | ||||
-rw-r--r-- | src/VBox/Main/xml/samples/VirtualBox-global.xml | 62 | ||||
-rw-r--r-- | src/VBox/Main/xml/samples/VirtualBox-machine-linux.xml | 65 | ||||
-rw-r--r-- | src/VBox/Main/xml/samples/VirtualBox-machine-windows.xml | 203 |
8 files changed, 12887 insertions, 0 deletions
diff --git a/src/VBox/Main/xml/Makefile.kup b/src/VBox/Main/xml/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/Main/xml/Makefile.kup diff --git a/src/VBox/Main/xml/SchemaDefs.xsl b/src/VBox/Main/xml/SchemaDefs.xsl new file mode 100644 index 00000000..abf80a20 --- /dev/null +++ b/src/VBox/Main/xml/SchemaDefs.xsl @@ -0,0 +1,246 @@ +<?xml version="1.0"?> + +<!-- + * A template to generate a header that will contain some important constraints + * extracted from the VirtualBox XML Schema (VirtualBox-settings-*.xsd). + * The output file name must be SchemaDefs.h. + * + * This template depends on XML Schema structure (type names and constraints) + * and should be reviewed on every Schema change. +--> +<!-- + Copyright (C) 2006-2022 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 +--> + +<xsl:stylesheet version="1.0" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:xsd="http://www.w3.org/2001/XMLSchema" +> +<xsl:output method="text"/> + +<xsl:strip-space elements="*"/> + +<xsl:param name="mode" expr=''/> + +<!-- +// helpers +//////////////////////////////////////////////////////////////////////////////// +--> + +<!-- + * Extract the specified value and assign it to an enum member with the given + * name +--> +<xsl:template name="defineEnumMember"> + <xsl:param name="member"/> + <xsl:param name="select"/> + <xsl:if test="$select"> + <xsl:value-of select="concat(' ', $member, ' = ', $select, ',
')"/> + </xsl:if> +</xsl:template> + +<!-- +// templates +//////////////////////////////////////////////////////////////////////////////// +--> + +<!-- + * shut down all implicit templates +--> +<xsl:template match="*"/> +<xsl:template match="*" mode="declare"/> +<xsl:template match="*" mode="declare.enum"/> +<xsl:template match="*" mode="define"/> + +<xsl:template match="/"> + <xsl:choose> + <xsl:when test="$mode='declare'"> + <xsl:apply-templates select="/" mode="declare"/> + </xsl:when> + <xsl:when test="$mode='define'"> + <xsl:apply-templates select="/" mode="define"/> + </xsl:when> + <xsl:otherwise> + <xsl:message terminate="yes"> +Value '<xsl:value-of select="$mode"/>' of parameter 'mode' is invalid! + </xsl:message> + </xsl:otherwise> + </xsl:choose> +</xsl:template> + +<!-- + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * declare mode (C++ header file) + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +--> + +<xsl:template match="/" mode="declare"> +<xsl:text> +/* + * DO NOT EDIT. + * + * This header is automatically generated from the VirtualBox XML Settings + * Schema and contains selected schema constraints declared in C++. + */ + +#ifndef ____H_SCHEMADEFS +#define ____H_SCHEMADEFS + +namespace SchemaDefs +{ + enum + { +</xsl:text> + + <xsl:apply-templates select="xsd:schema" mode="declare.enum"/> + +<xsl:text> DummyTerminator + }; +</xsl:text> + +<xsl:apply-templates select="xsd:schema" mode="declare"/> + +<xsl:text>} + +#endif // !____H_SCHEMADEFS +</xsl:text> +</xsl:template> + +<!-- + * enumeration values +--> +<xsl:template match="xsd:schema" mode="declare.enum"> + + <!-- process include statements --> + <xsl:for-each select="xsd:include"> + <xsl:apply-templates select="document(@schemaLocation)/xsd:schema" mode="declare.enum"/> + </xsl:for-each> + + <xsl:call-template name="defineEnumMember"> + <xsl:with-param name="member" select="'MinGuestRAM'"/> + <xsl:with-param name="select" select=" + xsd:complexType[@name='TMemory']/xsd:attribute[@name='RAMSize']//xsd:minInclusive/@value + "/> + </xsl:call-template> + <xsl:call-template name="defineEnumMember"> + <xsl:with-param name="member" select="'MaxGuestRAM'"/> + <xsl:with-param name="select" select=" + xsd:complexType[@name='TMemory']/xsd:attribute[@name='RAMSize']//xsd:maxInclusive/@value + "/> + </xsl:call-template> + + <xsl:call-template name="defineEnumMember"> + <xsl:with-param name="member" select="'MinGuestVRAM'"/> + <xsl:with-param name="select" select=" + xsd:complexType[@name='TDisplay']/xsd:attribute[@name='VRAMSize']//xsd:minInclusive/@value + "/> + </xsl:call-template> + <xsl:call-template name="defineEnumMember"> + <xsl:with-param name="member" select="'MaxGuestVRAM'"/> + <xsl:with-param name="select" select=" + xsd:complexType[@name='TDisplay']/xsd:attribute[@name='VRAMSize']//xsd:maxInclusive/@value + "/> + </xsl:call-template> + + <xsl:call-template name="defineEnumMember"> + <xsl:with-param name="member" select="'MinCPUCount'"/> + <xsl:with-param name="select" select=" + xsd:simpleType[@name='TCPUCount']//xsd:minInclusive/@value + "/> + </xsl:call-template> + <xsl:call-template name="defineEnumMember"> + <xsl:with-param name="member" select="'MaxCPUCount'"/> + <xsl:with-param name="select" select=" + xsd:simpleType[@name='TCPUCount']//xsd:maxInclusive/@value + "/> + </xsl:call-template> + + <xsl:call-template name="defineEnumMember"> + <xsl:with-param name="member" select="'MaxGuestMonitors'"/> + <xsl:with-param name="select" select=" + xsd:simpleType[@name='TMonitorCount']//xsd:maxInclusive/@value + "/> + </xsl:call-template> + + <xsl:call-template name="defineEnumMember"> + <xsl:with-param name="member" select="'SerialPortCount'"/> + <xsl:with-param name="select" select=" + xsd:complexType[@name='TUARTPort']/xsd:attribute[@name='slot']//xsd:maxExclusive/@value + "/> + </xsl:call-template> + + <xsl:call-template name="defineEnumMember"> + <xsl:with-param name="member" select="'ParallelPortCount'"/> + <xsl:with-param name="select" select=" + xsd:complexType[@name='TLPTPort']/xsd:attribute[@name='slot']//xsd:maxExclusive/@value + "/> + </xsl:call-template> + + <xsl:call-template name="defineEnumMember"> + <xsl:with-param name="member" select="'MaxBootPosition'"/> + <xsl:with-param name="select" select=" + xsd:complexType[@name='TBoot']//xsd:element[@name='Order']//xsd:attribute[@name='position']//xsd:maxInclusive/@value + "/> + </xsl:call-template> + + <xsl:call-template name="defineEnumMember"> + <xsl:with-param name="member" select="'DefaultHardwareVersion'"/> + <xsl:with-param name="select" select=" + xsd:complexType[@name='THardware']/xsd:attribute[@name='version']/@default + "/> + </xsl:call-template> + +</xsl:template> + +<!-- + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * define mode (C++ source file) + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +--> + +<xsl:template match="/" mode="define"> +<xsl:text> +/* + * DO NOT EDIT. + * + * This source is automatically generated from the VirtualBox XML Settings + * Schema and contains selected schema constraints defined in C++. + */ + +#include "SchemaDefs.h" + +namespace SchemaDefs +{ +</xsl:text> + +<xsl:apply-templates select="xsd:schema" mode="define"/> + +<xsl:text>} +</xsl:text> +</xsl:template> + +<!-- + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * END + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +--> + +</xsl:stylesheet> diff --git a/src/VBox/Main/xml/Settings.cpp b/src/VBox/Main/xml/Settings.cpp new file mode 100644 index 00000000..046ae0d7 --- /dev/null +++ b/src/VBox/Main/xml/Settings.cpp @@ -0,0 +1,9719 @@ +/* $Id: Settings.cpp $ */ +/** @file + * Settings File Manipulation API. + * + * Two classes, MainConfigFile and MachineConfigFile, represent the VirtualBox.xml and + * machine XML files. They share a common ancestor class, ConfigFileBase, which shares + * functionality such as talking to the XML back-end classes and settings version management. + * + * The code can read all VirtualBox settings files version 1.3 and higher. That version was + * written by VirtualBox 2.0. It can write settings version 1.7 (used by VirtualBox 2.2 and + * 3.0) and 1.9 (used by VirtualBox 3.1) and newer ones obviously. + * + * The settings versions enum is defined in src/VBox/Main/idl/VirtualBox.xidl. To introduce + * a new settings version (should be necessary at most once per VirtualBox major release, + * if at all), add a new SettingsVersion value to that enum and grep for the previously + * highest value to see which code in here needs adjusting. + * + * Certainly ConfigFileBase::ConfigFileBase() will. Change VBOX_XML_VERSION below as well. + * VBOX_XML_VERSION does not have to be changed if the settings for a default VM do not + * touch newly introduced attributes or tags. It has the benefit that older VirtualBox + * versions do not trigger their "newer" code path. + * + * Once a new settings version has been added, these are the rules for introducing a new + * setting: If an XML element or attribute or value is introduced that was not present in + * previous versions, then settings version checks need to be introduced. See the + * SettingsVersion enumeration in src/VBox/Main/idl/VirtualBox.xidl for details about which + * version was used when. + * + * The settings versions checks are necessary because since version 3.1, VirtualBox no longer + * automatically converts XML settings files but only if necessary, that is, if settings are + * present that the old format does not support. If we write an element or attribute to a + * settings file of an older version, then an old VirtualBox (before 3.1) will attempt to + * validate it with XML schema, and that will certainly fail. + * + * So, to introduce a new setting: + * + * 1) Make sure the constructor of corresponding settings structure has a proper default. + * + * 2) In the settings reader method, try to read the setting; if it's there, great, if not, + * the default value will have been set by the constructor. The rule is to be tolerant + * here. + * + * 3) In MachineConfigFile::bumpSettingsVersionIfNeeded(), check if the new setting has + * a non-default value (i.e. that differs from the constructor). If so, bump the + * settings version to the current version so the settings writer (4) can write out + * the non-default value properly. + * + * So far a corresponding method for MainConfigFile has not been necessary since there + * have been no incompatible changes yet. + * + * 4) In the settings writer method, write the setting _only_ if the current settings + * version (stored in m->sv) is high enough. That is, for VirtualBox 4.0, write it + * only if (m->sv >= SettingsVersion_v1_11). + * + * 5) You _must_ update xml/VirtualBox-settings.xsd to contain the new tags and attributes. + * Check that settings file from before and after your change are validating properly. + * Use "kmk testvalidsettings", it should not find any files which don't validate. + */ + +/* + * Copyright (C) 2007-2022 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 + */ + +#define LOG_GROUP LOG_GROUP_MAIN +#include "VBox/com/string.h" +#include "VBox/settings.h" +#include <iprt/base64.h> +#include <iprt/cpp/lock.h> +#include <iprt/cpp/utils.h> +#include <iprt/cpp/xml.h> +#include <iprt/ctype.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/ldr.h> +#include <iprt/process.h> +#include <iprt/stream.h> +#ifdef RT_OS_WINDOWS +# include <iprt/system.h> /* For RTSystemGetNtVersion() / RTSYSTEM_MAKE_NT_VERSION. */ +#endif +#include <iprt/uri.h> + +// Guest Properties validation. +#include "VBox/HostServices/GuestPropertySvc.h" + +// generated header +#include "SchemaDefs.h" + +#include "HashedPw.h" +#include "LoggingNew.h" + +using namespace com; +using namespace settings; + +//////////////////////////////////////////////////////////////////////////////// +// +// Defines +// +//////////////////////////////////////////////////////////////////////////////// + +/** VirtualBox XML settings namespace */ +#define VBOX_XML_NAMESPACE "http://www.virtualbox.org/" + +/** VirtualBox XML schema location (relative URI) */ +#define VBOX_XML_SCHEMA "VirtualBox-settings.xsd" + +/** VirtualBox XML settings version number substring ("x.y") */ +#define VBOX_XML_VERSION "1.12" + +/** VirtualBox OVF settings import default version number substring ("x.y"). + * + * Think twice before changing this, as all VirtualBox versions before 5.1 + * wrote the settings version when exporting, but totally ignored it on + * importing (while it should have been a mandatory attribute), so 3rd party + * software out there creates OVF files with the VirtualBox specific settings + * but lacking the version attribute. This shouldn't happen any more, but + * breaking existing OVF files isn't nice. */ +#define VBOX_XML_IMPORT_VERSION "1.15" + +/** VirtualBox XML settings version platform substring */ +#if defined (RT_OS_DARWIN) +# define VBOX_XML_PLATFORM "macosx" +#elif defined (RT_OS_FREEBSD) +# define VBOX_XML_PLATFORM "freebsd" +#elif defined (RT_OS_LINUX) +# define VBOX_XML_PLATFORM "linux" +#elif defined (RT_OS_NETBSD) +# define VBOX_XML_PLATFORM "netbsd" +#elif defined (RT_OS_OPENBSD) +# define VBOX_XML_PLATFORM "openbsd" +#elif defined (RT_OS_OS2) +# define VBOX_XML_PLATFORM "os2" +#elif defined (RT_OS_SOLARIS) +# define VBOX_XML_PLATFORM "solaris" +#elif defined (RT_OS_WINDOWS) +# define VBOX_XML_PLATFORM "windows" +#else +# error Unsupported platform! +#endif + +/** VirtualBox XML settings full version string ("x.y-platform") */ +#define VBOX_XML_VERSION_FULL VBOX_XML_VERSION "-" VBOX_XML_PLATFORM + +/** VirtualBox OVF import default settings full version string ("x.y-platform") */ +#define VBOX_XML_IMPORT_VERSION_FULL VBOX_XML_IMPORT_VERSION "-" VBOX_XML_PLATFORM + +//////////////////////////////////////////////////////////////////////////////// +// +// Internal data +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Opaque data structore for ConfigFileBase (only declared + * in header, defined only here). + */ + +struct ConfigFileBase::Data +{ + Data() + : pDoc(NULL), + pelmRoot(NULL), + sv(SettingsVersion_Null), + svRead(SettingsVersion_Null) + {} + + ~Data() + { + cleanup(); + } + + RTCString strFilename; + bool fFileExists; + + xml::Document *pDoc; + xml::ElementNode *pelmRoot; + + com::Utf8Str strSettingsVersionFull; // e.g. "1.7-linux" + SettingsVersion_T sv; // e.g. SettingsVersion_v1_7 + + SettingsVersion_T svRead; // settings version that the original file had when it was read, + // or SettingsVersion_Null if none + + void copyFrom(const Data &d) + { + strFilename = d.strFilename; + fFileExists = d.fFileExists; + strSettingsVersionFull = d.strSettingsVersionFull; + sv = d.sv; + svRead = d.svRead; + } + + void cleanup() + { + if (pDoc) + { + delete pDoc; + pDoc = NULL; + pelmRoot = NULL; + } + } +}; + +/** + * Private exception class (not in the header file) that makes + * throwing xml::LogicError instances easier. That class is public + * and should be caught by client code. + */ +class settings::ConfigFileError : public xml::LogicError +{ +public: + ConfigFileError(const ConfigFileBase *file, + const xml::Node *pNode, + const char *pcszFormat, ...) + : xml::LogicError() + { + va_list args; + va_start(args, pcszFormat); + Utf8Str strWhat(pcszFormat, args); + va_end(args); + + Utf8Str strLine; + if (pNode) + strLine = Utf8StrFmt(" (line %RU32)", pNode->getLineNumber()); + + const char *pcsz = strLine.c_str(); + Utf8StrFmt str(N_("Error in %s%s -- %s"), + file->m->strFilename.c_str(), + (pcsz) ? pcsz : "", + strWhat.c_str()); + + setWhat(str.c_str()); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// ConfigFileBase +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Constructor. Allocates the XML internals, parses the XML file if + * pstrFilename is != NULL and reads the settings version from it. + * @param pstrFilename + */ +ConfigFileBase::ConfigFileBase(const com::Utf8Str *pstrFilename) + : m(new Data) +{ + m->fFileExists = false; + + if (pstrFilename) + { + try + { + // reading existing settings file: + m->strFilename = *pstrFilename; + + xml::XmlFileParser parser; + m->pDoc = new xml::Document; + parser.read(*pstrFilename, + *m->pDoc); + + m->fFileExists = true; + + m->pelmRoot = m->pDoc->getRootElement(); + if (!m->pelmRoot || !m->pelmRoot->nameEquals("VirtualBox")) + throw ConfigFileError(this, m->pelmRoot, N_("Root element in VirtualBox settings files must be \"VirtualBox\"")); + + if (!(m->pelmRoot->getAttributeValue("version", m->strSettingsVersionFull))) + throw ConfigFileError(this, m->pelmRoot, N_("Required VirtualBox/@version attribute is missing")); + + LogRel(("Loading settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str())); + + m->sv = parseVersion(m->strSettingsVersionFull, m->pelmRoot); + + // remember the settings version we read in case it gets upgraded later, + // so we know when to make backups + m->svRead = m->sv; + } + catch(...) + { + /* + * The destructor is not called when an exception is thrown in the constructor, + * so we have to do the cleanup here. + */ + delete m; + m = NULL; + throw; + } + } + else + { + // creating new settings file: + m->strSettingsVersionFull = VBOX_XML_VERSION_FULL; + m->sv = SettingsVersion_v1_12; + } +} + +ConfigFileBase::ConfigFileBase(const ConfigFileBase &other) + : m(new Data) +{ + copyBaseFrom(other); + m->strFilename = ""; + m->fFileExists = false; +} + +/** + * Clean up. + */ +ConfigFileBase::~ConfigFileBase() +{ + if (m) + { + delete m; + m = NULL; + } +} + +/** + * Helper function to convert a MediaType enum value into string from. + * @param t + */ +/*static*/ +const char *ConfigFileBase::stringifyMediaType(MediaType t) +{ + switch (t) + { + case HardDisk: + return "hard disk"; + case DVDImage: + return "DVD"; + case FloppyImage: + return "floppy"; + default: + AssertMsgFailed(("media type %d\n", t)); + return "UNKNOWN"; + } +} + +/** + * Helper function that parses a full version number. + * + * Allow future versions but fail if file is older than 1.6. Throws on errors. + * @returns settings version + * @param strVersion + * @param pElm + */ +SettingsVersion_T ConfigFileBase::parseVersion(const Utf8Str &strVersion, const xml::ElementNode *pElm) +{ + SettingsVersion_T sv = SettingsVersion_Null; + if (strVersion.length() > 3) + { + const char *pcsz = strVersion.c_str(); + + uint32_t uMajor = 0; + char ch; + while ( (ch = *pcsz) + && RT_C_IS_DIGIT(ch) ) + { + uMajor *= 10; + uMajor += (uint32_t)(ch - '0'); + ++pcsz; + } + + uint32_t uMinor = 0; + if (ch == '.') + { + pcsz++; + while ( (ch = *pcsz) + && RT_C_IS_DIGIT(ch)) + { + uMinor *= 10; + uMinor += (ULONG)(ch - '0'); + ++pcsz; + } + } + + if (uMajor == 1) + { + if (uMinor == 3) + sv = SettingsVersion_v1_3; + else if (uMinor == 4) + sv = SettingsVersion_v1_4; + else if (uMinor == 5) + sv = SettingsVersion_v1_5; + else if (uMinor == 6) + sv = SettingsVersion_v1_6; + else if (uMinor == 7) + sv = SettingsVersion_v1_7; + else if (uMinor == 8) + sv = SettingsVersion_v1_8; + else if (uMinor == 9) + sv = SettingsVersion_v1_9; + else if (uMinor == 10) + sv = SettingsVersion_v1_10; + else if (uMinor == 11) + sv = SettingsVersion_v1_11; + else if (uMinor == 12) + sv = SettingsVersion_v1_12; + else if (uMinor == 13) + sv = SettingsVersion_v1_13; + else if (uMinor == 14) + sv = SettingsVersion_v1_14; + else if (uMinor == 15) + sv = SettingsVersion_v1_15; + else if (uMinor == 16) + sv = SettingsVersion_v1_16; + else if (uMinor == 17) + sv = SettingsVersion_v1_17; + else if (uMinor == 18) + sv = SettingsVersion_v1_18; + else if (uMinor == 19) + sv = SettingsVersion_v1_19; + else if (uMinor > 19) + sv = SettingsVersion_Future; + } + else if (uMajor > 1) + sv = SettingsVersion_Future; + + Log(("Parsed settings version %d.%d to enum value %d\n", uMajor, uMinor, sv)); + } + + if (sv == SettingsVersion_Null) + throw ConfigFileError(this, pElm, N_("Cannot handle settings version '%s'"), strVersion.c_str()); + + return sv; +} + +/** + * Helper function that parses a UUID in string form into + * a com::Guid item. Accepts UUIDs both with and without + * "{}" brackets. Throws on errors. + * @param guid + * @param strUUID + * @param pElm + */ +void ConfigFileBase::parseUUID(Guid &guid, + const Utf8Str &strUUID, + const xml::ElementNode *pElm) const +{ + guid = strUUID.c_str(); + if (guid.isZero()) + throw ConfigFileError(this, pElm, N_("UUID \"%s\" has zero format"), strUUID.c_str()); + else if (!guid.isValid()) + throw ConfigFileError(this, pElm, N_("UUID \"%s\" has invalid format"), strUUID.c_str()); +} + +/** + * Parses the given string in str and attempts to treat it as an ISO + * date/time stamp to put into timestamp. Throws on errors. + * @param timestamp + * @param str + * @param pElm + */ +void ConfigFileBase::parseTimestamp(RTTIMESPEC ×tamp, + const com::Utf8Str &str, + const xml::ElementNode *pElm) const +{ + const char *pcsz = str.c_str(); + // yyyy-mm-ddThh:mm:ss + // "2009-07-10T11:54:03Z" + // 01234567890123456789 + // 1 + if (str.length() > 19) + { + // timezone must either be unspecified or 'Z' for UTC + if ( (pcsz[19]) + && (pcsz[19] != 'Z') + ) + throw ConfigFileError(this, pElm, N_("Cannot handle ISO timestamp '%s': is not UTC date"), str.c_str()); + + int32_t yyyy; + uint32_t mm, dd, hh, min, secs; + if ( (pcsz[4] == '-') + && (pcsz[7] == '-') + && (pcsz[10] == 'T') + && (pcsz[13] == ':') + && (pcsz[16] == ':') + ) + { + int rc; + if ( (RT_SUCCESS(rc = RTStrToInt32Ex(pcsz, NULL, 0, &yyyy))) + // could theoretically be negative but let's assume that nobody + // created virtual machines before the Christian era + && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 5, NULL, 0, &mm))) + && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 8, NULL, 0, &dd))) + && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 11, NULL, 0, &hh))) + && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 14, NULL, 0, &min))) + && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 17, NULL, 0, &secs))) + ) + { + RTTIME time = + { + yyyy, + (uint8_t)mm, + 0, + 0, + (uint8_t)dd, + (uint8_t)hh, + (uint8_t)min, + (uint8_t)secs, + 0, + RTTIME_FLAGS_TYPE_UTC, + 0 + }; + if (RTTimeNormalize(&time)) + if (RTTimeImplode(×tamp, &time)) + return; + } + + throw ConfigFileError(this, pElm, N_("Cannot parse ISO timestamp '%s': runtime error, %Rra"), str.c_str(), rc); + } + + throw ConfigFileError(this, pElm, N_("Cannot parse ISO timestamp '%s': invalid format"), str.c_str()); + } +} + +/** + * Helper function that parses a Base64 formatted string into a binary blob. + * @param binary + * @param str + * @param pElm + */ +void ConfigFileBase::parseBase64(IconBlob &binary, + const Utf8Str &str, + const xml::ElementNode *pElm) const +{ +#define DECODE_STR_MAX _1M + const char* psz = str.c_str(); + ssize_t cbOut = RTBase64DecodedSize(psz, NULL); + if (cbOut > DECODE_STR_MAX) + throw ConfigFileError(this, pElm, N_("Base64 encoded data too long (%d > %d)"), cbOut, DECODE_STR_MAX); + else if (cbOut < 0) + throw ConfigFileError(this, pElm, N_("Base64 encoded data '%s' invalid"), psz); + binary.resize((size_t)cbOut); + int vrc = VINF_SUCCESS; + if (cbOut) + vrc = RTBase64Decode(psz, &binary.front(), (size_t)cbOut, NULL, NULL); + if (RT_FAILURE(vrc)) + { + binary.resize(0); + throw ConfigFileError(this, pElm, N_("Base64 encoded data could not be decoded (%Rrc)"), vrc); + } +} + +/** + * Helper to create a string for a RTTIMESPEC for writing out ISO timestamps. + * @param stamp + * @return + */ +com::Utf8Str ConfigFileBase::stringifyTimestamp(const RTTIMESPEC &stamp) const +{ + RTTIME time; + if (!RTTimeExplode(&time, &stamp)) + throw ConfigFileError(this, NULL, N_("Timespec %lld ms is invalid"), RTTimeSpecGetMilli(&stamp)); + + return Utf8StrFmt("%04u-%02u-%02uT%02u:%02u:%02uZ", + time.i32Year, time.u8Month, time.u8MonthDay, + time.u8Hour, time.u8Minute, time.u8Second); +} + +/** + * Helper to create a base64 encoded string out of a binary blob. + * @param str + * @param binary + * @throws std::bad_alloc and ConfigFileError + */ +void ConfigFileBase::toBase64(com::Utf8Str &str, const IconBlob &binary) const +{ + size_t cb = binary.size(); + if (cb > 0) + { + size_t cchOut = RTBase64EncodedLength(cb); + str.reserve(cchOut + 1); + int vrc = RTBase64Encode(&binary.front(), cb, str.mutableRaw(), str.capacity(), NULL); + if (RT_FAILURE(vrc)) + throw ConfigFileError(this, NULL, N_("Failed to convert binary data to base64 format (%Rrc)"), vrc); + str.jolt(); + } +} + +/** + * Helper method to read in an ExtraData subtree and stores its contents + * in the given map of extradata items. Used for both main and machine + * extradata (MainConfigFile and MachineConfigFile). + * @param elmExtraData + * @param map + */ +void ConfigFileBase::readExtraData(const xml::ElementNode &elmExtraData, + StringsMap &map) +{ + xml::NodesLoop nlLevel4(elmExtraData); + const xml::ElementNode *pelmExtraDataItem; + while ((pelmExtraDataItem = nlLevel4.forAllNodes())) + { + if (pelmExtraDataItem->nameEquals("ExtraDataItem")) + { + // <ExtraDataItem name="GUI/LastWindowPostion" value="97,88,981,858"/> + Utf8Str strName, strValue; + if ( pelmExtraDataItem->getAttributeValue("name", strName) + && pelmExtraDataItem->getAttributeValue("value", strValue) ) + map[strName] = strValue; + else + throw ConfigFileError(this, pelmExtraDataItem, N_("Required ExtraDataItem/@name or @value attribute is missing")); + } + } +} + +/** + * Reads \<USBDeviceFilter\> entries from under the given elmDeviceFilters node and + * stores them in the given linklist. This is in ConfigFileBase because it's used + * from both MainConfigFile (for host filters) and MachineConfigFile (for machine + * filters). + * @param elmDeviceFilters + * @param ll + */ +void ConfigFileBase::readUSBDeviceFilters(const xml::ElementNode &elmDeviceFilters, + USBDeviceFiltersList &ll) +{ + xml::NodesLoop nl1(elmDeviceFilters, "DeviceFilter"); + const xml::ElementNode *pelmLevel4Child; + while ((pelmLevel4Child = nl1.forAllNodes())) + { + USBDeviceFilter flt; + flt.action = USBDeviceFilterAction_Ignore; + Utf8Str strAction; + if ( pelmLevel4Child->getAttributeValue("name", flt.strName) + && pelmLevel4Child->getAttributeValue("active", flt.fActive)) + { + if (!pelmLevel4Child->getAttributeValue("vendorId", flt.strVendorId)) + pelmLevel4Child->getAttributeValue("vendorid", flt.strVendorId); // used before 1.3 + if (!pelmLevel4Child->getAttributeValue("productId", flt.strProductId)) + pelmLevel4Child->getAttributeValue("productid", flt.strProductId); // used before 1.3 + pelmLevel4Child->getAttributeValue("revision", flt.strRevision); + pelmLevel4Child->getAttributeValue("manufacturer", flt.strManufacturer); + pelmLevel4Child->getAttributeValue("product", flt.strProduct); + if (!pelmLevel4Child->getAttributeValue("serialNumber", flt.strSerialNumber)) + pelmLevel4Child->getAttributeValue("serialnumber", flt.strSerialNumber); // used before 1.3 + pelmLevel4Child->getAttributeValue("port", flt.strPort); + + // the next 2 are irrelevant for host USB objects + pelmLevel4Child->getAttributeValue("remote", flt.strRemote); + pelmLevel4Child->getAttributeValue("maskedInterfaces", flt.ulMaskedInterfaces); + + // action is only used with host USB objects + if (pelmLevel4Child->getAttributeValue("action", strAction)) + { + if (strAction == "Ignore") + flt.action = USBDeviceFilterAction_Ignore; + else if (strAction == "Hold") + flt.action = USBDeviceFilterAction_Hold; + else + throw ConfigFileError(this, pelmLevel4Child, N_("Invalid value '%s' in DeviceFilter/@action attribute"), strAction.c_str()); + } + + ll.push_back(flt); + } + } +} + +/** + * Reads a media registry entry from the main VirtualBox.xml file. + * + * Whereas the current media registry code is fairly straightforward, it was quite a mess + * with settings format before 1.4 (VirtualBox 2.0 used settings format 1.3). The elements + * in the media registry were much more inconsistent, and different elements were used + * depending on the type of device and image. + * + * @param t + * @param elmMedium + * @param med + */ +void ConfigFileBase::readMediumOne(MediaType t, + const xml::ElementNode &elmMedium, + Medium &med) +{ + // <HardDisk uuid="{5471ecdb-1ddb-4012-a801-6d98e226868b}" location="/mnt/innotek-unix/vdis/Windows XP.vdi" format="VDI" type="Normal"> + + Utf8Str strUUID; + if (!elmMedium.getAttributeValue("uuid", strUUID)) + throw ConfigFileError(this, &elmMedium, N_("Required %s/@uuid attribute is missing"), elmMedium.getName()); + + parseUUID(med.uuid, strUUID, &elmMedium); + + bool fNeedsLocation = true; + + if (t == HardDisk) + { + if (m->sv < SettingsVersion_v1_4) + { + // here the system is: + // <HardDisk uuid="{....}" type="normal"> + // <VirtualDiskImage filePath="/path/to/xxx.vdi"/> + // </HardDisk> + + fNeedsLocation = false; + bool fNeedsFilePath = true; + const xml::ElementNode *pelmImage; + if ((pelmImage = elmMedium.findChildElement("VirtualDiskImage"))) + med.strFormat = "VDI"; + else if ((pelmImage = elmMedium.findChildElement("VMDKImage"))) + med.strFormat = "VMDK"; + else if ((pelmImage = elmMedium.findChildElement("VHDImage"))) + med.strFormat = "VHD"; + else if ((pelmImage = elmMedium.findChildElement("ISCSIHardDisk"))) + { + med.strFormat = "iSCSI"; + + fNeedsFilePath = false; + // location is special here: current settings specify an "iscsi://user@server:port/target/lun" + // string for the location and also have several disk properties for these, whereas this used + // to be hidden in several sub-elements before 1.4, so compose a location string and set up + // the properties: + med.strLocation = "iscsi://"; + Utf8Str strUser, strServer, strPort, strTarget, strLun; + if (pelmImage->getAttributeValue("userName", strUser)) + { + med.strLocation.append(strUser); + med.strLocation.append("@"); + } + Utf8Str strServerAndPort; + if (pelmImage->getAttributeValue("server", strServer)) + { + strServerAndPort = strServer; + } + if (pelmImage->getAttributeValue("port", strPort)) + { + if (strServerAndPort.length()) + strServerAndPort.append(":"); + strServerAndPort.append(strPort); + } + med.strLocation.append(strServerAndPort); + if (pelmImage->getAttributeValue("target", strTarget)) + { + med.strLocation.append("/"); + med.strLocation.append(strTarget); + } + if (pelmImage->getAttributeValue("lun", strLun)) + { + med.strLocation.append("/"); + med.strLocation.append(strLun); + } + + if (strServer.length() && strPort.length()) + med.properties["TargetAddress"] = strServerAndPort; + if (strTarget.length()) + med.properties["TargetName"] = strTarget; + if (strUser.length()) + med.properties["InitiatorUsername"] = strUser; + Utf8Str strPassword; + if (pelmImage->getAttributeValue("password", strPassword)) + med.properties["InitiatorSecret"] = strPassword; + if (strLun.length()) + med.properties["LUN"] = strLun; + } + else if ((pelmImage = elmMedium.findChildElement("CustomHardDisk"))) + { + fNeedsFilePath = false; + fNeedsLocation = true; + // also requires @format attribute, which will be queried below + } + else + throw ConfigFileError(this, &elmMedium, N_("Required %s/VirtualDiskImage element is missing"), elmMedium.getName()); + + if (fNeedsFilePath) + { + if (!(pelmImage->getAttributeValuePath("filePath", med.strLocation))) + throw ConfigFileError(this, &elmMedium, N_("Required %s/@filePath attribute is missing"), elmMedium.getName()); + } + } + + if (med.strFormat.isEmpty()) // not set with 1.4 format above, or 1.4 Custom format? + if (!elmMedium.getAttributeValue("format", med.strFormat)) + throw ConfigFileError(this, &elmMedium, N_("Required %s/@format attribute is missing"), elmMedium.getName()); + + if (!elmMedium.getAttributeValue("autoReset", med.fAutoReset)) + med.fAutoReset = false; + + Utf8Str strType; + if (elmMedium.getAttributeValue("type", strType)) + { + // pre-1.4 used lower case, so make this case-insensitive + strType.toUpper(); + if (strType == "NORMAL") + med.hdType = MediumType_Normal; + else if (strType == "IMMUTABLE") + med.hdType = MediumType_Immutable; + else if (strType == "WRITETHROUGH") + med.hdType = MediumType_Writethrough; + else if (strType == "SHAREABLE") + med.hdType = MediumType_Shareable; + else if (strType == "READONLY") + med.hdType = MediumType_Readonly; + else if (strType == "MULTIATTACH") + med.hdType = MediumType_MultiAttach; + else + throw ConfigFileError(this, &elmMedium, N_("HardDisk/@type attribute must be one of Normal, Immutable, Writethrough, Shareable, Readonly or MultiAttach")); + } + } + else + { + if (m->sv < SettingsVersion_v1_4) + { + // DVD and floppy images before 1.4 had "src" attribute instead of "location" + if (!elmMedium.getAttributeValue("src", med.strLocation)) + throw ConfigFileError(this, &elmMedium, N_("Required %s/@src attribute is missing"), elmMedium.getName()); + + fNeedsLocation = false; + } + + if (!elmMedium.getAttributeValue("format", med.strFormat)) + { + // DVD and floppy images before 1.11 had no format attribute. assign the default. + med.strFormat = "RAW"; + } + + if (t == DVDImage) + med.hdType = MediumType_Readonly; + else if (t == FloppyImage) + med.hdType = MediumType_Writethrough; + } + + if (fNeedsLocation) + // current files and 1.4 CustomHardDisk elements must have a location attribute + if (!elmMedium.getAttributeValue("location", med.strLocation)) + throw ConfigFileError(this, &elmMedium, N_("Required %s/@location attribute is missing"), elmMedium.getName()); + + // 3.2 builds added Description as an attribute, read it silently + // and write it back as an element starting with 5.1.26 + elmMedium.getAttributeValue("Description", med.strDescription); + + xml::NodesLoop nlMediumChildren(elmMedium); + const xml::ElementNode *pelmMediumChild; + while ((pelmMediumChild = nlMediumChildren.forAllNodes())) + { + if (pelmMediumChild->nameEquals("Description")) + med.strDescription = pelmMediumChild->getValue(); + else if (pelmMediumChild->nameEquals("Property")) + { + // handle medium properties + Utf8Str strPropName, strPropValue; + if ( pelmMediumChild->getAttributeValue("name", strPropName) + && pelmMediumChild->getAttributeValue("value", strPropValue) ) + med.properties[strPropName] = strPropValue; + else + throw ConfigFileError(this, pelmMediumChild, N_("Required HardDisk/Property/@name or @value attribute is missing")); + } + } +} + +/** + * Reads a media registry entry from the main VirtualBox.xml file and + * likewise for all children where applicable. + * + * @param t + * @param elmMedium + * @param med + */ +void ConfigFileBase::readMedium(MediaType t, + const xml::ElementNode &elmMedium, + Medium &med) +{ + std::list<const xml::ElementNode *> llElementsTodo; + llElementsTodo.push_back(&elmMedium); + std::list<Medium *> llSettingsTodo; + llSettingsTodo.push_back(&med); + std::list<uint32_t> llDepthsTodo; + llDepthsTodo.push_back(1); + + while (llElementsTodo.size() > 0) + { + const xml::ElementNode *pElement = llElementsTodo.front(); + llElementsTodo.pop_front(); + Medium *pMed = llSettingsTodo.front(); + llSettingsTodo.pop_front(); + uint32_t depth = llDepthsTodo.front(); + llDepthsTodo.pop_front(); + + if (depth > SETTINGS_MEDIUM_DEPTH_MAX) + throw ConfigFileError(this, pElement, N_("Maximum medium tree depth of %u exceeded"), SETTINGS_MEDIUM_DEPTH_MAX); + + readMediumOne(t, *pElement, *pMed); + + if (t != HardDisk) + return; + + // load all children + xml::NodesLoop nl2(*pElement, m->sv >= SettingsVersion_v1_4 ? "HardDisk" : "DiffHardDisk"); + const xml::ElementNode *pelmHDChild; + while ((pelmHDChild = nl2.forAllNodes())) + { + llElementsTodo.push_back(pelmHDChild); + pMed->llChildren.push_back(Medium::Empty); + llSettingsTodo.push_back(&pMed->llChildren.back()); + llDepthsTodo.push_back(depth + 1); + } + } +} + +/** + * Reads in the entire \<MediaRegistry\> chunk and stores its media in the lists + * of the given MediaRegistry structure. + * + * This is used in both MainConfigFile and MachineConfigFile since starting with + * VirtualBox 4.0, we can have media registries in both. + * + * For pre-1.4 files, this gets called with the \<DiskRegistry\> chunk instead. + * + * @param elmMediaRegistry + * @param mr + */ +void ConfigFileBase::readMediaRegistry(const xml::ElementNode &elmMediaRegistry, + MediaRegistry &mr) +{ + xml::NodesLoop nl1(elmMediaRegistry); + const xml::ElementNode *pelmChild1; + while ((pelmChild1 = nl1.forAllNodes())) + { + MediaType t = Error; + if (pelmChild1->nameEquals("HardDisks")) + t = HardDisk; + else if (pelmChild1->nameEquals("DVDImages")) + t = DVDImage; + else if (pelmChild1->nameEquals("FloppyImages")) + t = FloppyImage; + else + continue; + + xml::NodesLoop nl2(*pelmChild1); + const xml::ElementNode *pelmMedium; + while ((pelmMedium = nl2.forAllNodes())) + { + if ( t == HardDisk + && (pelmMedium->nameEquals("HardDisk"))) + { + mr.llHardDisks.push_back(Medium::Empty); + readMedium(t, *pelmMedium, mr.llHardDisks.back()); + } + else if ( t == DVDImage + && (pelmMedium->nameEquals("Image"))) + { + mr.llDvdImages.push_back(Medium::Empty); + readMedium(t, *pelmMedium, mr.llDvdImages.back()); + } + else if ( t == FloppyImage + && (pelmMedium->nameEquals("Image"))) + { + mr.llFloppyImages.push_back(Medium::Empty); + readMedium(t, *pelmMedium, mr.llFloppyImages.back()); + } + } + } +} + +/** + * This is common version for reading NAT port forward rule in per-_machine's_adapter_ and + * per-network approaches. + * Note: this function doesn't in fill given list from xml::ElementNodesList, because there is conflicting + * declaration in ovmfreader.h. + */ +void ConfigFileBase::readNATForwardRulesMap(const xml::ElementNode &elmParent, NATRulesMap &mapRules) +{ + xml::ElementNodesList plstRules; + elmParent.getChildElements(plstRules, "Forwarding"); + for (xml::ElementNodesList::iterator pf = plstRules.begin(); pf != plstRules.end(); ++pf) + { + NATRule rule; + uint32_t port = 0; + (*pf)->getAttributeValue("name", rule.strName); + (*pf)->getAttributeValue("proto", (uint32_t&)rule.proto); + (*pf)->getAttributeValue("hostip", rule.strHostIP); + (*pf)->getAttributeValue("hostport", port); + rule.u16HostPort = (uint16_t)port; + (*pf)->getAttributeValue("guestip", rule.strGuestIP); + (*pf)->getAttributeValue("guestport", port); + rule.u16GuestPort = (uint16_t)port; + mapRules.insert(std::make_pair(rule.strName, rule)); + } +} + +void ConfigFileBase::readNATLoopbacks(const xml::ElementNode &elmParent, NATLoopbackOffsetList &llLoopbacks) +{ + xml::ElementNodesList plstLoopbacks; + elmParent.getChildElements(plstLoopbacks, "Loopback4"); + for (xml::ElementNodesList::iterator lo = plstLoopbacks.begin(); + lo != plstLoopbacks.end(); ++lo) + { + NATHostLoopbackOffset loopback; + (*lo)->getAttributeValue("address", loopback.strLoopbackHostAddress); + (*lo)->getAttributeValue("offset", (uint32_t&)loopback.u32Offset); + llLoopbacks.push_back(loopback); + } +} + + +/** + * Adds a "version" attribute to the given XML element with the + * VirtualBox settings version (e.g. "1.10-linux"). Used by + * the XML format for the root element and by the OVF export + * for the vbox:Machine element. + * @param elm + */ +void ConfigFileBase::setVersionAttribute(xml::ElementNode &elm) +{ + const char *pcszVersion = NULL; + switch (m->sv) + { + case SettingsVersion_v1_8: + pcszVersion = "1.8"; + break; + + case SettingsVersion_v1_9: + pcszVersion = "1.9"; + break; + + case SettingsVersion_v1_10: + pcszVersion = "1.10"; + break; + + case SettingsVersion_v1_11: + pcszVersion = "1.11"; + break; + + case SettingsVersion_v1_12: + pcszVersion = "1.12"; + break; + + case SettingsVersion_v1_13: + pcszVersion = "1.13"; + break; + + case SettingsVersion_v1_14: + pcszVersion = "1.14"; + break; + + case SettingsVersion_v1_15: + pcszVersion = "1.15"; + break; + + case SettingsVersion_v1_16: + pcszVersion = "1.16"; + break; + + case SettingsVersion_v1_17: + pcszVersion = "1.17"; + break; + + case SettingsVersion_v1_18: + pcszVersion = "1.18"; + break; + + case SettingsVersion_v1_19: + pcszVersion = "1.19"; + break; + + default: + // catch human error: the assertion below will trigger in debug + // or dbgopt builds, so hopefully this will get noticed sooner in + // the future, because it's easy to forget top update something. + AssertMsg(m->sv <= SettingsVersion_v1_7, ("Settings.cpp: unexpected settings version %d, unhandled future version?\n", m->sv)); + // silently upgrade if this is less than 1.7 because that's the oldest we can write + if (m->sv <= SettingsVersion_v1_7) + { + pcszVersion = "1.7"; + m->sv = SettingsVersion_v1_7; + } + else + { + // This is reached for SettingsVersion_Future and forgotten + // settings version after SettingsVersion_v1_7, which should + // not happen (see assertion above). Set the version to the + // latest known version, to minimize loss of information, but + // as we can't predict the future we have to use some format + // we know, and latest should be the best choice. Note that + // for "forgotten settings" this may not be the best choice, + // but as it's an omission of someone who changed this file + // it's the only generic possibility. + pcszVersion = "1.19"; + m->sv = SettingsVersion_v1_19; + } + break; + } + + m->strSettingsVersionFull = Utf8StrFmt("%s-%s", + pcszVersion, + VBOX_XML_PLATFORM); // e.g. "linux" + elm.setAttribute("version", m->strSettingsVersionFull); +} + + +/** + * Creates a special backup file in case there is a version + * bump, so that it is possible to go back to the previous + * state. This is done only once (not for every settings + * version bump), when the settings version is newer than + * the version read from the config file. Must be called + * before ConfigFileBase::createStubDocument, because that + * method may alter information which this method needs. + */ +void ConfigFileBase::specialBackupIfFirstBump() +{ + // Since this gets called before the XML document is actually written out, + // this is where we must check whether we're upgrading the settings version + // and need to make a backup, so the user can go back to an earlier + // VirtualBox version and recover his old settings files. + if ( (m->svRead != SettingsVersion_Null) // old file exists? + && (m->svRead < m->sv) // we're upgrading? + ) + { + // compose new filename: strip off trailing ".xml"/".vbox" + Utf8Str strFilenameNew; + Utf8Str strExt = ".xml"; + if (m->strFilename.endsWith(".xml")) + strFilenameNew = m->strFilename.substr(0, m->strFilename.length() - 4); + else if (m->strFilename.endsWith(".vbox")) + { + strFilenameNew = m->strFilename.substr(0, m->strFilename.length() - 5); + strExt = ".vbox"; + } + + // and append something like "-1.3-linux.xml" + strFilenameNew.append("-"); + strFilenameNew.append(m->strSettingsVersionFull); // e.g. "1.3-linux" + strFilenameNew.append(strExt); // .xml for main config, .vbox for machine config + + // Copying the file cannot be avoided, as doing tricks with renaming + // causes trouble on OS X with aliases (which follow the rename), and + // on all platforms there is a risk of "losing" the VM config when + // running out of space, as a rename here couldn't be rolled back. + // Ignoring all errors besides running out of space is intentional, as + // we don't want to do anything if the file already exists. + int vrc = RTFileCopy(m->strFilename.c_str(), strFilenameNew.c_str()); + if (RT_UNLIKELY(vrc == VERR_DISK_FULL)) + throw ConfigFileError(this, NULL, N_("Cannot create settings backup file when upgrading to a newer settings format")); + + // do this only once + m->svRead = SettingsVersion_Null; + } +} + +/** + * Creates a new stub xml::Document in the m->pDoc member with the + * root "VirtualBox" element set up. This is used by both + * MainConfigFile and MachineConfigFile at the beginning of writing + * out their XML. + * + * Before calling this, it is the responsibility of the caller to + * set the "sv" member to the required settings version that is to + * be written. For newly created files, the settings version will be + * recent (1.12 or later if necessary); for files read in from disk + * earlier, it will be the settings version indicated in the file. + * However, this method will silently make sure that the settings + * version is always at least 1.7 and change it if necessary, since + * there is no write support for earlier settings versions. + */ +void ConfigFileBase::createStubDocument() +{ + Assert(m->pDoc == NULL); + m->pDoc = new xml::Document; + + m->pelmRoot = m->pDoc->createRootElement("VirtualBox", + "\n" + "** DO NOT EDIT THIS FILE.\n" + "** If you make changes to this file while any VirtualBox related application\n" + "** is running, your changes will be overwritten later, without taking effect.\n" + "** Use VBoxManage or the VirtualBox Manager GUI to make changes.\n" +); + m->pelmRoot->setAttribute("xmlns", VBOX_XML_NAMESPACE); + // Have the code for producing a proper schema reference. Not used by most + // tools, so don't bother doing it. The schema is not on the server anyway. +#ifdef VBOX_WITH_SETTINGS_SCHEMA + m->pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); + m->pelmRoot->setAttribute("xsi:schemaLocation", VBOX_XML_NAMESPACE " " VBOX_XML_SCHEMA); +#endif + + // add settings version attribute to root element, update m->strSettingsVersionFull + setVersionAttribute(*m->pelmRoot); + + LogRel(("Saving settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str())); +} + +/** + * Creates an \<ExtraData\> node under the given parent element with + * \<ExtraDataItem\> childern according to the contents of the given + * map. + * + * This is in ConfigFileBase because it's used in both MainConfigFile + * and MachineConfigFile, which both can have extradata. + * + * @param elmParent + * @param me + */ +void ConfigFileBase::buildExtraData(xml::ElementNode &elmParent, + const StringsMap &me) +{ + if (me.size()) + { + xml::ElementNode *pelmExtraData = elmParent.createChild("ExtraData"); + for (StringsMap::const_iterator it = me.begin(); + it != me.end(); + ++it) + { + const Utf8Str &strName = it->first; + const Utf8Str &strValue = it->second; + xml::ElementNode *pelmThis = pelmExtraData->createChild("ExtraDataItem"); + pelmThis->setAttribute("name", strName); + pelmThis->setAttribute("value", strValue); + } + } +} + +/** + * Creates \<DeviceFilter\> nodes under the given parent element according to + * the contents of the given USBDeviceFiltersList. This is in ConfigFileBase + * because it's used in both MainConfigFile (for host filters) and + * MachineConfigFile (for machine filters). + * + * If fHostMode is true, this means that we're supposed to write filters + * for the IHost interface (respect "action", omit "strRemote" and + * "ulMaskedInterfaces" in struct USBDeviceFilter). + * + * @param elmParent + * @param ll + * @param fHostMode + */ +void ConfigFileBase::buildUSBDeviceFilters(xml::ElementNode &elmParent, + const USBDeviceFiltersList &ll, + bool fHostMode) +{ + for (USBDeviceFiltersList::const_iterator it = ll.begin(); + it != ll.end(); + ++it) + { + const USBDeviceFilter &flt = *it; + xml::ElementNode *pelmFilter = elmParent.createChild("DeviceFilter"); + pelmFilter->setAttribute("name", flt.strName); + pelmFilter->setAttribute("active", flt.fActive); + if (flt.strVendorId.length()) + pelmFilter->setAttribute("vendorId", flt.strVendorId); + if (flt.strProductId.length()) + pelmFilter->setAttribute("productId", flt.strProductId); + if (flt.strRevision.length()) + pelmFilter->setAttribute("revision", flt.strRevision); + if (flt.strManufacturer.length()) + pelmFilter->setAttribute("manufacturer", flt.strManufacturer); + if (flt.strProduct.length()) + pelmFilter->setAttribute("product", flt.strProduct); + if (flt.strSerialNumber.length()) + pelmFilter->setAttribute("serialNumber", flt.strSerialNumber); + if (flt.strPort.length()) + pelmFilter->setAttribute("port", flt.strPort); + + if (fHostMode) + { + const char *pcsz = + (flt.action == USBDeviceFilterAction_Ignore) ? "Ignore" + : /*(flt.action == USBDeviceFilterAction_Hold) ?*/ "Hold"; + pelmFilter->setAttribute("action", pcsz); + } + else + { + if (flt.strRemote.length()) + pelmFilter->setAttribute("remote", flt.strRemote); + if (flt.ulMaskedInterfaces) + pelmFilter->setAttribute("maskedInterfaces", flt.ulMaskedInterfaces); + } + } +} + +/** + * Creates a single \<HardDisk\> element for the given Medium structure + * and all child hard disks underneath. Called from MainConfigFile::write(). + * + * @param t + * @param elmMedium + * @param med + */ +void ConfigFileBase::buildMedium(MediaType t, + xml::ElementNode &elmMedium, + const Medium &med) +{ + std::list<const Medium *> llSettingsTodo; + llSettingsTodo.push_back(&med); + std::list<xml::ElementNode *> llElementsTodo; + llElementsTodo.push_back(&elmMedium); + std::list<uint32_t> llDepthsTodo; + llDepthsTodo.push_back(1); + + while (llSettingsTodo.size() > 0) + { + const Medium *pMed = llSettingsTodo.front(); + llSettingsTodo.pop_front(); + xml::ElementNode *pElement = llElementsTodo.front(); + llElementsTodo.pop_front(); + uint32_t depth = llDepthsTodo.front(); + llDepthsTodo.pop_front(); + + if (depth > SETTINGS_MEDIUM_DEPTH_MAX) + throw ConfigFileError(this, pElement, N_("Maximum medium tree depth of %u exceeded"), SETTINGS_MEDIUM_DEPTH_MAX); + + xml::ElementNode *pelmMedium; + + if (t == HardDisk) + pelmMedium = pElement->createChild("HardDisk"); + else + pelmMedium = pElement->createChild("Image"); + + pelmMedium->setAttribute("uuid", pMed->uuid.toStringCurly()); + + pelmMedium->setAttributePath("location", pMed->strLocation); + + if (t == HardDisk || RTStrICmp(pMed->strFormat.c_str(), "RAW")) + pelmMedium->setAttribute("format", pMed->strFormat); + if ( t == HardDisk + && pMed->fAutoReset) + pelmMedium->setAttribute("autoReset", pMed->fAutoReset); + if (pMed->strDescription.length()) + pelmMedium->createChild("Description")->addContent(pMed->strDescription); + + for (StringsMap::const_iterator it = pMed->properties.begin(); + it != pMed->properties.end(); + ++it) + { + xml::ElementNode *pelmProp = pelmMedium->createChild("Property"); + pelmProp->setAttribute("name", it->first); + pelmProp->setAttribute("value", it->second); + } + + // only for base hard disks, save the type + if (depth == 1) + { + // no need to save the usual DVD/floppy medium types + if ( ( t != DVDImage + || ( pMed->hdType != MediumType_Writethrough // shouldn't happen + && pMed->hdType != MediumType_Readonly)) + && ( t != FloppyImage + || pMed->hdType != MediumType_Writethrough)) + { + const char *pcszType = + pMed->hdType == MediumType_Normal ? "Normal" : + pMed->hdType == MediumType_Immutable ? "Immutable" : + pMed->hdType == MediumType_Writethrough ? "Writethrough" : + pMed->hdType == MediumType_Shareable ? "Shareable" : + pMed->hdType == MediumType_Readonly ? "Readonly" : + pMed->hdType == MediumType_MultiAttach ? "MultiAttach" : + "INVALID"; + pelmMedium->setAttribute("type", pcszType); + } + } + + /* save all children */ + MediaList::const_iterator itBegin = pMed->llChildren.begin(); + MediaList::const_iterator itEnd = pMed->llChildren.end(); + for (MediaList::const_iterator it = itBegin; it != itEnd; ++it) + { + llSettingsTodo.push_back(&*it); + llElementsTodo.push_back(pelmMedium); + llDepthsTodo.push_back(depth + 1); + } + } +} + +/** + * Creates a \<MediaRegistry\> node under the given parent and writes out all + * hard disks and DVD and floppy images from the lists in the given MediaRegistry + * structure under it. + * + * This is used in both MainConfigFile and MachineConfigFile since starting with + * VirtualBox 4.0, we can have media registries in both. + * + * @param elmParent + * @param mr + */ +void ConfigFileBase::buildMediaRegistry(xml::ElementNode &elmParent, + const MediaRegistry &mr) +{ + if (mr.llHardDisks.size() == 0 && mr.llDvdImages.size() == 0 && mr.llFloppyImages.size() == 0) + return; + + xml::ElementNode *pelmMediaRegistry = elmParent.createChild("MediaRegistry"); + + if (mr.llHardDisks.size()) + { + xml::ElementNode *pelmHardDisks = pelmMediaRegistry->createChild("HardDisks"); + for (MediaList::const_iterator it = mr.llHardDisks.begin(); + it != mr.llHardDisks.end(); + ++it) + { + buildMedium(HardDisk, *pelmHardDisks, *it); + } + } + + if (mr.llDvdImages.size()) + { + xml::ElementNode *pelmDVDImages = pelmMediaRegistry->createChild("DVDImages"); + for (MediaList::const_iterator it = mr.llDvdImages.begin(); + it != mr.llDvdImages.end(); + ++it) + { + buildMedium(DVDImage, *pelmDVDImages, *it); + } + } + + if (mr.llFloppyImages.size()) + { + xml::ElementNode *pelmFloppyImages = pelmMediaRegistry->createChild("FloppyImages"); + for (MediaList::const_iterator it = mr.llFloppyImages.begin(); + it != mr.llFloppyImages.end(); + ++it) + { + buildMedium(FloppyImage, *pelmFloppyImages, *it); + } + } +} + +/** + * Serialize NAT port-forwarding rules in parent container. + * Note: it's responsibility of caller to create parent of the list tag. + * because this method used for serializing per-_mahine's_adapter_ and per-network approaches. + */ +void ConfigFileBase::buildNATForwardRulesMap(xml::ElementNode &elmParent, const NATRulesMap &mapRules) +{ + for (NATRulesMap::const_iterator r = mapRules.begin(); + r != mapRules.end(); ++r) + { + xml::ElementNode *pelmPF; + pelmPF = elmParent.createChild("Forwarding"); + const NATRule &nr = r->second; + if (nr.strName.length()) + pelmPF->setAttribute("name", nr.strName); + pelmPF->setAttribute("proto", nr.proto); + if (nr.strHostIP.length()) + pelmPF->setAttribute("hostip", nr.strHostIP); + if (nr.u16HostPort) + pelmPF->setAttribute("hostport", nr.u16HostPort); + if (nr.strGuestIP.length()) + pelmPF->setAttribute("guestip", nr.strGuestIP); + if (nr.u16GuestPort) + pelmPF->setAttribute("guestport", nr.u16GuestPort); + } +} + + +void ConfigFileBase::buildNATLoopbacks(xml::ElementNode &elmParent, const NATLoopbackOffsetList &natLoopbackOffsetList) +{ + for (NATLoopbackOffsetList::const_iterator lo = natLoopbackOffsetList.begin(); + lo != natLoopbackOffsetList.end(); ++lo) + { + xml::ElementNode *pelmLo; + pelmLo = elmParent.createChild("Loopback4"); + pelmLo->setAttribute("address", (*lo).strLoopbackHostAddress); + pelmLo->setAttribute("offset", (*lo).u32Offset); + } +} + +/** + * Cleans up memory allocated by the internal XML parser. To be called by + * descendant classes when they're done analyzing the DOM tree to discard it. + */ +void ConfigFileBase::clearDocument() +{ + m->cleanup(); +} + +/** + * Returns true only if the underlying config file exists on disk; + * either because the file has been loaded from disk, or it's been written + * to disk, or both. + * @return + */ +bool ConfigFileBase::fileExists() +{ + return m->fFileExists; +} + +/** + * Returns the settings file version + * + * @returns Settings file version enum. + */ +SettingsVersion_T ConfigFileBase::getSettingsVersion() +{ + return m->sv; +} + + +/** + * Copies the base variables from another instance. Used by Machine::saveSettings + * so that the settings version does not get lost when a copy of the Machine settings + * file is made to see if settings have actually changed. + * @param b + */ +void ConfigFileBase::copyBaseFrom(const ConfigFileBase &b) +{ + m->copyFrom(*b.m); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Structures shared between Machine XML and VirtualBox.xml +// +//////////////////////////////////////////////////////////////////////////////// + + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +USBDeviceFilter::USBDeviceFilter() : + fActive(false), + action(USBDeviceFilterAction_Null), + ulMaskedInterfaces(0) +{ +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool USBDeviceFilter::operator==(const USBDeviceFilter &u) const +{ + return (this == &u) + || ( strName == u.strName + && fActive == u.fActive + && strVendorId == u.strVendorId + && strProductId == u.strProductId + && strRevision == u.strRevision + && strManufacturer == u.strManufacturer + && strProduct == u.strProduct + && strSerialNumber == u.strSerialNumber + && strPort == u.strPort + && action == u.action + && strRemote == u.strRemote + && ulMaskedInterfaces == u.ulMaskedInterfaces); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +settings::Medium::Medium() : + fAutoReset(false), + hdType(MediumType_Normal) +{ +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool settings::Medium::operator==(const settings::Medium &m) const +{ + return (this == &m) + || ( uuid == m.uuid + && strLocation == m.strLocation + && strDescription == m.strDescription + && strFormat == m.strFormat + && fAutoReset == m.fAutoReset + && properties == m.properties + && hdType == m.hdType + && llChildren == m.llChildren); // this is deep and recurses +} + +const struct settings::Medium settings::Medium::Empty; /* default ctor is OK */ + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool MediaRegistry::operator==(const MediaRegistry &m) const +{ + return (this == &m) + || ( llHardDisks == m.llHardDisks + && llDvdImages == m.llDvdImages + && llFloppyImages == m.llFloppyImages); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +NATRule::NATRule() : + proto(NATProtocol_TCP), + u16HostPort(0), + u16GuestPort(0) +{ +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool NATRule::operator==(const NATRule &r) const +{ + return (this == &r) + || ( strName == r.strName + && proto == r.proto + && u16HostPort == r.u16HostPort + && strHostIP == r.strHostIP + && u16GuestPort == r.u16GuestPort + && strGuestIP == r.strGuestIP); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +NATHostLoopbackOffset::NATHostLoopbackOffset() : + u32Offset(0) +{ +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool NATHostLoopbackOffset::operator==(const NATHostLoopbackOffset &o) const +{ + return (this == &o) + || ( strLoopbackHostAddress == o.strLoopbackHostAddress + && u32Offset == o.u32Offset); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// VirtualBox.xml structures +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +SystemProperties::SystemProperties() + : uProxyMode(ProxyMode_System) + , uLogHistoryCount(3) + , fExclusiveHwVirt(true) +{ +#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) + fExclusiveHwVirt = false; +#endif +} + +#ifdef VBOX_WITH_UPDATE_AGENT +UpdateAgent::UpdateAgent() + : fEnabled(false) + , enmChannel(UpdateChannel_Stable) + , uCheckFreqSeconds(RT_SEC_1DAY) + , uCheckCount(0) +{ +} +#endif /* VBOX_WITH_UPDATE_AGENT */ + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +DhcpOptValue::DhcpOptValue() + : strValue() + , enmEncoding(DHCPOptionEncoding_Normal) +{ +} + +/** + * Non-standard constructor. + */ +DhcpOptValue::DhcpOptValue(const com::Utf8Str &aText, DHCPOptionEncoding_T aEncoding) + : strValue(aText) + , enmEncoding(aEncoding) +{ +} + +/** + * Default constructor. + */ +DHCPGroupCondition::DHCPGroupCondition() + : fInclusive(true) + , enmType(DHCPGroupConditionType_MAC) + , strValue() +{ +} + +/** + * Default constructor. + */ +DHCPConfig::DHCPConfig() + : mapOptions() + , secMinLeaseTime(0) + , secDefaultLeaseTime(0) + , secMaxLeaseTime(0) +{ +} + +/** + * Default constructor. + */ +DHCPGroupConfig::DHCPGroupConfig() + : DHCPConfig() + , strName() + , vecConditions() +{ +} + +/** + * Default constructor. + */ +DHCPIndividualConfig::DHCPIndividualConfig() + : DHCPConfig() + , strMACAddress() + , strVMName() + , uSlot(0) +{ +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +DHCPServer::DHCPServer() + : fEnabled(false) +{ +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +NATNetwork::NATNetwork() : + fEnabled(true), + fIPv6Enabled(false), + fAdvertiseDefaultIPv6Route(false), + fNeedDhcpServer(true), + u32HostLoopback6Offset(0) +{ +} + +#ifdef VBOX_WITH_VMNET +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +HostOnlyNetwork::HostOnlyNetwork() : + strNetworkMask("255.255.255.0"), + strIPLower("192.168.56.1"), + strIPUpper("192.168.56.199"), + fEnabled(true) +{ + uuid.create(); +} +#endif /* VBOX_WITH_VMNET */ + +#ifdef VBOX_WITH_CLOUD_NET +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +CloudNetwork::CloudNetwork() : + strProviderShortName("OCI"), + strProfileName("Default"), + fEnabled(true) +{ +} +#endif /* VBOX_WITH_CLOUD_NET */ + + + +//////////////////////////////////////////////////////////////////////////////// +// +// MainConfigFile +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Reads one \<MachineEntry\> from the main VirtualBox.xml file. + * @param elmMachineRegistry + */ +void MainConfigFile::readMachineRegistry(const xml::ElementNode &elmMachineRegistry) +{ + // <MachineEntry uuid="{ xxx }" src=" xxx "/> + xml::NodesLoop nl1(elmMachineRegistry); + const xml::ElementNode *pelmChild1; + while ((pelmChild1 = nl1.forAllNodes())) + { + if (pelmChild1->nameEquals("MachineEntry")) + { + MachineRegistryEntry mre; + Utf8Str strUUID; + if ( pelmChild1->getAttributeValue("uuid", strUUID) + && pelmChild1->getAttributeValue("src", mre.strSettingsFile) ) + { + parseUUID(mre.uuid, strUUID, pelmChild1); + llMachines.push_back(mre); + } + else + throw ConfigFileError(this, pelmChild1, N_("Required MachineEntry/@uuid or @src attribute is missing")); + } + } +} + +/** + * Builds the XML tree for the DHCP servers. + */ +void MainConfigFile::buildDHCPServers(xml::ElementNode &elmDHCPServers, DHCPServersList const &ll) +{ + for (DHCPServersList::const_iterator it = ll.begin(); it != ll.end(); ++it) + { + const DHCPServer &srv = *it; + xml::ElementNode *pElmThis = elmDHCPServers.createChild("DHCPServer"); + + pElmThis->setAttribute("networkName", srv.strNetworkName); + pElmThis->setAttribute("IPAddress", srv.strIPAddress); + DhcpOptConstIterator itOpt = srv.globalConfig.mapOptions.find(DHCPOption_SubnetMask); + if (itOpt != srv.globalConfig.mapOptions.end()) + pElmThis->setAttribute("networkMask", itOpt->second.strValue); + pElmThis->setAttribute("lowerIP", srv.strIPLower); + pElmThis->setAttribute("upperIP", srv.strIPUpper); + pElmThis->setAttribute("enabled", (srv.fEnabled) ? 1 : 0); // too bad we chose 1 vs. 0 here + + /* We don't want duplicate validation check of networkMask here*/ + if (srv.globalConfig.mapOptions.size() > (itOpt != srv.globalConfig.mapOptions.end() ? 1U : 0U)) + { + xml::ElementNode *pElmOptions = pElmThis->createChild("Options"); + buildDHCPOptions(*pElmOptions, srv.globalConfig, true); + } + + for (DHCPGroupConfigVec::const_iterator itGroup = srv.vecGroupConfigs.begin(); + itGroup != srv.vecGroupConfigs.end(); ++itGroup) + { + DHCPGroupConfig const &rGroupConfig = *itGroup; + + xml::ElementNode *pElmGroup = pElmThis->createChild("Group"); + pElmGroup->setAttribute("name", rGroupConfig.strName); + buildDHCPOptions(*pElmGroup, rGroupConfig, false); + + for (DHCPGroupConditionVec::const_iterator itCond = rGroupConfig.vecConditions.begin(); + itCond != rGroupConfig.vecConditions.end(); ++itCond) + { + xml::ElementNode *pElmCondition = pElmGroup->createChild("Condition"); + pElmCondition->setAttribute("inclusive", itCond->fInclusive); + pElmCondition->setAttribute("type", (int32_t)itCond->enmType); + pElmCondition->setAttribute("value", itCond->strValue); + } + } + + for (DHCPIndividualConfigMap::const_iterator itHost = srv.mapIndividualConfigs.begin(); + itHost != srv.mapIndividualConfigs.end(); ++itHost) + { + DHCPIndividualConfig const &rIndividualConfig = itHost->second; + + xml::ElementNode *pElmConfig = pElmThis->createChild("Config"); + if (rIndividualConfig.strMACAddress.isNotEmpty()) + pElmConfig->setAttribute("MACAddress", rIndividualConfig.strMACAddress); + if (rIndividualConfig.strVMName.isNotEmpty()) + pElmConfig->setAttribute("vm-name", rIndividualConfig.strVMName); + if (rIndividualConfig.uSlot != 0 || rIndividualConfig.strVMName.isNotEmpty()) + pElmConfig->setAttribute("slot", rIndividualConfig.uSlot); + if (rIndividualConfig.strFixedAddress.isNotEmpty()) + pElmConfig->setAttribute("fixedAddress", rIndividualConfig.strFixedAddress); + buildDHCPOptions(*pElmConfig, rIndividualConfig, false); + } + } +} + +/** + * Worker for buildDHCPServers() that builds Options or Config element trees. + */ +void MainConfigFile::buildDHCPOptions(xml::ElementNode &elmOptions, DHCPConfig const &rConfig, bool fSkipSubnetMask) +{ + /* Generic (and optional) attributes on the Options or Config element: */ + if (rConfig.secMinLeaseTime > 0) + elmOptions.setAttribute("secMinLeaseTime", rConfig.secMinLeaseTime); + if (rConfig.secDefaultLeaseTime > 0) + elmOptions.setAttribute("secDefaultLeaseTime", rConfig.secDefaultLeaseTime); + if (rConfig.secMaxLeaseTime > 0) + elmOptions.setAttribute("secMaxLeaseTime", rConfig.secMaxLeaseTime); + if (rConfig.strForcedOptions.isNotEmpty()) + elmOptions.setAttribute("forcedOptions", rConfig.strForcedOptions); + if (rConfig.strSuppressedOptions.isNotEmpty()) + elmOptions.setAttribute("suppressedOptions", rConfig.strSuppressedOptions); + + /* The DHCP options are <Option> child elements: */ + for (DhcpOptConstIterator it = rConfig.mapOptions.begin(); it != rConfig.mapOptions.end(); ++it) + if (it->first != DHCPOption_SubnetMask || !fSkipSubnetMask) + { + xml::ElementNode *pElmOption = elmOptions.createChild("Option"); + pElmOption->setAttribute("name", it->first); + pElmOption->setAttribute("value", it->second.strValue); + if (it->second.enmEncoding != DHCPOptionEncoding_Normal) + pElmOption->setAttribute("encoding", (int32_t)it->second.enmEncoding); + } +} + +/** + * Reads in the \<DHCPServers\> chunk. + * @param elmDHCPServers + */ +void MainConfigFile::readDHCPServers(const xml::ElementNode &elmDHCPServers) +{ + xml::NodesLoop nl1(elmDHCPServers); + const xml::ElementNode *pelmServer; + while ((pelmServer = nl1.forAllNodes())) + { + if (pelmServer->nameEquals("DHCPServer")) + { + DHCPServer srv; + if ( pelmServer->getAttributeValue("networkName", srv.strNetworkName) + && pelmServer->getAttributeValue("IPAddress", srv.strIPAddress) + && pelmServer->getAttributeValue("networkMask", srv.globalConfig.mapOptions[DHCPOption_SubnetMask].strValue) + && pelmServer->getAttributeValue("lowerIP", srv.strIPLower) + && pelmServer->getAttributeValue("upperIP", srv.strIPUpper) + && pelmServer->getAttributeValue("enabled", srv.fEnabled) ) + { + /* Global options: */ + const xml::ElementNode *pElmOptions; + xml::NodesLoop nlOptions(*pelmServer, "Options"); + while ((pElmOptions = nlOptions.forAllNodes()) != NULL) /** @todo this loop makes no sense, there can only be one \<Options\> child. */ + readDHCPOptions(srv.globalConfig, *pElmOptions, true /*fIgnoreSubnetMask*/); + + /* Group configurations: */ + xml::NodesLoop nlGroup(*pelmServer, "Group"); + const xml::ElementNode *pElmGroup; + size_t i = 0; + while ((pElmGroup = nlGroup.forAllNodes()) != NULL) + { + srv.vecGroupConfigs.push_back(DHCPGroupConfig()); + DHCPGroupConfig &rGroupConfig = srv.vecGroupConfigs.back(); + + if (!pElmGroup->getAttributeValue("name", rGroupConfig.strName)) + rGroupConfig.strName.printf("Unamed Group #%u", ++i); + + readDHCPOptions(rGroupConfig, *pElmGroup, false /*fIgnoreSubnetMask*/); + + xml::NodesLoop nlCondition(*pElmGroup, "Condition"); + const xml::ElementNode *pElmCondition; + while ((pElmCondition = nlCondition.forAllNodes()) != NULL) + { + rGroupConfig.vecConditions.push_back(DHCPGroupCondition()); + DHCPGroupCondition &rGroupCondition = rGroupConfig.vecConditions.back(); + + if (!pElmCondition->getAttributeValue("inclusive", rGroupCondition.fInclusive)) + rGroupCondition.fInclusive = true; + + int32_t iType; + if (!pElmCondition->getAttributeValue("type", iType)) + iType = DHCPGroupConditionType_MAC; + rGroupCondition.enmType = (DHCPGroupConditionType_T)iType; + + pElmCondition->getAttributeValue("value", rGroupCondition.strValue); + } + } + + /* host specific configuration: */ + xml::NodesLoop nlConfig(*pelmServer, "Config"); + const xml::ElementNode *pElmConfig; + while ((pElmConfig = nlConfig.forAllNodes()) != NULL) + { + com::Utf8Str strMACAddress; + if (!pElmConfig->getAttributeValue("MACAddress", strMACAddress)) + strMACAddress.setNull(); + + com::Utf8Str strVMName; + if (!pElmConfig->getAttributeValue("vm-name", strVMName)) + strVMName.setNull(); + + uint32_t uSlot; + if (!pElmConfig->getAttributeValue("slot", uSlot)) + uSlot = 0; + + com::Utf8Str strKey; + if (strVMName.isNotEmpty()) + strKey.printf("%s/%u", strVMName.c_str(), uSlot); + else + strKey.printf("%s/%u", strMACAddress.c_str(), uSlot); + + DHCPIndividualConfig &rIndividualConfig = srv.mapIndividualConfigs[strKey]; + rIndividualConfig.strMACAddress = strMACAddress; + rIndividualConfig.strVMName = strVMName; + rIndividualConfig.uSlot = uSlot; + pElmConfig->getAttributeValue("fixedAddress", rIndividualConfig.strFixedAddress); + + readDHCPOptions(rIndividualConfig, *pElmConfig, false /*fIgnoreSubnetMask*/); + } + + llDhcpServers.push_back(srv); + } + else + throw ConfigFileError(this, pelmServer, N_("Required DHCPServer/@networkName, @IPAddress, @networkMask, @lowerIP, @upperIP or @enabled attribute is missing")); + } + } +} + +/** + * Worker for readDHCPServers that reads a configuration, either global, + * group or host (VM+NIC) specific. + */ +void MainConfigFile::readDHCPOptions(DHCPConfig &rConfig, const xml::ElementNode &elmConfig, bool fIgnoreSubnetMask) +{ + /* Generic (and optional) attributes on the Options or Config element: */ + if (!elmConfig.getAttributeValue("secMinLeaseTime", rConfig.secMinLeaseTime)) + rConfig.secMinLeaseTime = 0; + if (!elmConfig.getAttributeValue("secDefaultLeaseTime", rConfig.secDefaultLeaseTime)) + rConfig.secDefaultLeaseTime = 0; + if (!elmConfig.getAttributeValue("secMaxLeaseTime", rConfig.secMaxLeaseTime)) + rConfig.secMaxLeaseTime = 0; + if (!elmConfig.getAttributeValue("forcedOptions", rConfig.strForcedOptions)) + rConfig.strSuppressedOptions.setNull(); + if (!elmConfig.getAttributeValue("suppressedOptions", rConfig.strSuppressedOptions)) + rConfig.strSuppressedOptions.setNull(); + + /* The DHCP options are <Option> child elements: */ + xml::NodesLoop nl2(elmConfig, "Option"); + const xml::ElementNode *pElmOption; + while ((pElmOption = nl2.forAllNodes()) != NULL) + { + int32_t iOptName; + if (!pElmOption->getAttributeValue("name", iOptName)) + continue; + DHCPOption_T OptName = (DHCPOption_T)iOptName; + if (OptName == DHCPOption_SubnetMask && fIgnoreSubnetMask) + continue; + + com::Utf8Str strValue; + pElmOption->getAttributeValue("value", strValue); + + int32_t iOptEnc; + if (!pElmOption->getAttributeValue("encoding", iOptEnc)) + iOptEnc = DHCPOptionEncoding_Normal; + + rConfig.mapOptions[OptName] = DhcpOptValue(strValue, (DHCPOptionEncoding_T)iOptEnc); + } /* end of forall("Option") */ + +} + +/** + * Reads in the \<NATNetworks\> chunk. + * @param elmNATNetworks + */ +void MainConfigFile::readNATNetworks(const xml::ElementNode &elmNATNetworks) +{ + xml::NodesLoop nl1(elmNATNetworks); + const xml::ElementNode *pelmNet; + while ((pelmNet = nl1.forAllNodes())) + { + if (pelmNet->nameEquals("NATNetwork")) + { + NATNetwork net; + if ( pelmNet->getAttributeValue("networkName", net.strNetworkName) + && pelmNet->getAttributeValue("enabled", net.fEnabled) + && pelmNet->getAttributeValue("network", net.strIPv4NetworkCidr) + && pelmNet->getAttributeValue("ipv6", net.fIPv6Enabled) + && pelmNet->getAttributeValue("ipv6prefix", net.strIPv6Prefix) + && pelmNet->getAttributeValue("advertiseDefaultIPv6Route", net.fAdvertiseDefaultIPv6Route) + && pelmNet->getAttributeValue("needDhcp", net.fNeedDhcpServer) ) + { + pelmNet->getAttributeValue("loopback6", net.u32HostLoopback6Offset); + const xml::ElementNode *pelmMappings; + if ((pelmMappings = pelmNet->findChildElement("Mappings"))) + readNATLoopbacks(*pelmMappings, net.llHostLoopbackOffsetList); + + const xml::ElementNode *pelmPortForwardRules4; + if ((pelmPortForwardRules4 = pelmNet->findChildElement("PortForwarding4"))) + readNATForwardRulesMap(*pelmPortForwardRules4, + net.mapPortForwardRules4); + + const xml::ElementNode *pelmPortForwardRules6; + if ((pelmPortForwardRules6 = pelmNet->findChildElement("PortForwarding6"))) + readNATForwardRulesMap(*pelmPortForwardRules6, + net.mapPortForwardRules6); + + llNATNetworks.push_back(net); + } + else + throw ConfigFileError(this, pelmNet, N_("Required NATNetwork/@networkName, @gateway, @network,@advertiseDefaultIpv6Route , @needDhcp or @enabled attribute is missing")); + } + } +} + +#ifdef VBOX_WITH_VMNET +/** + * Reads in the \<HostOnlyNetworks\> chunk. + * @param elmHostOnlyNetworks + */ +void MainConfigFile::readHostOnlyNetworks(const xml::ElementNode &elmHostOnlyNetworks) +{ + xml::NodesLoop nl1(elmHostOnlyNetworks); + const xml::ElementNode *pelmNet; + while ((pelmNet = nl1.forAllNodes())) + { + if (pelmNet->nameEquals("HostOnlyNetwork")) + { + HostOnlyNetwork net; + Utf8Str strID; + if ( pelmNet->getAttributeValue("name", net.strNetworkName) + && pelmNet->getAttributeValue("mask", net.strNetworkMask) + && pelmNet->getAttributeValue("ipLower", net.strIPLower) + && pelmNet->getAttributeValue("ipUpper", net.strIPUpper) + && pelmNet->getAttributeValue("id", strID) + && pelmNet->getAttributeValue("enabled", net.fEnabled) ) + { + parseUUID(net.uuid, strID, pelmNet); + llHostOnlyNetworks.push_back(net); + } + else + throw ConfigFileError(this, pelmNet, N_("Required HostOnlyNetwork/@name, @mask, @ipLower, @ipUpper, @id or @enabled attribute is missing")); + } + } +} +#endif /* VBOX_WITH_VMNET */ + +#ifdef VBOX_WITH_CLOUD_NET +/** + * Reads in the \<CloudNetworks\> chunk. + * @param elmCloudNetworks + */ +void MainConfigFile::readCloudNetworks(const xml::ElementNode &elmCloudNetworks) +{ + xml::NodesLoop nl1(elmCloudNetworks); + const xml::ElementNode *pelmNet; + while ((pelmNet = nl1.forAllNodes())) + { + if (pelmNet->nameEquals("CloudNetwork")) + { + CloudNetwork net; + if ( pelmNet->getAttributeValue("name", net.strNetworkName) + && pelmNet->getAttributeValue("provider", net.strProviderShortName) + && pelmNet->getAttributeValue("profile", net.strProfileName) + && pelmNet->getAttributeValue("id", net.strNetworkId) + && pelmNet->getAttributeValue("enabled", net.fEnabled) ) + { + llCloudNetworks.push_back(net); + } + else + throw ConfigFileError(this, pelmNet, N_("Required CloudNetwork/@name, @provider, @profile, @id or @enabled attribute is missing")); + } + } +} +#endif /* VBOX_WITH_CLOUD_NET */ + +/** + * Creates \<USBDeviceSource\> nodes under the given parent element according to + * the contents of the given USBDeviceSourcesList. + * + * @param elmParent + * @param ll + */ +void MainConfigFile::buildUSBDeviceSources(xml::ElementNode &elmParent, + const USBDeviceSourcesList &ll) +{ + for (USBDeviceSourcesList::const_iterator it = ll.begin(); + it != ll.end(); + ++it) + { + const USBDeviceSource &src = *it; + xml::ElementNode *pelmSource = elmParent.createChild("USBDeviceSource"); + pelmSource->setAttribute("name", src.strName); + pelmSource->setAttribute("backend", src.strBackend); + pelmSource->setAttribute("address", src.strAddress); + + /* Write the properties. */ + for (StringsMap::const_iterator itProp = src.properties.begin(); + itProp != src.properties.end(); + ++itProp) + { + xml::ElementNode *pelmProp = pelmSource->createChild("Property"); + pelmProp->setAttribute("name", itProp->first); + pelmProp->setAttribute("value", itProp->second); + } + } +} + +/** + * Reads \<USBDeviceFilter\> entries from under the given elmDeviceFilters node and + * stores them in the given linklist. This is in ConfigFileBase because it's used + * from both MainConfigFile (for host filters) and MachineConfigFile (for machine + * filters). + * @param elmDeviceSources + * @param ll + */ +void MainConfigFile::readUSBDeviceSources(const xml::ElementNode &elmDeviceSources, + USBDeviceSourcesList &ll) +{ + xml::NodesLoop nl1(elmDeviceSources, "USBDeviceSource"); + const xml::ElementNode *pelmChild; + while ((pelmChild = nl1.forAllNodes())) + { + USBDeviceSource src; + + if ( pelmChild->getAttributeValue("name", src.strName) + && pelmChild->getAttributeValue("backend", src.strBackend) + && pelmChild->getAttributeValue("address", src.strAddress)) + { + // handle medium properties + xml::NodesLoop nl2(*pelmChild, "Property"); + const xml::ElementNode *pelmSrcChild; + while ((pelmSrcChild = nl2.forAllNodes())) + { + Utf8Str strPropName, strPropValue; + if ( pelmSrcChild->getAttributeValue("name", strPropName) + && pelmSrcChild->getAttributeValue("value", strPropValue) ) + src.properties[strPropName] = strPropValue; + else + throw ConfigFileError(this, pelmSrcChild, N_("Required USBDeviceSource/Property/@name or @value attribute is missing")); + } + + ll.push_back(src); + } + } +} + +/** + * Converts old style Proxy settings from ExtraData/UI section. + * + * Saves proxy settings directly to systemProperties structure. + * + * @returns true if conversion was successfull, false if not. + * @param strUIProxySettings The GUI settings string to convert. + */ +bool MainConfigFile::convertGuiProxySettings(const com::Utf8Str &strUIProxySettings) +{ + /* + * Possible variants: + * - "ProxyAuto,proxyserver.url,1080,authDisabled,," + * - "ProxyDisabled,proxyserver.url,1080,authDisabled,," + * - "ProxyEnabled,proxyserver.url,1080,authDisabled,," + * + * Note! We only need to bother with the first three fields as the last + * three was never really used or ever actually passed to the HTTP + * client code. + */ + /* First field: The proxy mode. */ + const char *psz = RTStrStripL(strUIProxySettings.c_str()); + static const struct { const char *psz; size_t cch; ProxyMode_T enmMode; } s_aModes[] = + { + { RT_STR_TUPLE("ProxyAuto"), ProxyMode_System }, + { RT_STR_TUPLE("ProxyDisabled"), ProxyMode_NoProxy }, + { RT_STR_TUPLE("ProxyEnabled"), ProxyMode_Manual }, + }; + for (size_t i = 0; i < RT_ELEMENTS(s_aModes); i++) + if (RTStrNICmpAscii(psz, s_aModes[i].psz, s_aModes[i].cch) == 0) + { + systemProperties.uProxyMode = s_aModes[i].enmMode; + psz = RTStrStripL(psz + s_aModes[i].cch); + if (*psz == ',') + { + /* Second field: The proxy host, possibly fully fledged proxy URL. */ + psz = RTStrStripL(psz + 1); + if (*psz != '\0' && *psz != ',') + { + const char *pszEnd = strchr(psz, ','); + size_t cchHost = pszEnd ? (size_t)(pszEnd - psz) : strlen(psz); + while (cchHost > 0 && RT_C_IS_SPACE(psz[cchHost - 1])) + cchHost--; + systemProperties.strProxyUrl.assign(psz, cchHost); + if (systemProperties.strProxyUrl.find("://") == RTCString::npos) + systemProperties.strProxyUrl.replace(0, 0, "http://"); + + /* Third field: The proxy port. Defaulted to 1080 for all proxies. + The new settings has type specific default ports. */ + uint16_t uPort = 1080; + if (pszEnd) + { + int rc = RTStrToUInt16Ex(RTStrStripL(pszEnd + 1), NULL, 10, &uPort); + if (RT_FAILURE(rc)) + uPort = 1080; + } + RTURIPARSED Parsed; + int rc = RTUriParse(systemProperties.strProxyUrl.c_str(), &Parsed); + if (RT_SUCCESS(rc)) + { + if (Parsed.uAuthorityPort == UINT32_MAX) + systemProperties.strProxyUrl.appendPrintf(systemProperties.strProxyUrl.endsWith(":") + ? "%u" : ":%u", uPort); + } + else + { + LogRelFunc(("Dropping invalid proxy URL for %u: %s\n", + systemProperties.uProxyMode, systemProperties.strProxyUrl.c_str())); + systemProperties.strProxyUrl.setNull(); + } + } + /* else: don't bother with the rest if we haven't got a host. */ + } + if ( systemProperties.strProxyUrl.isEmpty() + && systemProperties.uProxyMode == ProxyMode_Manual) + { + systemProperties.uProxyMode = ProxyMode_System; + return false; + } + return true; + } + LogRelFunc(("Unknown proxy type: %s\n", psz)); + return false; +} + +/** + * Constructor. + * + * If pstrFilename is != NULL, this reads the given settings file into the member + * variables and various substructures and lists. Otherwise, the member variables + * are initialized with default values. + * + * Throws variants of xml::Error for I/O, XML and logical content errors, which + * the caller should catch; if this constructor does not throw, then the member + * variables contain meaningful values (either from the file or defaults). + * + * @param pstrFilename + */ +MainConfigFile::MainConfigFile(const Utf8Str *pstrFilename) + : ConfigFileBase(pstrFilename) +{ + if (pstrFilename) + { + // the ConfigFileBase constructor has loaded the XML file, so now + // we need only analyze what is in there + xml::NodesLoop nlRootChildren(*m->pelmRoot); + const xml::ElementNode *pelmRootChild; + bool fCopyProxySettingsFromExtraData = false; + while ((pelmRootChild = nlRootChildren.forAllNodes())) + { + if (pelmRootChild->nameEquals("Global")) + { + xml::NodesLoop nlGlobalChildren(*pelmRootChild); + const xml::ElementNode *pelmGlobalChild; + while ((pelmGlobalChild = nlGlobalChildren.forAllNodes())) + { + if (pelmGlobalChild->nameEquals("SystemProperties")) + { + pelmGlobalChild->getAttributeValue("defaultMachineFolder", systemProperties.strDefaultMachineFolder); + pelmGlobalChild->getAttributeValue("LoggingLevel", systemProperties.strLoggingLevel); + pelmGlobalChild->getAttributeValue("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat); + if (!pelmGlobalChild->getAttributeValue("VRDEAuthLibrary", systemProperties.strVRDEAuthLibrary)) + // pre-1.11 used @remoteDisplayAuthLibrary instead + pelmGlobalChild->getAttributeValue("remoteDisplayAuthLibrary", systemProperties.strVRDEAuthLibrary); + pelmGlobalChild->getAttributeValue("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary); + pelmGlobalChild->getAttributeValue("defaultVRDEExtPack", systemProperties.strDefaultVRDEExtPack); + pelmGlobalChild->getAttributeValue("defaultCryptoExtPack", systemProperties.strDefaultCryptoExtPack); + pelmGlobalChild->getAttributeValue("LogHistoryCount", systemProperties.uLogHistoryCount); + pelmGlobalChild->getAttributeValue("autostartDatabasePath", systemProperties.strAutostartDatabasePath); + pelmGlobalChild->getAttributeValue("defaultFrontend", systemProperties.strDefaultFrontend); + pelmGlobalChild->getAttributeValue("exclusiveHwVirt", systemProperties.fExclusiveHwVirt); + if (!pelmGlobalChild->getAttributeValue("proxyMode", systemProperties.uProxyMode)) + fCopyProxySettingsFromExtraData = true; + pelmGlobalChild->getAttributeValue("proxyUrl", systemProperties.strProxyUrl); + pelmGlobalChild->getAttributeValue("LanguageId", systemProperties.strLanguageId); + } +#ifdef VBOX_WITH_UPDATE_AGENT + else if (pelmGlobalChild->nameEquals("Updates")) + { + /* We keep the updates configuration as part of the host for now, as the API exposes the IHost::updateHost attribute, + * but use an own "Updates" branch in the XML for better structurizing stuff in the future. */ + UpdateAgent &updateHost = host.updateHost; + + xml::NodesLoop nlLevel4(*pelmGlobalChild); + const xml::ElementNode *pelmLevel4Child; + while ((pelmLevel4Child = nlLevel4.forAllNodes())) + { + if (pelmLevel4Child->nameEquals("Host")) + { + pelmLevel4Child->getAttributeValue("enabled", updateHost.fEnabled); + pelmLevel4Child->getAttributeValue("channel", (uint32_t&)updateHost.enmChannel); + pelmLevel4Child->getAttributeValue("checkFreqSec", updateHost.uCheckFreqSeconds); + pelmLevel4Child->getAttributeValue("repoUrl", updateHost.strRepoUrl); + pelmLevel4Child->getAttributeValue("lastCheckDate", updateHost.strLastCheckDate); + pelmLevel4Child->getAttributeValue("checkCount", updateHost.uCheckCount); + } + /** @todo Add update settings for ExtPack and Guest Additions here later. See @bugref{7983}. */ + } + + /* Global enabled switch for updates. Currently bound to host updates, as this is the only update we have so far. */ + pelmGlobalChild->getAttributeValue("enabled", updateHost.fEnabled); + } +#endif + else if (pelmGlobalChild->nameEquals("ExtraData")) + readExtraData(*pelmGlobalChild, mapExtraDataItems); + else if (pelmGlobalChild->nameEquals("MachineRegistry")) + readMachineRegistry(*pelmGlobalChild); + else if ( (pelmGlobalChild->nameEquals("MediaRegistry")) + || ( (m->sv < SettingsVersion_v1_4) + && (pelmGlobalChild->nameEquals("DiskRegistry")) + ) + ) + readMediaRegistry(*pelmGlobalChild, mediaRegistry); + else if (pelmGlobalChild->nameEquals("NetserviceRegistry")) + { + xml::NodesLoop nlLevel4(*pelmGlobalChild); + const xml::ElementNode *pelmLevel4Child; + while ((pelmLevel4Child = nlLevel4.forAllNodes())) + { + if (pelmLevel4Child->nameEquals("DHCPServers")) + readDHCPServers(*pelmLevel4Child); + if (pelmLevel4Child->nameEquals("NATNetworks")) + readNATNetworks(*pelmLevel4Child); +#ifdef VBOX_WITH_VMNET + if (pelmLevel4Child->nameEquals("HostOnlyNetworks")) + readHostOnlyNetworks(*pelmLevel4Child); +#endif /* VBOX_WITH_VMNET */ +#ifdef VBOX_WITH_CLOUD_NET + if (pelmLevel4Child->nameEquals("CloudNetworks")) + readCloudNetworks(*pelmLevel4Child); +#endif /* VBOX_WITH_CLOUD_NET */ + } + } + else if (pelmGlobalChild->nameEquals("USBDeviceFilters")) + readUSBDeviceFilters(*pelmGlobalChild, host.llUSBDeviceFilters); + else if (pelmGlobalChild->nameEquals("USBDeviceSources")) + readUSBDeviceSources(*pelmGlobalChild, host.llUSBDeviceSources); + } + } // end if (pelmRootChild->nameEquals("Global")) + } + + if (fCopyProxySettingsFromExtraData) + for (StringsMap::const_iterator it = mapExtraDataItems.begin(); it != mapExtraDataItems.end(); ++it) + if (it->first.equals("GUI/ProxySettings")) + { + convertGuiProxySettings(it->second); + break; + } + + clearDocument(); + } + + // DHCP servers were introduced with settings version 1.7; if we're loading + // from an older version OR this is a fresh install, then add one DHCP server + // with default settings + if ( (!llDhcpServers.size()) + && ( (!pstrFilename) // empty VirtualBox.xml file + || (m->sv < SettingsVersion_v1_7) // upgrading from before 1.7 + ) + ) + { + DHCPServer srv; +#ifdef RT_OS_WINDOWS + srv.strNetworkName = "HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter"; +#else + srv.strNetworkName = "HostInterfaceNetworking-vboxnet0"; +#endif + srv.strIPAddress = "192.168.56.100"; + srv.globalConfig.mapOptions[DHCPOption_SubnetMask] = DhcpOptValue("255.255.255.0"); + srv.strIPLower = "192.168.56.101"; + srv.strIPUpper = "192.168.56.254"; + srv.fEnabled = true; + llDhcpServers.push_back(srv); + } +} + +void MainConfigFile::bumpSettingsVersionIfNeeded() +{ +#ifdef VBOX_WITH_VMNET + if (m->sv < SettingsVersion_v1_19) + { + // VirtualBox 7.0 adds support for host-only networks. + if (!llHostOnlyNetworks.empty()) + m->sv = SettingsVersion_v1_19; + } +#endif /* VBOX_WITH_VMNET */ +#ifdef VBOX_WITH_CLOUD_NET + if (m->sv < SettingsVersion_v1_18) + { + // VirtualBox 6.1 adds support for cloud networks. + if (!llCloudNetworks.empty()) + m->sv = SettingsVersion_v1_18; + } +#endif /* VBOX_WITH_CLOUD_NET */ + + if (m->sv < SettingsVersion_v1_16) + { + // VirtualBox 5.1 add support for additional USB device sources. + if (!host.llUSBDeviceSources.empty()) + m->sv = SettingsVersion_v1_16; + } + + if (m->sv < SettingsVersion_v1_14) + { + // VirtualBox 4.3 adds NAT networks. + if ( !llNATNetworks.empty()) + m->sv = SettingsVersion_v1_14; + } +} + + +/** + * Called from the IVirtualBox interface to write out VirtualBox.xml. This + * builds an XML DOM tree and writes it out to disk. + */ +void MainConfigFile::write(const com::Utf8Str strFilename) +{ + bumpSettingsVersionIfNeeded(); + + m->strFilename = strFilename; + specialBackupIfFirstBump(); + createStubDocument(); + + xml::ElementNode *pelmGlobal = m->pelmRoot->createChild("Global"); + + buildExtraData(*pelmGlobal, mapExtraDataItems); + + xml::ElementNode *pelmMachineRegistry = pelmGlobal->createChild("MachineRegistry"); + for (MachinesRegistry::const_iterator it = llMachines.begin(); + it != llMachines.end(); + ++it) + { + // <MachineEntry uuid="{5f102a55-a51b-48e3-b45a-b28d33469488}" src="/mnt/innotek-unix/vbox-machines/Windows 5.1 XP 1 (Office 2003)/Windows 5.1 XP 1 (Office 2003).xml"/> + const MachineRegistryEntry &mre = *it; + xml::ElementNode *pelmMachineEntry = pelmMachineRegistry->createChild("MachineEntry"); + pelmMachineEntry->setAttribute("uuid", mre.uuid.toStringCurly()); + pelmMachineEntry->setAttribute("src", mre.strSettingsFile); + } + + buildMediaRegistry(*pelmGlobal, mediaRegistry); + + xml::ElementNode *pelmNetServiceRegistry = pelmGlobal->createChild("NetserviceRegistry"); /** @todo r=bird: wrong capitalization of NetServiceRegistry. sigh. */ + buildDHCPServers(*pelmNetServiceRegistry->createChild("DHCPServers"), llDhcpServers); + + xml::ElementNode *pelmNATNetworks; + /* don't create entry if no NAT networks are registered. */ + if (!llNATNetworks.empty()) + { + pelmNATNetworks = pelmNetServiceRegistry->createChild("NATNetworks"); + for (NATNetworksList::const_iterator it = llNATNetworks.begin(); + it != llNATNetworks.end(); + ++it) + { + const NATNetwork &n = *it; + xml::ElementNode *pelmThis = pelmNATNetworks->createChild("NATNetwork"); + pelmThis->setAttribute("networkName", n.strNetworkName); + pelmThis->setAttribute("network", n.strIPv4NetworkCidr); + pelmThis->setAttribute("ipv6", n.fIPv6Enabled ? 1 : 0); + pelmThis->setAttribute("ipv6prefix", n.strIPv6Prefix); + pelmThis->setAttribute("advertiseDefaultIPv6Route", (n.fAdvertiseDefaultIPv6Route)? 1 : 0); + pelmThis->setAttribute("needDhcp", (n.fNeedDhcpServer) ? 1 : 0); + pelmThis->setAttribute("enabled", (n.fEnabled) ? 1 : 0); // too bad we chose 1 vs. 0 here + if (n.mapPortForwardRules4.size()) + { + xml::ElementNode *pelmPf4 = pelmThis->createChild("PortForwarding4"); + buildNATForwardRulesMap(*pelmPf4, n.mapPortForwardRules4); + } + if (n.mapPortForwardRules6.size()) + { + xml::ElementNode *pelmPf6 = pelmThis->createChild("PortForwarding6"); + buildNATForwardRulesMap(*pelmPf6, n.mapPortForwardRules6); + } + + if (n.llHostLoopbackOffsetList.size()) + { + xml::ElementNode *pelmMappings = pelmThis->createChild("Mappings"); + buildNATLoopbacks(*pelmMappings, n.llHostLoopbackOffsetList); + + } + } + } + +#ifdef VBOX_WITH_VMNET + xml::ElementNode *pelmHostOnlyNetworks; + /* don't create entry if no HostOnly networks are registered. */ + if (!llHostOnlyNetworks.empty()) + { + pelmHostOnlyNetworks = pelmNetServiceRegistry->createChild("HostOnlyNetworks"); + for (HostOnlyNetworksList::const_iterator it = llHostOnlyNetworks.begin(); + it != llHostOnlyNetworks.end(); + ++it) + { + const HostOnlyNetwork &n = *it; + xml::ElementNode *pelmThis = pelmHostOnlyNetworks->createChild("HostOnlyNetwork"); + pelmThis->setAttribute("name", n.strNetworkName); + pelmThis->setAttribute("mask", n.strNetworkMask); + pelmThis->setAttribute("ipLower", n.strIPLower); + pelmThis->setAttribute("ipUpper", n.strIPUpper); + pelmThis->setAttribute("id", n.uuid.toStringCurly()); + pelmThis->setAttribute("enabled", (n.fEnabled) ? 1 : 0); // too bad we chose 1 vs. 0 here + } + } +#endif /* VBOX_WITH_VMNET */ +#ifdef VBOX_WITH_CLOUD_NET + xml::ElementNode *pelmCloudNetworks; + /* don't create entry if no cloud networks are registered. */ + if (!llCloudNetworks.empty()) + { + pelmCloudNetworks = pelmNetServiceRegistry->createChild("CloudNetworks"); + for (CloudNetworksList::const_iterator it = llCloudNetworks.begin(); + it != llCloudNetworks.end(); + ++it) + { + const CloudNetwork &n = *it; + xml::ElementNode *pelmThis = pelmCloudNetworks->createChild("CloudNetwork"); + pelmThis->setAttribute("name", n.strNetworkName); + pelmThis->setAttribute("provider", n.strProviderShortName); + pelmThis->setAttribute("profile", n.strProfileName); + pelmThis->setAttribute("id", n.strNetworkId); + pelmThis->setAttribute("enabled", (n.fEnabled) ? 1 : 0); // too bad we chose 1 vs. 0 here + } + } +#endif /* VBOX_WITH_CLOUD_NET */ + +#ifdef VBOX_WITH_UPDATE_AGENT + /* We keep the updates configuration as part of the host for now, as the API exposes the IHost::updateHost attribute, + * but use an own "Updates" branch in the XML for better structurizing stuff in the future. */ + UpdateAgent &updateHost = host.updateHost; + + xml::ElementNode *pelmUpdates = pelmGlobal->createChild("Updates"); + /* Global enabled switch for updates. Currently bound to host updates, as this is the only update we have so far. */ + pelmUpdates->setAttribute("enabled", updateHost.fEnabled); + + xml::ElementNode *pelmUpdateHost = pelmUpdates->createChild("Host"); + pelmUpdateHost->setAttribute("enabled", updateHost.fEnabled); + pelmUpdateHost->setAttribute("channel", (int32_t)updateHost.enmChannel); + pelmUpdateHost->setAttribute("checkFreqSec", updateHost.uCheckFreqSeconds); + if (updateHost.strRepoUrl.length()) + pelmUpdateHost->setAttribute("repoUrl", updateHost.strRepoUrl); + if (updateHost.strLastCheckDate.length()) + pelmUpdateHost->setAttribute("lastCheckDate", updateHost.strLastCheckDate); + pelmUpdateHost->setAttribute("checkCount", updateHost.uCheckCount); + /** @todo Add update settings for ExtPack and Guest Additions here later. See @bugref{7983}. */ +#endif + + xml::ElementNode *pelmSysProps = pelmGlobal->createChild("SystemProperties"); + if (systemProperties.strDefaultMachineFolder.length()) + pelmSysProps->setAttribute("defaultMachineFolder", systemProperties.strDefaultMachineFolder); + if (systemProperties.strLoggingLevel.length()) + pelmSysProps->setAttribute("LoggingLevel", systemProperties.strLoggingLevel); + if (systemProperties.strDefaultHardDiskFormat.length()) + pelmSysProps->setAttribute("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat); + if (systemProperties.strVRDEAuthLibrary.length()) + pelmSysProps->setAttribute("VRDEAuthLibrary", systemProperties.strVRDEAuthLibrary); + if (systemProperties.strWebServiceAuthLibrary.length()) + pelmSysProps->setAttribute("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary); + if (systemProperties.strDefaultVRDEExtPack.length()) + pelmSysProps->setAttribute("defaultVRDEExtPack", systemProperties.strDefaultVRDEExtPack); + if (systemProperties.strDefaultCryptoExtPack.length()) + pelmSysProps->setAttribute("defaultCryptoExtPack", systemProperties.strDefaultCryptoExtPack); + pelmSysProps->setAttribute("LogHistoryCount", systemProperties.uLogHistoryCount); + if (systemProperties.strAutostartDatabasePath.length()) + pelmSysProps->setAttribute("autostartDatabasePath", systemProperties.strAutostartDatabasePath); + if (systemProperties.strDefaultFrontend.length()) + pelmSysProps->setAttribute("defaultFrontend", systemProperties.strDefaultFrontend); + if (systemProperties.strProxyUrl.length()) + pelmSysProps->setAttribute("proxyUrl", systemProperties.strProxyUrl); + pelmSysProps->setAttribute("proxyMode", systemProperties.uProxyMode); + pelmSysProps->setAttribute("exclusiveHwVirt", systemProperties.fExclusiveHwVirt); + if (systemProperties.strLanguageId.isNotEmpty()) + pelmSysProps->setAttribute("LanguageId", systemProperties.strLanguageId); + + buildUSBDeviceFilters(*pelmGlobal->createChild("USBDeviceFilters"), + host.llUSBDeviceFilters, + true); // fHostMode + + if (!host.llUSBDeviceSources.empty()) + buildUSBDeviceSources(*pelmGlobal->createChild("USBDeviceSources"), + host.llUSBDeviceSources); + + // now go write the XML + xml::XmlFileWriter writer(*m->pDoc); + writer.write(m->strFilename.c_str(), true /*fSafe*/); + + m->fFileExists = true; + + clearDocument(); + LogRel(("Finished saving settings file \"%s\"\n", m->strFilename.c_str())); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Machine XML structures +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +VRDESettings::VRDESettings() : + fEnabled(true), // default for old VMs, for new ones it's false + authType(AuthType_Null), + ulAuthTimeout(5000), + fAllowMultiConnection(false), + fReuseSingleConnection(false) +{ +} + +/** + * Check if all settings have default values. + */ +bool VRDESettings::areDefaultSettings(SettingsVersion_T sv) const +{ + return (sv < SettingsVersion_v1_16 ? fEnabled : !fEnabled) + && authType == AuthType_Null + && (ulAuthTimeout == 5000 || ulAuthTimeout == 0) + && strAuthLibrary.isEmpty() + && !fAllowMultiConnection + && !fReuseSingleConnection + && strVrdeExtPack.isEmpty() + && mapProperties.size() == 0; +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool VRDESettings::operator==(const VRDESettings& v) const +{ + return (this == &v) + || ( fEnabled == v.fEnabled + && authType == v.authType + && ulAuthTimeout == v.ulAuthTimeout + && strAuthLibrary == v.strAuthLibrary + && fAllowMultiConnection == v.fAllowMultiConnection + && fReuseSingleConnection == v.fReuseSingleConnection + && strVrdeExtPack == v.strVrdeExtPack + && mapProperties == v.mapProperties); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +BIOSSettings::BIOSSettings() : + fACPIEnabled(true), + fIOAPICEnabled(false), + fLogoFadeIn(true), + fLogoFadeOut(true), + fPXEDebugEnabled(false), + fSmbiosUuidLittleEndian(true), + ulLogoDisplayTime(0), + biosBootMenuMode(BIOSBootMenuMode_MessageAndMenu), + apicMode(APICMode_APIC), + llTimeOffset(0) +{ +} + +/** + * Check if all settings have default values. + */ +bool BIOSSettings::areDefaultSettings() const +{ + return fACPIEnabled + && !fIOAPICEnabled + && fLogoFadeIn + && fLogoFadeOut + && !fPXEDebugEnabled + && !fSmbiosUuidLittleEndian + && ulLogoDisplayTime == 0 + && biosBootMenuMode == BIOSBootMenuMode_MessageAndMenu + && apicMode == APICMode_APIC + && llTimeOffset == 0 + && strLogoImagePath.isEmpty(); +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool BIOSSettings::operator==(const BIOSSettings &d) const +{ + return (this == &d) + || ( fACPIEnabled == d.fACPIEnabled + && fIOAPICEnabled == d.fIOAPICEnabled + && fLogoFadeIn == d.fLogoFadeIn + && fLogoFadeOut == d.fLogoFadeOut + && fPXEDebugEnabled == d.fPXEDebugEnabled + && fSmbiosUuidLittleEndian == d.fSmbiosUuidLittleEndian + && ulLogoDisplayTime == d.ulLogoDisplayTime + && biosBootMenuMode == d.biosBootMenuMode + && apicMode == d.apicMode + && llTimeOffset == d.llTimeOffset + && strLogoImagePath == d.strLogoImagePath); +} + +RecordingScreenSettings::RecordingScreenSettings(uint32_t a_idScreen /* = UINT32_MAX */) + : idScreen(a_idScreen) +{ + applyDefaults(); +} + +RecordingScreenSettings::~RecordingScreenSettings() +{ + +} + +/** + * Returns the default options string for screen recording settings. + * + * @returns Default options string for a given screen. + */ +/* static */ +const char *RecordingScreenSettings::getDefaultOptions(void) +{ + /* Note: Needs to be kept in sync with FE/Qt's UIMachineSettingsDisplay::putToCache()! */ + return "vc_enabled=true,ac_enabled=false,ac_profile=med"; +} + +/** + * Returns a recording settings feature map from a given string. + * + * @returns VBox status code. + * @param strFeatures String of features to convert. + * @param featureMap Where to return the converted features on success. + */ +/* static */ +int RecordingScreenSettings::featuresFromString(const com::Utf8Str &strFeatures, RecordingFeatureMap &featureMap) +{ + featureMap.clear(); + + RTCList<RTCString> lstFeatures = strFeatures.split(" "); + for (size_t i = 0; i < lstFeatures.size(); i++) + { + if (lstFeatures.at(i).compare("video", RTCString::CaseInsensitive) == 0) + featureMap[RecordingFeature_Video] = true; + else if (lstFeatures.at(i).compare("audio", RTCString::CaseInsensitive) == 0) + featureMap[RecordingFeature_Audio] = true; + /* ignore everything else */ + } + + return VINF_SUCCESS; +} + +/** + * Converts a feature map to a serializable string. + * + * @param featureMap Feature map to convert. + * @param strFeatures Where to return the features converted as a string. + */ +/* static */ +void RecordingScreenSettings::featuresToString(const RecordingFeatureMap &featureMap, com::Utf8Str &strFeatures) +{ + strFeatures = ""; + + RecordingFeatureMap::const_iterator itFeature = featureMap.begin(); + while (itFeature != featureMap.end()) + { + if (itFeature->first == RecordingFeature_Video && itFeature->second) + strFeatures += "video "; + if (itFeature->first == RecordingFeature_Audio && itFeature->second) + strFeatures += "audio "; + ++itFeature; + } + strFeatures.strip(); +} + +/** + * Returns a recording settings audio codec from a given string. + * + * @returns VBox status code. + * @retval VERR_NOT_SUPPORTED if audio codec is invalid or not supported. + * @param strCodec String that contains the codec name. + * @param enmCodec Where to return the audio codec on success. + * + * @note An empty string will return "none" (no codec). + */ +/* static */ +int RecordingScreenSettings::audioCodecFromString(const com::Utf8Str &strCodec, RecordingAudioCodec_T &enmCodec) +{ + if ( RTStrIStr(strCodec.c_str(), "none") + || strCodec.isEmpty()) + { + enmCodec = RecordingAudioCodec_None; + return VINF_SUCCESS; + } + else if (RTStrIStr(strCodec.c_str(), "wav")) + { + enmCodec = RecordingAudioCodec_WavPCM; + return VINF_SUCCESS; + } + else if (RTStrIStr(strCodec.c_str(), "mp3")) + { + enmCodec = RecordingAudioCodec_MP3; + return VINF_SUCCESS; + } + else if (RTStrIStr(strCodec.c_str(), "opus")) + { + enmCodec = RecordingAudioCodec_Opus; + return VINF_SUCCESS; + } + else if (RTStrIStr(strCodec.c_str(), "vorbis")) + { + enmCodec = RecordingAudioCodec_OggVorbis; + return VINF_SUCCESS; + } + + AssertFailedReturn(VERR_NOT_SUPPORTED); +} + +/** + * Converts an audio codec to a serializable string. + * + * @param enmCodec Codec to convert to a string. + * @param strCodec Where to return the audio codec converted as a string. + */ +/* static */ +void RecordingScreenSettings::audioCodecToString(const RecordingAudioCodec_T &enmCodec, com::Utf8Str &strCodec) +{ + switch (enmCodec) + { + case RecordingAudioCodec_None: strCodec = "none"; return; + case RecordingAudioCodec_WavPCM: strCodec = "wav"; return; + case RecordingAudioCodec_MP3: strCodec = "mp3"; return; + case RecordingAudioCodec_Opus: strCodec = "opus"; return; + case RecordingAudioCodec_OggVorbis: strCodec = "vorbis"; return; + default: AssertFailedReturnVoid(); + } +} + +/** + * Returns a recording settings video codec from a given string. + * + * @returns VBox status code. + * @retval VERR_NOT_SUPPORTED if video codec is invalid or not supported. + * @param strCodec String that contains the codec name. + * @param enmCodec Where to return the video codec on success. + * + * @note An empty string will return "none" (no codec). + */ +/* static */ +int RecordingScreenSettings::videoCodecFromString(const com::Utf8Str &strCodec, RecordingVideoCodec_T &enmCodec) +{ + if ( RTStrIStr(strCodec.c_str(), "none") + || strCodec.isEmpty()) + { + enmCodec = RecordingVideoCodec_None; + return VINF_SUCCESS; + } + else if (RTStrIStr(strCodec.c_str(), "MJPEG")) + { + enmCodec = RecordingVideoCodec_MJPEG; + return VINF_SUCCESS; + } + else if (RTStrIStr(strCodec.c_str(), "H262")) + { + enmCodec = RecordingVideoCodec_H262; + return VINF_SUCCESS; + } + else if (RTStrIStr(strCodec.c_str(), "H264")) + { + enmCodec = RecordingVideoCodec_H264; + return VINF_SUCCESS; + } + else if (RTStrIStr(strCodec.c_str(), "H265")) + { + enmCodec = RecordingVideoCodec_H265; + return VINF_SUCCESS; + } + else if (RTStrIStr(strCodec.c_str(), "H266")) + { + enmCodec = RecordingVideoCodec_H266; + return VINF_SUCCESS; + } + else if (RTStrIStr(strCodec.c_str(), "VP8")) + { + enmCodec = RecordingVideoCodec_VP8; + return VINF_SUCCESS; + } + else if (RTStrIStr(strCodec.c_str(), "VP9")) + { + enmCodec = RecordingVideoCodec_VP9; + return VINF_SUCCESS; + } + else if (RTStrIStr(strCodec.c_str(), "AV1")) + { + enmCodec = RecordingVideoCodec_AV1; + return VINF_SUCCESS; + } + else if (RTStrIStr(strCodec.c_str(), "other")) + { + enmCodec = RecordingVideoCodec_Other; + return VINF_SUCCESS; + } + + AssertFailedReturn(VERR_NOT_SUPPORTED); +} + +/** + * Converts a video codec to a serializable string. + * + * @param enmCodec Codec to convert to a string. + * @param strCodec Where to return the video codec converted as a string. + */ +/* static */ +void RecordingScreenSettings::videoCodecToString(const RecordingVideoCodec_T &enmCodec, com::Utf8Str &strCodec) +{ + switch (enmCodec) + { + case RecordingVideoCodec_None: strCodec = "none"; return; + case RecordingVideoCodec_MJPEG: strCodec = "MJPEG"; return; + case RecordingVideoCodec_H262: strCodec = "H262"; return; + case RecordingVideoCodec_H264: strCodec = "H264"; return; + case RecordingVideoCodec_H265: strCodec = "H265"; return; + case RecordingVideoCodec_H266: strCodec = "H266"; return; + case RecordingVideoCodec_VP8: strCodec = "VP8"; return; + case RecordingVideoCodec_VP9: strCodec = "VP9"; return; + case RecordingVideoCodec_AV1: strCodec = "AV1"; return; + case RecordingVideoCodec_Other: strCodec = "other"; return; + default: AssertFailedReturnVoid(); + } +} + +/** + * Applies the default settings. + */ +void RecordingScreenSettings::applyDefaults(void) +{ + /* + * Set sensible defaults. + */ + + /* + * Enable screen 0 by default. + * Otherwise enabling recording without any screen enabled at all makes no sense. + * + * Note: When tweaking this, make sure to also alter RecordingScreenSettings::areDefaultSettings(). + */ + fEnabled = idScreen == 0 ? true : false;; + enmDest = RecordingDestination_File; + ulMaxTimeS = 0; + strOptions = RecordingScreenSettings::getDefaultOptions(); + File.ulMaxSizeMB = 0; + File.strName = ""; + Video.enmCodec = RecordingVideoCodec_VP8; + Video.enmDeadline = RecordingCodecDeadline_Default; + Video.enmRateCtlMode = RecordingRateControlMode_VBR; + Video.enmScalingMode = RecordingVideoScalingMode_None; + Video.ulWidth = 1024; + Video.ulHeight = 768; + Video.ulRate = 512; + Video.ulFPS = 25; +#ifdef VBOX_WITH_AUDIO_RECORDING +# if defined(VBOX_WITH_LIBVORBIS) + Audio.enmCodec = RecordingAudioCodec_OggVorbis; +# else + Audio.enmCodec = RecordingAudioCodec_None; +# endif +#else + Audio.enmCodec = RecordingAudioCodec_None; +#endif /* VBOX_WITH_RECORDING */ + Audio.enmDeadline = RecordingCodecDeadline_Default; + Audio.enmRateCtlMode = RecordingRateControlMode_VBR; + Audio.cBits = 16; + Audio.cChannels = 2; + Audio.uHz = 22050; + + featureMap[RecordingFeature_Video] = true; + featureMap[RecordingFeature_Audio] = false; /** @todo Audio is not yet enabled by default. */ +} + +/** + * Check if all settings have default values. + * + * @returns @c true if default, @c false if not. + */ +bool RecordingScreenSettings::areDefaultSettings(void) const +{ + return ( fEnabled == false + /* Screen 0 is special: There we ALWAYS enable recording by default. */ + || ( idScreen == 0 + && fEnabled == true) + ) + && enmDest == RecordingDestination_File + && ulMaxTimeS == 0 + && strOptions == RecordingScreenSettings::getDefaultOptions() + && File.ulMaxSizeMB == 0 + && File.strName == "" + && Video.enmCodec == RecordingVideoCodec_VP8 + && Video.enmDeadline == RecordingCodecDeadline_Default + && Video.enmRateCtlMode == RecordingRateControlMode_VBR + && Video.enmScalingMode == RecordingVideoScalingMode_None + && Video.ulWidth == 1024 + && Video.ulHeight == 768 + && Video.ulRate == 512 + && Video.ulFPS == 25 +#ifdef VBOX_WITH_AUDIO_RECORDING +# if defined(VBOX_WITH_LIBVORBIS) + && Audio.enmCodec == RecordingAudioCodec_OggVorbis +# else + && Audio.enmCodec == RecordingAudioCodec_None +# endif +#else + && Audio.enmCodec == RecordingAudioCodec_None +#endif /* VBOX_WITH_AUDIO_RECORDING */ + && Audio.enmDeadline == RecordingCodecDeadline_Default + && Audio.enmRateCtlMode == RecordingRateControlMode_VBR + && Audio.cBits == 16 + && Audio.cChannels == 2 + && Audio.uHz == 22050 + && featureMap.find(RecordingFeature_Video)->second == true + && featureMap.find(RecordingFeature_Audio)->second == false; +} + +/** + * Returns if a certain recording feature is enabled or not. + * + * @returns @c true if the feature is enabled, @c false if not. + * @param enmFeature Feature to check. + */ +bool RecordingScreenSettings::isFeatureEnabled(RecordingFeature_T enmFeature) const +{ + RecordingFeatureMap::const_iterator itFeature = featureMap.find(enmFeature); + if (itFeature != featureMap.end()) + return itFeature->second; + + return false; +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool RecordingScreenSettings::operator==(const RecordingScreenSettings &d) const +{ + return fEnabled == d.fEnabled + && enmDest == d.enmDest + && featureMap == d.featureMap + && ulMaxTimeS == d.ulMaxTimeS + && strOptions == d.strOptions + && File.strName == d.File.strName + && File.ulMaxSizeMB == d.File.ulMaxSizeMB + && Video.enmCodec == d.Video.enmCodec + && Video.enmDeadline == d.Video.enmDeadline + && Video.enmRateCtlMode == d.Video.enmRateCtlMode + && Video.enmScalingMode == d.Video.enmScalingMode + && Video.ulWidth == d.Video.ulWidth + && Video.ulHeight == d.Video.ulHeight + && Video.ulRate == d.Video.ulRate + && Video.ulFPS == d.Video.ulFPS + && Audio.enmCodec == d.Audio.enmCodec + && Audio.enmDeadline == d.Audio.enmDeadline + && Audio.enmRateCtlMode == d.Audio.enmRateCtlMode + && Audio.cBits == d.Audio.cBits + && Audio.cChannels == d.Audio.cChannels + && Audio.uHz == d.Audio.uHz + && featureMap == d.featureMap; +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +RecordingCommonSettings::RecordingCommonSettings() +{ + applyDefaults(); +} + +/** + * Applies the default settings. + */ +void RecordingCommonSettings::applyDefaults(void) +{ + fEnabled = false; +} + +/** + * Check if all settings have default values. + */ +bool RecordingCommonSettings::areDefaultSettings(void) const +{ + return fEnabled == false; +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool RecordingCommonSettings::operator==(const RecordingCommonSettings &d) const +{ + if (this == &d) + return true; + + return fEnabled == d.fEnabled; +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +RecordingSettings::RecordingSettings() +{ + applyDefaults(); +} + +/** + * Applies the default settings. + */ +void RecordingSettings::applyDefaults(void) +{ + common.applyDefaults(); + + mapScreens.clear(); + + try + { + /* Always add screen 0 to the default configuration. */ + RecordingScreenSettings screenSettings(0 /* Screen ID */); + + mapScreens[0 /* Screen ID */] = screenSettings; + } + catch (std::bad_alloc &) + { + AssertFailed(); + } +} + +/** + * Check if all settings have default values. + */ +bool RecordingSettings::areDefaultSettings(void) const +{ + AssertReturn(mapScreens.size() >= 1, false); /* The first screen always must be present. */ + + if (!common.areDefaultSettings()) + return false; + + RecordingScreenSettingsMap::const_iterator itScreen = mapScreens.begin(); + while (itScreen != mapScreens.end()) + { + if (!itScreen->second.areDefaultSettings()) + return false; + ++itScreen; + } + + return true; +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool RecordingSettings::operator==(const RecordingSettings &that) const +{ + if (this == &that) /* If pointers match, take a shortcut. */ + return true; + + if (common == that.common) + { + /* Too lazy for a != operator. */ + } + else + return false; + + if (mapScreens.size() != that.mapScreens.size()) + return false; + + RecordingScreenSettingsMap::const_iterator itScreen = mapScreens.begin(); + RecordingScreenSettingsMap::const_iterator itScreenThat = that.mapScreens.begin(); + while ( itScreen != mapScreens.end() + && itScreenThat != that.mapScreens.end()) + { + if (itScreen->second == itScreenThat->second) + { + /* Nothing to do in here (yet). */ + } + else + return false; + + ++itScreen; + ++itScreenThat; + } + + return true; +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +GraphicsAdapter::GraphicsAdapter() : + graphicsControllerType(GraphicsControllerType_VBoxVGA), + ulVRAMSizeMB(8), + cMonitors(1), + fAccelerate3D(false), + fAccelerate2DVideo(false) +{ +} + +/** + * Check if all settings have default values. + */ +bool GraphicsAdapter::areDefaultSettings() const +{ + return graphicsControllerType == GraphicsControllerType_VBoxVGA + && ulVRAMSizeMB == 8 + && cMonitors <= 1 + && !fAccelerate3D + && !fAccelerate2DVideo; +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool GraphicsAdapter::operator==(const GraphicsAdapter &g) const +{ + return (this == &g) + || ( graphicsControllerType == g.graphicsControllerType + && ulVRAMSizeMB == g.ulVRAMSizeMB + && cMonitors == g.cMonitors + && fAccelerate3D == g.fAccelerate3D + && fAccelerate2DVideo == g.fAccelerate2DVideo); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +TpmSettings::TpmSettings() : + tpmType(TpmType_None) +{ +} + +/** + * Check if all settings have default values. + */ +bool TpmSettings::areDefaultSettings() const +{ + return tpmType == TpmType_None + && strLocation.isEmpty(); +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool TpmSettings::operator==(const TpmSettings &g) const +{ + return (this == &g) + || ( tpmType == g.tpmType + && strLocation == g.strLocation); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +NvramSettings::NvramSettings() +{ +} + +/** + * Check if all settings have default values. + */ +bool NvramSettings::areDefaultSettings() const +{ + return strNvramPath.isEmpty() + && strKeyId.isEmpty() + && strKeyStore.isEmpty(); +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool NvramSettings::operator==(const NvramSettings &g) const +{ + return (this == &g) + || (strNvramPath == g.strNvramPath) + || (strKeyId == g.strKeyId) + || (strKeyStore == g.strKeyStore); +} + + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +USBController::USBController() : + enmType(USBControllerType_Null) +{ +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool USBController::operator==(const USBController &u) const +{ + return (this == &u) + || ( strName == u.strName + && enmType == u.enmType); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +USB::USB() +{ +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool USB::operator==(const USB &u) const +{ + return (this == &u) + || ( llUSBControllers == u.llUSBControllers + && llDeviceFilters == u.llDeviceFilters); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +NAT::NAT() : + u32Mtu(0), + u32SockRcv(0), + u32SockSnd(0), + u32TcpRcv(0), + u32TcpSnd(0), + fDNSPassDomain(true), /* historically this value is true */ + fDNSProxy(false), + fDNSUseHostResolver(false), + fAliasLog(false), + fAliasProxyOnly(false), + fAliasUseSamePorts(false), + fLocalhostReachable(true) /* Historically this value is true. */ +{ +} + +/** + * Check if all DNS settings have default values. + */ +bool NAT::areDNSDefaultSettings() const +{ + return fDNSPassDomain && !fDNSProxy && !fDNSUseHostResolver; +} + +/** + * Check if all Alias settings have default values. + */ +bool NAT::areAliasDefaultSettings() const +{ + return !fAliasLog && !fAliasProxyOnly && !fAliasUseSamePorts; +} + +/** + * Check if all TFTP settings have default values. + */ +bool NAT::areTFTPDefaultSettings() const +{ + return strTFTPPrefix.isEmpty() + && strTFTPBootFile.isEmpty() + && strTFTPNextServer.isEmpty(); +} + +/** + * Check whether the localhost-reachable setting is the default for the given settings version. + */ +bool NAT::areLocalhostReachableDefaultSettings(SettingsVersion_T sv) const +{ + return ( fLocalhostReachable + && sv < SettingsVersion_v1_19) + || ( !fLocalhostReachable + && sv >= SettingsVersion_v1_19); +} + +/** + * Check if all settings have default values. + */ +bool NAT::areDefaultSettings(SettingsVersion_T sv) const +{ + /* + * Before settings version 1.19 localhost was reachable by default + * when using NAT which was changed with version 1.19+, see @bugref{9896} + * for more information. + */ + return strNetwork.isEmpty() + && strBindIP.isEmpty() + && u32Mtu == 0 + && u32SockRcv == 0 + && u32SockSnd == 0 + && u32TcpRcv == 0 + && u32TcpSnd == 0 + && areDNSDefaultSettings() + && areAliasDefaultSettings() + && areTFTPDefaultSettings() + && mapRules.size() == 0 + && areLocalhostReachableDefaultSettings(sv); +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool NAT::operator==(const NAT &n) const +{ + return (this == &n) + || ( strNetwork == n.strNetwork + && strBindIP == n.strBindIP + && u32Mtu == n.u32Mtu + && u32SockRcv == n.u32SockRcv + && u32SockSnd == n.u32SockSnd + && u32TcpSnd == n.u32TcpSnd + && u32TcpRcv == n.u32TcpRcv + && strTFTPPrefix == n.strTFTPPrefix + && strTFTPBootFile == n.strTFTPBootFile + && strTFTPNextServer == n.strTFTPNextServer + && fDNSPassDomain == n.fDNSPassDomain + && fDNSProxy == n.fDNSProxy + && fDNSUseHostResolver == n.fDNSUseHostResolver + && fAliasLog == n.fAliasLog + && fAliasProxyOnly == n.fAliasProxyOnly + && fAliasUseSamePorts == n.fAliasUseSamePorts + && fLocalhostReachable == n.fLocalhostReachable + && mapRules == n.mapRules); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +NetworkAdapter::NetworkAdapter() : + ulSlot(0), + type(NetworkAdapterType_Am79C970A), // default for old VMs, for new ones it's Am79C973 + fEnabled(false), + fCableConnected(false), // default for old VMs, for new ones it's true + ulLineSpeed(0), + enmPromiscModePolicy(NetworkAdapterPromiscModePolicy_Deny), + fTraceEnabled(false), + mode(NetworkAttachmentType_Null), + ulBootPriority(0) +{ +} + +/** + * Check if all Generic Driver settings have default values. + */ +bool NetworkAdapter::areGenericDriverDefaultSettings() const +{ + return strGenericDriver.isEmpty() + && genericProperties.size() == 0; +} + +/** + * Check if all settings have default values. + */ +bool NetworkAdapter::areDefaultSettings(SettingsVersion_T sv) const +{ + // 5.0 and earlier had a default of fCableConnected=false, which doesn't + // make a lot of sense (but it's a fact). Later versions don't save the + // setting if it's at the default value and thus must get it right. + return !fEnabled + && strMACAddress.isEmpty() + && ( (sv >= SettingsVersion_v1_16 && fCableConnected && type == NetworkAdapterType_Am79C973) + || (sv < SettingsVersion_v1_16 && !fCableConnected && type == NetworkAdapterType_Am79C970A)) + && ulLineSpeed == 0 + && enmPromiscModePolicy == NetworkAdapterPromiscModePolicy_Deny + && mode == NetworkAttachmentType_Null + && nat.areDefaultSettings(sv) + && strBridgedName.isEmpty() + && strInternalNetworkName.isEmpty() +#ifdef VBOX_WITH_VMNET + && strHostOnlyNetworkName.isEmpty() +#endif /* VBOX_WITH_VMNET */ +#ifdef VBOX_WITH_CLOUD_NET + && strCloudNetworkName.isEmpty() +#endif /* VBOX_WITH_CLOUD_NET */ + && strHostOnlyName.isEmpty() + && areGenericDriverDefaultSettings() + && strNATNetworkName.isEmpty(); +} + +/** + * Special check if settings of the non-current attachment type have default values. + */ +bool NetworkAdapter::areDisabledDefaultSettings(SettingsVersion_T sv) const +{ + return (mode != NetworkAttachmentType_NAT ? nat.areDefaultSettings(sv) : true) + && (mode != NetworkAttachmentType_Bridged ? strBridgedName.isEmpty() : true) + && (mode != NetworkAttachmentType_Internal ? strInternalNetworkName.isEmpty() : true) +#ifdef VBOX_WITH_VMNET + && (mode != NetworkAttachmentType_HostOnlyNetwork ? strHostOnlyNetworkName.isEmpty() : true) +#endif /* VBOX_WITH_VMNET */ +#ifdef VBOX_WITH_CLOUD_NET + && (mode != NetworkAttachmentType_Cloud ? strCloudNetworkName.isEmpty() : true) +#endif /* VBOX_WITH_CLOUD_NET */ + && (mode != NetworkAttachmentType_HostOnly ? strHostOnlyName.isEmpty() : true) + && (mode != NetworkAttachmentType_Generic ? areGenericDriverDefaultSettings() : true) + && (mode != NetworkAttachmentType_NATNetwork ? strNATNetworkName.isEmpty() : true); +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool NetworkAdapter::operator==(const NetworkAdapter &n) const +{ + return (this == &n) + || ( ulSlot == n.ulSlot + && type == n.type + && fEnabled == n.fEnabled + && strMACAddress == n.strMACAddress + && fCableConnected == n.fCableConnected + && ulLineSpeed == n.ulLineSpeed + && enmPromiscModePolicy == n.enmPromiscModePolicy + && fTraceEnabled == n.fTraceEnabled + && strTraceFile == n.strTraceFile + && mode == n.mode + && nat == n.nat + && strBridgedName == n.strBridgedName + && strHostOnlyName == n.strHostOnlyName +#ifdef VBOX_WITH_VMNET + && strHostOnlyNetworkName == n.strHostOnlyNetworkName +#endif /* VBOX_WITH_VMNET */ + && strInternalNetworkName == n.strInternalNetworkName +#ifdef VBOX_WITH_CLOUD_NET + && strCloudNetworkName == n.strCloudNetworkName +#endif /* VBOX_WITH_CLOUD_NET */ + && strGenericDriver == n.strGenericDriver + && genericProperties == n.genericProperties + && ulBootPriority == n.ulBootPriority + && strBandwidthGroup == n.strBandwidthGroup); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +SerialPort::SerialPort() : + ulSlot(0), + fEnabled(false), + ulIOBase(0x3f8), + ulIRQ(4), + portMode(PortMode_Disconnected), + fServer(false), + uartType(UartType_U16550A) +{ +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool SerialPort::operator==(const SerialPort &s) const +{ + return (this == &s) + || ( ulSlot == s.ulSlot + && fEnabled == s.fEnabled + && ulIOBase == s.ulIOBase + && ulIRQ == s.ulIRQ + && portMode == s.portMode + && strPath == s.strPath + && fServer == s.fServer + && uartType == s.uartType); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +ParallelPort::ParallelPort() : + ulSlot(0), + fEnabled(false), + ulIOBase(0x378), + ulIRQ(7) +{ +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool ParallelPort::operator==(const ParallelPort &s) const +{ + return (this == &s) + || ( ulSlot == s.ulSlot + && fEnabled == s.fEnabled + && ulIOBase == s.ulIOBase + && ulIRQ == s.ulIRQ + && strPath == s.strPath); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +AudioAdapter::AudioAdapter() : + fEnabled(true), // default for old VMs, for new ones it's false + fEnabledIn(true), // default for old VMs, for new ones it's false + fEnabledOut(true), // default for old VMs, for new ones it's false + controllerType(AudioControllerType_AC97), + codecType(AudioCodecType_STAC9700), + driverType(AudioDriverType_Null) +{ +} + +/** + * Check if all settings have default values. + */ +bool AudioAdapter::areDefaultSettings(SettingsVersion_T sv) const +{ + return (sv < SettingsVersion_v1_16 ? false : !fEnabled) + && (sv <= SettingsVersion_v1_16 ? fEnabledIn : !fEnabledIn) + && (sv <= SettingsVersion_v1_16 ? fEnabledOut : !fEnabledOut) + && fEnabledOut == true + && controllerType == AudioControllerType_AC97 + && codecType == AudioCodecType_STAC9700 + && properties.size() == 0; +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool AudioAdapter::operator==(const AudioAdapter &a) const +{ + return (this == &a) + || ( fEnabled == a.fEnabled + && fEnabledIn == a.fEnabledIn + && fEnabledOut == a.fEnabledOut + && controllerType == a.controllerType + && codecType == a.codecType + && driverType == a.driverType + && properties == a.properties); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +SharedFolder::SharedFolder() : + fWritable(false), + fAutoMount(false) +{ +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool SharedFolder::operator==(const SharedFolder &g) const +{ + return (this == &g) + || ( strName == g.strName + && strHostPath == g.strHostPath + && fWritable == g.fWritable + && fAutoMount == g.fAutoMount + && strAutoMountPoint == g.strAutoMountPoint); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +GuestProperty::GuestProperty() : + timestamp(0) +{ +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool GuestProperty::operator==(const GuestProperty &g) const +{ + return (this == &g) + || ( strName == g.strName + && strValue == g.strValue + && timestamp == g.timestamp + && strFlags == g.strFlags); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +CpuIdLeaf::CpuIdLeaf() : + idx(UINT32_MAX), + idxSub(0), + uEax(0), + uEbx(0), + uEcx(0), + uEdx(0) +{ +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool CpuIdLeaf::operator==(const CpuIdLeaf &c) const +{ + return (this == &c) + || ( idx == c.idx + && idxSub == c.idxSub + && uEax == c.uEax + && uEbx == c.uEbx + && uEcx == c.uEcx + && uEdx == c.uEdx); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +Cpu::Cpu() : + ulId(UINT32_MAX) +{ +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool Cpu::operator==(const Cpu &c) const +{ + return (this == &c) + || (ulId == c.ulId); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +BandwidthGroup::BandwidthGroup() : + cMaxBytesPerSec(0), + enmType(BandwidthGroupType_Null) +{ +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool BandwidthGroup::operator==(const BandwidthGroup &i) const +{ + return (this == &i) + || ( strName == i.strName + && cMaxBytesPerSec == i.cMaxBytesPerSec + && enmType == i.enmType); +} + +/** + * IOSettings constructor. + */ +IOSettings::IOSettings() : + fIOCacheEnabled(true), + ulIOCacheSize(5) +{ +} + +/** + * Check if all IO Cache settings have default values. + */ +bool IOSettings::areIOCacheDefaultSettings() const +{ + return fIOCacheEnabled + && ulIOCacheSize == 5; +} + +/** + * Check if all settings have default values. + */ +bool IOSettings::areDefaultSettings() const +{ + return areIOCacheDefaultSettings() + && llBandwidthGroups.size() == 0; +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool IOSettings::operator==(const IOSettings &i) const +{ + return (this == &i) + || ( fIOCacheEnabled == i.fIOCacheEnabled + && ulIOCacheSize == i.ulIOCacheSize + && llBandwidthGroups == i.llBandwidthGroups); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +HostPCIDeviceAttachment::HostPCIDeviceAttachment() : + uHostAddress(0), + uGuestAddress(0) +{ +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool HostPCIDeviceAttachment::operator==(const HostPCIDeviceAttachment &a) const +{ + return (this == &a) + || ( uHostAddress == a.uHostAddress + && uGuestAddress == a.uGuestAddress + && strDeviceName == a.strDeviceName); +} + + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +Hardware::Hardware() : + strVersion("1"), + fHardwareVirt(true), + fNestedPaging(true), + fVPID(true), + fUnrestrictedExecution(true), + fHardwareVirtForce(false), + fUseNativeApi(false), + fTripleFaultReset(false), + fPAE(false), + fAPIC(true), + fX2APIC(false), + fIBPBOnVMExit(false), + fIBPBOnVMEntry(false), + fSpecCtrl(false), + fSpecCtrlByHost(false), + fL1DFlushOnSched(true), + fL1DFlushOnVMEntry(false), + fMDSClearOnSched(true), + fMDSClearOnVMEntry(false), + fNestedHWVirt(false), + fVirtVmsaveVmload(true), + enmLongMode(HC_ARCH_BITS == 64 ? Hardware::LongMode_Enabled : Hardware::LongMode_Disabled), + cCPUs(1), + fCpuHotPlug(false), + fHPETEnabled(false), + ulCpuExecutionCap(100), + uCpuIdPortabilityLevel(0), + strCpuProfile("host"), + ulMemorySizeMB((uint32_t)-1), + firmwareType(FirmwareType_BIOS), + pointingHIDType(PointingHIDType_PS2Mouse), + keyboardHIDType(KeyboardHIDType_PS2Keyboard), + chipsetType(ChipsetType_PIIX3), + iommuType(IommuType_None), + paravirtProvider(ParavirtProvider_Legacy), // default for old VMs, for new ones it's ParavirtProvider_Default + strParavirtDebug(""), + fEmulatedUSBCardReader(false), + clipboardMode(ClipboardMode_Disabled), + fClipboardFileTransfersEnabled(false), + dndMode(DnDMode_Disabled), + ulMemoryBalloonSize(0), + fPageFusionEnabled(false) +{ + mapBootOrder[0] = DeviceType_Floppy; + mapBootOrder[1] = DeviceType_DVD; + mapBootOrder[2] = DeviceType_HardDisk; + + /* The default value for PAE depends on the host: + * - 64 bits host -> always true + * - 32 bits host -> true for Windows & Darwin (masked off if the host cpu doesn't support it anyway) + */ +#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN) + fPAE = true; +#endif + + /* The default value of large page supports depends on the host: + * - 64 bits host -> true, unless it's Linux (pending further prediction work due to excessively expensive large page allocations) + * - 32 bits host -> false + */ +#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX) + fLargePages = true; +#else + /* Not supported on 32 bits hosts. */ + fLargePages = false; +#endif +} + +/** + * Check if all Paravirt settings have default values. + */ +bool Hardware::areParavirtDefaultSettings(SettingsVersion_T sv) const +{ + // 5.0 didn't save the paravirt settings if it is ParavirtProvider_Legacy, + // so this default must be kept. Later versions don't save the setting if + // it's at the default value. + return ( (sv >= SettingsVersion_v1_16 && paravirtProvider == ParavirtProvider_Default) + || (sv < SettingsVersion_v1_16 && paravirtProvider == ParavirtProvider_Legacy)) + && strParavirtDebug.isEmpty(); +} + +/** + * Check if all Boot Order settings have default values. + */ +bool Hardware::areBootOrderDefaultSettings() const +{ + BootOrderMap::const_iterator it0 = mapBootOrder.find(0); + BootOrderMap::const_iterator it1 = mapBootOrder.find(1); + BootOrderMap::const_iterator it2 = mapBootOrder.find(2); + BootOrderMap::const_iterator it3 = mapBootOrder.find(3); + return ( mapBootOrder.size() == 3 + || ( mapBootOrder.size() == 4 + && (it3 != mapBootOrder.end() && it3->second == DeviceType_Null))) + && (it0 != mapBootOrder.end() && it0->second == DeviceType_Floppy) + && (it1 != mapBootOrder.end() && it1->second == DeviceType_DVD) + && (it2 != mapBootOrder.end() && it2->second == DeviceType_HardDisk); +} + +/** + * Check if all Network Adapter settings have default values. + */ +bool Hardware::areAllNetworkAdaptersDefaultSettings(SettingsVersion_T sv) const +{ + for (NetworkAdaptersList::const_iterator it = llNetworkAdapters.begin(); + it != llNetworkAdapters.end(); + ++it) + { + if (!it->areDefaultSettings(sv)) + return false; + } + return true; +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool Hardware::operator==(const Hardware& h) const +{ + return (this == &h) + || ( strVersion == h.strVersion + && uuid == h.uuid + && fHardwareVirt == h.fHardwareVirt + && fNestedPaging == h.fNestedPaging + && fLargePages == h.fLargePages + && fVPID == h.fVPID + && fUnrestrictedExecution == h.fUnrestrictedExecution + && fHardwareVirtForce == h.fHardwareVirtForce + && fUseNativeApi == h.fUseNativeApi + && fPAE == h.fPAE + && enmLongMode == h.enmLongMode + && fTripleFaultReset == h.fTripleFaultReset + && fAPIC == h.fAPIC + && fX2APIC == h.fX2APIC + && fIBPBOnVMExit == h.fIBPBOnVMExit + && fIBPBOnVMEntry == h.fIBPBOnVMEntry + && fSpecCtrl == h.fSpecCtrl + && fSpecCtrlByHost == h.fSpecCtrlByHost + && fL1DFlushOnSched == h.fL1DFlushOnSched + && fL1DFlushOnVMEntry == h.fL1DFlushOnVMEntry + && fMDSClearOnSched == h.fMDSClearOnSched + && fMDSClearOnVMEntry == h.fMDSClearOnVMEntry + && fNestedHWVirt == h.fNestedHWVirt + && fVirtVmsaveVmload == h.fVirtVmsaveVmload + && cCPUs == h.cCPUs + && fCpuHotPlug == h.fCpuHotPlug + && ulCpuExecutionCap == h.ulCpuExecutionCap + && uCpuIdPortabilityLevel == h.uCpuIdPortabilityLevel + && strCpuProfile == h.strCpuProfile + && fHPETEnabled == h.fHPETEnabled + && llCpus == h.llCpus + && llCpuIdLeafs == h.llCpuIdLeafs + && ulMemorySizeMB == h.ulMemorySizeMB + && mapBootOrder == h.mapBootOrder + && firmwareType == h.firmwareType + && pointingHIDType == h.pointingHIDType + && keyboardHIDType == h.keyboardHIDType + && chipsetType == h.chipsetType + && iommuType == h.iommuType + && paravirtProvider == h.paravirtProvider + && strParavirtDebug == h.strParavirtDebug + && fEmulatedUSBCardReader == h.fEmulatedUSBCardReader + && vrdeSettings == h.vrdeSettings + && biosSettings == h.biosSettings + && nvramSettings == h.nvramSettings + && graphicsAdapter == h.graphicsAdapter + && usbSettings == h.usbSettings + && tpmSettings == h.tpmSettings + && llNetworkAdapters == h.llNetworkAdapters + && llSerialPorts == h.llSerialPorts + && llParallelPorts == h.llParallelPorts + && audioAdapter == h.audioAdapter + && storage == h.storage + && llSharedFolders == h.llSharedFolders + && clipboardMode == h.clipboardMode + && fClipboardFileTransfersEnabled == h.fClipboardFileTransfersEnabled + && dndMode == h.dndMode + && ulMemoryBalloonSize == h.ulMemoryBalloonSize + && fPageFusionEnabled == h.fPageFusionEnabled + && llGuestProperties == h.llGuestProperties + && ioSettings == h.ioSettings + && pciAttachments == h.pciAttachments + && strDefaultFrontend == h.strDefaultFrontend); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +AttachedDevice::AttachedDevice() : + deviceType(DeviceType_Null), + fPassThrough(false), + fTempEject(false), + fNonRotational(false), + fDiscard(false), + fHotPluggable(false), + lPort(0), + lDevice(0) +{ +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool AttachedDevice::operator==(const AttachedDevice &a) const +{ + return (this == &a) + || ( deviceType == a.deviceType + && fPassThrough == a.fPassThrough + && fTempEject == a.fTempEject + && fNonRotational == a.fNonRotational + && fDiscard == a.fDiscard + && fHotPluggable == a.fHotPluggable + && lPort == a.lPort + && lDevice == a.lDevice + && uuid == a.uuid + && strHostDriveSrc == a.strHostDriveSrc + && strBwGroup == a.strBwGroup); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +StorageController::StorageController() : + storageBus(StorageBus_IDE), + controllerType(StorageControllerType_PIIX3), + ulPortCount(2), + ulInstance(0), + fUseHostIOCache(true), + fBootable(true) +{ +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool StorageController::operator==(const StorageController &s) const +{ + return (this == &s) + || ( strName == s.strName + && storageBus == s.storageBus + && controllerType == s.controllerType + && ulPortCount == s.ulPortCount + && ulInstance == s.ulInstance + && fUseHostIOCache == s.fUseHostIOCache + && llAttachedDevices == s.llAttachedDevices); +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool Storage::operator==(const Storage &s) const +{ + return (this == &s) + || (llStorageControllers == s.llStorageControllers); // deep compare +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +Debugging::Debugging() : + fTracingEnabled(false), + fAllowTracingToAccessVM(false), + strTracingConfig(), + enmDbgProvider(GuestDebugProvider_None), + enmIoProvider(GuestDebugIoProvider_None), + strAddress(), + ulPort(0) +{ +} + +/** + * Check if all settings have default values. + */ +bool Debugging::areDefaultSettings() const +{ + return !fTracingEnabled + && !fAllowTracingToAccessVM + && strTracingConfig.isEmpty() + && enmDbgProvider == GuestDebugProvider_None + && enmIoProvider == GuestDebugIoProvider_None + && strAddress.isEmpty() + && ulPort == 0; +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool Debugging::operator==(const Debugging &d) const +{ + return (this == &d) + || ( fTracingEnabled == d.fTracingEnabled + && fAllowTracingToAccessVM == d.fAllowTracingToAccessVM + && strTracingConfig == d.strTracingConfig + && enmDbgProvider == d.enmDbgProvider + && enmIoProvider == d.enmIoProvider + && strAddress == d.strAddress + && ulPort == d.ulPort); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +Autostart::Autostart() : + fAutostartEnabled(false), + uAutostartDelay(0), + enmAutostopType(AutostopType_Disabled) +{ +} + +/** + * Check if all settings have default values. + */ +bool Autostart::areDefaultSettings() const +{ + return !fAutostartEnabled + && !uAutostartDelay + && enmAutostopType == AutostopType_Disabled; +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool Autostart::operator==(const Autostart &a) const +{ + return (this == &a) + || ( fAutostartEnabled == a.fAutostartEnabled + && uAutostartDelay == a.uAutostartDelay + && enmAutostopType == a.enmAutostopType); +} + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +Snapshot::Snapshot() +{ + RTTimeSpecSetNano(×tamp, 0); +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool Snapshot::operator==(const Snapshot &s) const +{ + return (this == &s) + || ( uuid == s.uuid + && strName == s.strName + && strDescription == s.strDescription + && RTTimeSpecIsEqual(×tamp, &s.timestamp) + && strStateFile == s.strStateFile + && hardware == s.hardware // deep compare + && recordingSettings == s.recordingSettings // deep compare + && llChildSnapshots == s.llChildSnapshots // deep compare + && debugging == s.debugging + && autostart == s.autostart); +} + +const struct Snapshot settings::Snapshot::Empty; /* default ctor is OK */ + +/** + * Constructor. Needs to set sane defaults which stand the test of time. + */ +MachineUserData::MachineUserData() : + fDirectoryIncludesUUID(false), + fNameSync(true), + fTeleporterEnabled(false), + uTeleporterPort(0), + fRTCUseUTC(false), + enmVMPriority(VMProcPriority_Default) +{ + llGroups.push_back("/"); +} + +/** + * Comparison operator. This gets called from MachineConfigFile::operator==, + * which in turn gets called from Machine::saveSettings to figure out whether + * machine settings have really changed and thus need to be written out to disk. + */ +bool MachineUserData::operator==(const MachineUserData &c) const +{ + return (this == &c) + || ( strName == c.strName + && fDirectoryIncludesUUID == c.fDirectoryIncludesUUID + && fNameSync == c.fNameSync + && strDescription == c.strDescription + && llGroups == c.llGroups + && strOsType == c.strOsType + && strSnapshotFolder == c.strSnapshotFolder + && fTeleporterEnabled == c.fTeleporterEnabled + && uTeleporterPort == c.uTeleporterPort + && strTeleporterAddress == c.strTeleporterAddress + && strTeleporterPassword == c.strTeleporterPassword + && fRTCUseUTC == c.fRTCUseUTC + && ovIcon == c.ovIcon + && enmVMPriority == c.enmVMPriority); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// MachineConfigFile +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Constructor. + * + * If pstrFilename is != NULL, this reads the given settings file into the member + * variables and various substructures and lists. Otherwise, the member variables + * are initialized with default values. + * + * Throws variants of xml::Error for I/O, XML and logical content errors, which + * the caller should catch; if this constructor does not throw, then the member + * variables contain meaningful values (either from the file or defaults). + * + * @param pstrFilename + * @param pCryptoIf Pointer to the cryptographic interface, required for an encrypted machine config. + * @param pszPassword The password to use for an encrypted machine config. + */ +MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename, PCVBOXCRYPTOIF pCryptoIf, const char *pszPassword) + : ConfigFileBase(pstrFilename), + enmParseState(ParseState_NotParsed), + fCurrentStateModified(true), + fAborted(false) +{ + RTTimeNow(&timeLastStateChange); + + if (pstrFilename) + { + // the ConfigFileBase constructor has loaded the XML file, so now + // we need only analyze what is in there + + xml::NodesLoop nlRootChildren(*m->pelmRoot); + const xml::ElementNode *pelmRootChild; + while ((pelmRootChild = nlRootChildren.forAllNodes())) + { + if (pelmRootChild->nameEquals("MachineEncrypted")) + readMachineEncrypted(*pelmRootChild, pCryptoIf, pszPassword); + if (pelmRootChild->nameEquals("Machine")) + readMachine(*pelmRootChild); + } + + // clean up memory allocated by XML engine + clearDocument(); + + if (enmParseState == ParseState_NotParsed) + enmParseState = ParseState_Parsed; + } +} + +/** + * Public routine which returns true if this machine config file can have its + * own media registry (which is true for settings version v1.11 and higher, + * i.e. files created by VirtualBox 4.0 and higher). + * @return + */ +bool MachineConfigFile::canHaveOwnMediaRegistry() const +{ + return (m->sv >= SettingsVersion_v1_11); +} + +/** + * Public routine which copies encryption settings. Used by Machine::saveSettings + * so that the encryption settings do not get lost when a copy of the Machine settings + * file is made to see if settings have actually changed. + * @param other + */ +void MachineConfigFile::copyEncryptionSettingsFrom(const MachineConfigFile &other) +{ + strKeyId = other.strKeyId; + strKeyStore = other.strKeyStore; +} + +/** + * Public routine which allows for importing machine XML from an external DOM tree. + * Use this after having called the constructor with a NULL argument. + * + * This is used by the OVF code if a <vbox:Machine> element has been encountered + * in an OVF VirtualSystem element. + * + * @param elmMachine + */ +void MachineConfigFile::importMachineXML(const xml::ElementNode &elmMachine) +{ + // Ideally the version should be mandatory, but since VirtualBox didn't + // care about it until 5.1 came with different defaults, there are OVF + // files created by magicians (not using VirtualBox, which always wrote it) + // which lack this information. Let's hope that they learn to add the + // version when they switch to the newer settings style/defaults of 5.1. + if (!(elmMachine.getAttributeValue("version", m->strSettingsVersionFull))) + m->strSettingsVersionFull = VBOX_XML_IMPORT_VERSION_FULL; + + LogRel(("Import settings with version \"%s\"\n", m->strSettingsVersionFull.c_str())); + + m->sv = parseVersion(m->strSettingsVersionFull, &elmMachine); + + // remember the settings version we read in case it gets upgraded later, + // so we know when to make backups + m->svRead = m->sv; + + readMachine(elmMachine); +} + +/** + * Comparison operator. This gets called from Machine::saveSettings to figure out + * whether machine settings have really changed and thus need to be written out to disk. + * + * Even though this is called operator==, this does NOT compare all fields; the "equals" + * should be understood as "has the same machine config as". The following fields are + * NOT compared: + * -- settings versions and file names inherited from ConfigFileBase; + * -- fCurrentStateModified because that is considered separately in Machine::saveSettings!! + * + * The "deep" comparisons marked below will invoke the operator== functions of the + * structs defined in this file, which may in turn go into comparing lists of + * other structures. As a result, invoking this can be expensive, but it's + * less expensive than writing out XML to disk. + */ +bool MachineConfigFile::operator==(const MachineConfigFile &c) const +{ + return (this == &c) + || ( uuid == c.uuid + && machineUserData == c.machineUserData + && strStateFile == c.strStateFile + && uuidCurrentSnapshot == c.uuidCurrentSnapshot + // skip fCurrentStateModified! + && RTTimeSpecIsEqual(&timeLastStateChange, &c.timeLastStateChange) + && fAborted == c.fAborted + && hardwareMachine == c.hardwareMachine // this one's deep + && mediaRegistry == c.mediaRegistry // this one's deep + // skip mapExtraDataItems! there is no old state available as it's always forced + && llFirstSnapshot == c.llFirstSnapshot // this one's deep + && recordingSettings == c.recordingSettings // this one's deep + && strKeyId == c.strKeyId + && strKeyStore == c.strKeyStore + && strStateKeyId == c.strStateKeyId + && strStateKeyStore == c.strStateKeyStore + && strLogKeyId == c.strLogKeyId + && strLogKeyStore == c.strLogKeyStore); +} + +/** + * Called from MachineConfigFile::readHardware() to read cpu information. + * @param elmCpu + * @param ll + */ +void MachineConfigFile::readCpuTree(const xml::ElementNode &elmCpu, + CpuList &ll) +{ + xml::NodesLoop nl1(elmCpu, "Cpu"); + const xml::ElementNode *pelmCpu; + while ((pelmCpu = nl1.forAllNodes())) + { + Cpu cpu; + + if (!pelmCpu->getAttributeValue("id", cpu.ulId)) + throw ConfigFileError(this, pelmCpu, N_("Required Cpu/@id attribute is missing")); + + ll.push_back(cpu); + } +} + +/** + * Called from MachineConfigFile::readHardware() to cpuid information. + * @param elmCpuid + * @param ll + */ +void MachineConfigFile::readCpuIdTree(const xml::ElementNode &elmCpuid, + CpuIdLeafsList &ll) +{ + xml::NodesLoop nl1(elmCpuid, "CpuIdLeaf"); + const xml::ElementNode *pelmCpuIdLeaf; + while ((pelmCpuIdLeaf = nl1.forAllNodes())) + { + CpuIdLeaf leaf; + + if (!pelmCpuIdLeaf->getAttributeValue("id", leaf.idx)) + throw ConfigFileError(this, pelmCpuIdLeaf, N_("Required CpuId/@id attribute is missing")); + + if (!pelmCpuIdLeaf->getAttributeValue("subleaf", leaf.idxSub)) + leaf.idxSub = 0; + pelmCpuIdLeaf->getAttributeValue("eax", leaf.uEax); + pelmCpuIdLeaf->getAttributeValue("ebx", leaf.uEbx); + pelmCpuIdLeaf->getAttributeValue("ecx", leaf.uEcx); + pelmCpuIdLeaf->getAttributeValue("edx", leaf.uEdx); + + ll.push_back(leaf); + } +} + +/** + * Called from MachineConfigFile::readHardware() to network information. + * @param elmNetwork + * @param ll + */ +void MachineConfigFile::readNetworkAdapters(const xml::ElementNode &elmNetwork, + NetworkAdaptersList &ll) +{ + xml::NodesLoop nl1(elmNetwork, "Adapter"); + const xml::ElementNode *pelmAdapter; + while ((pelmAdapter = nl1.forAllNodes())) + { + NetworkAdapter nic; + + if (m->sv >= SettingsVersion_v1_16) + { + /* Starting with VirtualBox 5.1 the default is cable connected and + * PCnet-FAST III. Needs to match NetworkAdapter.areDefaultSettings(). */ + nic.fCableConnected = true; + nic.type = NetworkAdapterType_Am79C973; + } + + if (!pelmAdapter->getAttributeValue("slot", nic.ulSlot)) + throw ConfigFileError(this, pelmAdapter, N_("Required Adapter/@slot attribute is missing")); + + Utf8Str strTemp; + if (pelmAdapter->getAttributeValue("type", strTemp)) + { + if (strTemp == "Am79C970A") + nic.type = NetworkAdapterType_Am79C970A; + else if (strTemp == "Am79C973") + nic.type = NetworkAdapterType_Am79C973; + else if (strTemp == "Am79C960") + nic.type = NetworkAdapterType_Am79C960; + else if (strTemp == "82540EM") + nic.type = NetworkAdapterType_I82540EM; + else if (strTemp == "82543GC") + nic.type = NetworkAdapterType_I82543GC; + else if (strTemp == "82545EM") + nic.type = NetworkAdapterType_I82545EM; + else if (strTemp == "virtio") + nic.type = NetworkAdapterType_Virtio; + else if (strTemp == "NE1000") + nic.type = NetworkAdapterType_NE1000; + else if (strTemp == "NE2000") + nic.type = NetworkAdapterType_NE2000; + else if (strTemp == "WD8003") + nic.type = NetworkAdapterType_WD8003; + else if (strTemp == "WD8013") + nic.type = NetworkAdapterType_WD8013; + else if (strTemp == "3C503") + nic.type = NetworkAdapterType_ELNK2; + else if (strTemp == "3C501") + nic.type = NetworkAdapterType_ELNK1; + else + throw ConfigFileError(this, pelmAdapter, N_("Invalid value '%s' in Adapter/@type attribute"), strTemp.c_str()); + } + + pelmAdapter->getAttributeValue("enabled", nic.fEnabled); + pelmAdapter->getAttributeValue("MACAddress", nic.strMACAddress); + pelmAdapter->getAttributeValue("cable", nic.fCableConnected); + pelmAdapter->getAttributeValue("speed", nic.ulLineSpeed); + + if (pelmAdapter->getAttributeValue("promiscuousModePolicy", strTemp)) + { + if (strTemp == "Deny") + nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_Deny; + else if (strTemp == "AllowNetwork") + nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowNetwork; + else if (strTemp == "AllowAll") + nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowAll; + else + throw ConfigFileError(this, pelmAdapter, + N_("Invalid value '%s' in Adapter/@promiscuousModePolicy attribute"), strTemp.c_str()); + } + + pelmAdapter->getAttributeValue("trace", nic.fTraceEnabled); + pelmAdapter->getAttributeValue("tracefile", nic.strTraceFile); + pelmAdapter->getAttributeValue("bootPriority", nic.ulBootPriority); + pelmAdapter->getAttributeValue("bandwidthGroup", nic.strBandwidthGroup); + + xml::ElementNodesList llNetworkModes; + pelmAdapter->getChildElements(llNetworkModes); + xml::ElementNodesList::iterator it; + /* We should have only active mode descriptor and disabled modes set */ + if (llNetworkModes.size() > 2) + { + throw ConfigFileError(this, pelmAdapter, N_("Invalid number of modes ('%d') attached to Adapter attribute"), llNetworkModes.size()); + } + for (it = llNetworkModes.begin(); it != llNetworkModes.end(); ++it) + { + const xml::ElementNode *pelmNode = *it; + if (pelmNode->nameEquals("DisabledModes")) + { + xml::ElementNodesList llDisabledNetworkModes; + xml::ElementNodesList::iterator itDisabled; + pelmNode->getChildElements(llDisabledNetworkModes); + /* run over disabled list and load settings */ + for (itDisabled = llDisabledNetworkModes.begin(); + itDisabled != llDisabledNetworkModes.end(); ++itDisabled) + { + const xml::ElementNode *pelmDisabledNode = *itDisabled; + readAttachedNetworkMode(*pelmDisabledNode, false, nic); + } + } + else + readAttachedNetworkMode(*pelmNode, true, nic); + } + // else: default is NetworkAttachmentType_Null + + ll.push_back(nic); + } +} + +void MachineConfigFile::readAttachedNetworkMode(const xml::ElementNode &elmMode, bool fEnabled, NetworkAdapter &nic) +{ + NetworkAttachmentType_T enmAttachmentType = NetworkAttachmentType_Null; + + if (elmMode.nameEquals("NAT")) + { + enmAttachmentType = NetworkAttachmentType_NAT; + + elmMode.getAttributeValue("network", nic.nat.strNetwork); + elmMode.getAttributeValue("hostip", nic.nat.strBindIP); + elmMode.getAttributeValue("mtu", nic.nat.u32Mtu); + elmMode.getAttributeValue("sockrcv", nic.nat.u32SockRcv); + elmMode.getAttributeValue("socksnd", nic.nat.u32SockSnd); + elmMode.getAttributeValue("tcprcv", nic.nat.u32TcpRcv); + elmMode.getAttributeValue("tcpsnd", nic.nat.u32TcpSnd); + elmMode.getAttributeValue("localhost-reachable", nic.nat.fLocalhostReachable); + const xml::ElementNode *pelmDNS; + if ((pelmDNS = elmMode.findChildElement("DNS"))) + { + pelmDNS->getAttributeValue("pass-domain", nic.nat.fDNSPassDomain); + pelmDNS->getAttributeValue("use-proxy", nic.nat.fDNSProxy); + pelmDNS->getAttributeValue("use-host-resolver", nic.nat.fDNSUseHostResolver); + } + const xml::ElementNode *pelmAlias; + if ((pelmAlias = elmMode.findChildElement("Alias"))) + { + pelmAlias->getAttributeValue("logging", nic.nat.fAliasLog); + pelmAlias->getAttributeValue("proxy-only", nic.nat.fAliasProxyOnly); + pelmAlias->getAttributeValue("use-same-ports", nic.nat.fAliasUseSamePorts); + } + const xml::ElementNode *pelmTFTP; + if ((pelmTFTP = elmMode.findChildElement("TFTP"))) + { + pelmTFTP->getAttributeValue("prefix", nic.nat.strTFTPPrefix); + pelmTFTP->getAttributeValue("boot-file", nic.nat.strTFTPBootFile); + pelmTFTP->getAttributeValue("next-server", nic.nat.strTFTPNextServer); + } + + readNATForwardRulesMap(elmMode, nic.nat.mapRules); + } + else if ( elmMode.nameEquals("HostInterface") + || elmMode.nameEquals("BridgedInterface")) + { + enmAttachmentType = NetworkAttachmentType_Bridged; + + // optional network name, cannot be required or we have trouble with + // settings which are saved before configuring the network name + elmMode.getAttributeValue("name", nic.strBridgedName); + } + else if (elmMode.nameEquals("InternalNetwork")) + { + enmAttachmentType = NetworkAttachmentType_Internal; + + // optional network name, cannot be required or we have trouble with + // settings which are saved before configuring the network name + elmMode.getAttributeValue("name", nic.strInternalNetworkName); + } + else if (elmMode.nameEquals("HostOnlyInterface")) + { + enmAttachmentType = NetworkAttachmentType_HostOnly; + + // optional network name, cannot be required or we have trouble with + // settings which are saved before configuring the network name + elmMode.getAttributeValue("name", nic.strHostOnlyName); + } +#ifdef VBOX_WITH_VMNET + else if (elmMode.nameEquals("HostOnlyNetwork")) + { + enmAttachmentType = NetworkAttachmentType_HostOnlyNetwork; + + // optional network name, cannot be required or we have trouble with + // settings which are saved before configuring the network name + elmMode.getAttributeValue("name", nic.strHostOnlyNetworkName); + } +#endif /* VBOX_WITH_VMNET */ + else if (elmMode.nameEquals("GenericInterface")) + { + enmAttachmentType = NetworkAttachmentType_Generic; + + elmMode.getAttributeValue("driver", nic.strGenericDriver); // optional network attachment driver + + // get all properties + xml::NodesLoop nl(elmMode); + const xml::ElementNode *pelmModeChild; + while ((pelmModeChild = nl.forAllNodes())) + { + if (pelmModeChild->nameEquals("Property")) + { + Utf8Str strPropName, strPropValue; + if ( pelmModeChild->getAttributeValue("name", strPropName) + && pelmModeChild->getAttributeValue("value", strPropValue) ) + nic.genericProperties[strPropName] = strPropValue; + else + throw ConfigFileError(this, pelmModeChild, N_("Required GenericInterface/Property/@name or @value attribute is missing")); + } + } + } + else if (elmMode.nameEquals("NATNetwork")) + { + enmAttachmentType = NetworkAttachmentType_NATNetwork; + + // optional network name, cannot be required or we have trouble with + // settings which are saved before configuring the network name + elmMode.getAttributeValue("name", nic.strNATNetworkName); + } + else if (elmMode.nameEquals("VDE")) + { + // inofficial hack (VDE networking was never part of the official + // settings, so it's not mentioned in VirtualBox-settings.xsd) + enmAttachmentType = NetworkAttachmentType_Generic; + + com::Utf8Str strVDEName; + elmMode.getAttributeValue("network", strVDEName); // optional network name + nic.strGenericDriver = "VDE"; + nic.genericProperties["network"] = strVDEName; + } +#ifdef VBOX_WITH_VMNET + else if (elmMode.nameEquals("HostOnlyNetwork")) + { + enmAttachmentType = NetworkAttachmentType_HostOnly; + + // optional network name, cannot be required or we have trouble with + // settings which are saved before configuring the network name + elmMode.getAttributeValue("name", nic.strHostOnlyNetworkName); + } +#endif /* VBOX_WITH_VMNET */ +#ifdef VBOX_WITH_CLOUD_NET + else if (elmMode.nameEquals("CloudNetwork")) + { + enmAttachmentType = NetworkAttachmentType_Cloud; + + // optional network name, cannot be required or we have trouble with + // settings which are saved before configuring the network name + elmMode.getAttributeValue("name", nic.strCloudNetworkName); + } +#endif /* VBOX_WITH_CLOUD_NET */ + + if (fEnabled && enmAttachmentType != NetworkAttachmentType_Null) + nic.mode = enmAttachmentType; +} + +/** + * Called from MachineConfigFile::readHardware() to read serial port information. + * @param elmUART + * @param ll + */ +void MachineConfigFile::readSerialPorts(const xml::ElementNode &elmUART, + SerialPortsList &ll) +{ + xml::NodesLoop nl1(elmUART, "Port"); + const xml::ElementNode *pelmPort; + while ((pelmPort = nl1.forAllNodes())) + { + SerialPort port; + if (!pelmPort->getAttributeValue("slot", port.ulSlot)) + throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@slot attribute is missing")); + + // slot must be unique + for (SerialPortsList::const_iterator it = ll.begin(); + it != ll.end(); + ++it) + if ((*it).ulSlot == port.ulSlot) + throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in UART/Port/@slot attribute: value is not unique"), port.ulSlot); + + if (!pelmPort->getAttributeValue("enabled", port.fEnabled)) + throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@enabled attribute is missing")); + if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase)) + throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IOBase attribute is missing")); + if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ)) + throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IRQ attribute is missing")); + + Utf8Str strPortMode; + if (!pelmPort->getAttributeValue("hostMode", strPortMode)) + throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@hostMode attribute is missing")); + if (strPortMode == "RawFile") + port.portMode = PortMode_RawFile; + else if (strPortMode == "HostPipe") + port.portMode = PortMode_HostPipe; + else if (strPortMode == "HostDevice") + port.portMode = PortMode_HostDevice; + else if (strPortMode == "Disconnected") + port.portMode = PortMode_Disconnected; + else if (strPortMode == "TCP") + port.portMode = PortMode_TCP; + else + throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@hostMode attribute"), strPortMode.c_str()); + + pelmPort->getAttributeValue("path", port.strPath); + pelmPort->getAttributeValue("server", port.fServer); + + Utf8Str strUartType; + if (pelmPort->getAttributeValue("uartType", strUartType)) + { + if (strUartType == "16450") + port.uartType = UartType_U16450; + else if (strUartType == "16550A") + port.uartType = UartType_U16550A; + else if (strUartType == "16750") + port.uartType = UartType_U16750; + else + throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@uartType attribute"), strUartType.c_str()); + } + + ll.push_back(port); + } +} + +/** + * Called from MachineConfigFile::readHardware() to read parallel port information. + * @param elmLPT + * @param ll + */ +void MachineConfigFile::readParallelPorts(const xml::ElementNode &elmLPT, + ParallelPortsList &ll) +{ + xml::NodesLoop nl1(elmLPT, "Port"); + const xml::ElementNode *pelmPort; + while ((pelmPort = nl1.forAllNodes())) + { + ParallelPort port; + if (!pelmPort->getAttributeValue("slot", port.ulSlot)) + throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@slot attribute is missing")); + + // slot must be unique + for (ParallelPortsList::const_iterator it = ll.begin(); + it != ll.end(); + ++it) + if ((*it).ulSlot == port.ulSlot) + throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in LPT/Port/@slot attribute: value is not unique"), port.ulSlot); + + if (!pelmPort->getAttributeValue("enabled", port.fEnabled)) + throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@enabled attribute is missing")); + if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase)) + throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IOBase attribute is missing")); + if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ)) + throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IRQ attribute is missing")); + + pelmPort->getAttributeValue("path", port.strPath); + + ll.push_back(port); + } +} + +/** + * Called from MachineConfigFile::readHardware() to read audio adapter information + * and maybe fix driver information depending on the current host hardware. + * + * @param elmAudioAdapter "AudioAdapter" XML element. + * @param aa + */ +void MachineConfigFile::readAudioAdapter(const xml::ElementNode &elmAudioAdapter, + AudioAdapter &aa) +{ + if (m->sv >= SettingsVersion_v1_15) + { + // get all properties + xml::NodesLoop nl1(elmAudioAdapter, "Property"); + const xml::ElementNode *pelmModeChild; + while ((pelmModeChild = nl1.forAllNodes())) + { + Utf8Str strPropName, strPropValue; + if ( pelmModeChild->getAttributeValue("name", strPropName) + && pelmModeChild->getAttributeValue("value", strPropValue) ) + aa.properties[strPropName] = strPropValue; + else + throw ConfigFileError(this, pelmModeChild, N_("Required AudioAdapter/Property/@name or @value attribute " + "is missing")); + } + } + + elmAudioAdapter.getAttributeValue("enabled", aa.fEnabled); + elmAudioAdapter.getAttributeValue("enabledIn", aa.fEnabledIn); + elmAudioAdapter.getAttributeValue("enabledOut", aa.fEnabledOut); + + Utf8Str strTemp; + if (elmAudioAdapter.getAttributeValue("controller", strTemp)) + { + if (strTemp == "SB16") + aa.controllerType = AudioControllerType_SB16; + else if (strTemp == "AC97") + aa.controllerType = AudioControllerType_AC97; + else if (strTemp == "HDA") + aa.controllerType = AudioControllerType_HDA; + else + throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@controller attribute"), strTemp.c_str()); + } + + if (elmAudioAdapter.getAttributeValue("codec", strTemp)) + { + if (strTemp == "SB16") + aa.codecType = AudioCodecType_SB16; + else if (strTemp == "STAC9700") + aa.codecType = AudioCodecType_STAC9700; + else if (strTemp == "AD1980") + aa.codecType = AudioCodecType_AD1980; + else if (strTemp == "STAC9221") + aa.codecType = AudioCodecType_STAC9221; + else + throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@codec attribute"), strTemp.c_str()); + } + else + { + /* No codec attribute provided; use defaults. */ + switch (aa.controllerType) + { + case AudioControllerType_AC97: + aa.codecType = AudioCodecType_STAC9700; + break; + case AudioControllerType_SB16: + aa.codecType = AudioCodecType_SB16; + break; + case AudioControllerType_HDA: + aa.codecType = AudioCodecType_STAC9221; + break; + default: + Assert(false); /* We just checked the controller type above. */ + } + } + + if (elmAudioAdapter.getAttributeValue("driver", strTemp)) + { + // settings before 1.3 used lower case so make sure this is case-insensitive + strTemp.toUpper(); + if (strTemp == "DEFAULT") /* Keep this to be backwards compatible for settings < r152556. */ + aa.driverType = AudioDriverType_Default; + else if (strTemp == "NULL") + aa.driverType = AudioDriverType_Null; + else if (strTemp == "WAS") + aa.driverType = AudioDriverType_WAS; + else if (strTemp == "WINMM") + aa.driverType = AudioDriverType_WinMM; + else if ( (strTemp == "DIRECTSOUND") || (strTemp == "DSOUND") ) + aa.driverType = AudioDriverType_DirectSound; + else if (strTemp == "SOLAUDIO") /* Deprecated -- Solaris will use OSS by default now. */ + aa.driverType = AudioDriverType_SolAudio; + else if (strTemp == "ALSA") + aa.driverType = AudioDriverType_ALSA; + else if (strTemp == "PULSE") + aa.driverType = AudioDriverType_Pulse; + else if (strTemp == "OSS") + aa.driverType = AudioDriverType_OSS; + else if (strTemp == "COREAUDIO") + aa.driverType = AudioDriverType_CoreAudio; + else if (strTemp == "MMPM") /* Deprecated; only kept for backwards compatibility. */ + aa.driverType = AudioDriverType_MMPM; + else + throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@driver attribute"), strTemp.c_str()); + + /* When loading settings >= 1.19 (VBox 7.0), the attribute "useDefault" will determine if the VM should use + * the OS' default audio driver or not. This additional attribute is necessary in order to be backwards compatible + * with older VBox versions. */ + bool fUseDefault = false; + if ( elmAudioAdapter.getAttributeValue("useDefault", &fUseDefault) /* Overrides "driver" above (if set). */ + && fUseDefault) + aa.driverType = AudioDriverType_Default; + + // now check if this is actually supported on the current host platform; + // people might be opening a file created on a Windows host, and that + // VM should still start on a Linux host + if (!isAudioDriverAllowedOnThisHost(aa.driverType)) + aa.driverType = getHostDefaultAudioDriver(); + } +} + +/** + * Called from MachineConfigFile::readHardware() to read guest property information. + * @param elmGuestProperties + * @param hw + */ +void MachineConfigFile::readGuestProperties(const xml::ElementNode &elmGuestProperties, + Hardware &hw) +{ + xml::NodesLoop nl1(elmGuestProperties, "GuestProperty"); + const xml::ElementNode *pelmProp; + while ((pelmProp = nl1.forAllNodes())) + { + GuestProperty prop; + + pelmProp->getAttributeValue("name", prop.strName); + pelmProp->getAttributeValue("value", prop.strValue); + + pelmProp->getAttributeValue("timestamp", prop.timestamp); + pelmProp->getAttributeValue("flags", prop.strFlags); + + /* Check guest property 'name' and 'value' for correctness before + * placing it to local cache. */ + + int rc = GuestPropValidateName(prop.strName.c_str(), prop.strName.length() + 1 /* '\0' */); + if (RT_FAILURE(rc)) + { + LogRel(("WARNING: Guest property with invalid name (%s) present in VM configuration file. Guest property will be dropped.\n", + prop.strName.c_str())); + continue; + } + + rc = GuestPropValidateValue(prop.strValue.c_str(), prop.strValue.length() + 1 /* '\0' */); + if (rc == VERR_TOO_MUCH_DATA) + { + LogRel(("WARNING: Guest property '%s' present in VM configuration file and has too long value. Guest property value will be truncated.\n", + prop.strName.c_str())); + + /* In order to pass validation, guest property value length (including '\0') in bytes + * should be less than GUEST_PROP_MAX_VALUE_LEN. Chop it down to an appropriate length. */ + prop.strValue.truncate(GUEST_PROP_MAX_VALUE_LEN - 1 /*terminator*/); + } + else if (RT_FAILURE(rc)) + { + LogRel(("WARNING: Guest property '%s' present in VM configuration file and has invalid value. Guest property will be dropped.\n", + prop.strName.c_str())); + continue; + } + + hw.llGuestProperties.push_back(prop); + } +} + +/** + * Helper function to read attributes that are common to \<SATAController\> (pre-1.7) + * and \<StorageController\>. + * @param elmStorageController + * @param sctl + */ +void MachineConfigFile::readStorageControllerAttributes(const xml::ElementNode &elmStorageController, + StorageController &sctl) +{ + elmStorageController.getAttributeValue("PortCount", sctl.ulPortCount); + elmStorageController.getAttributeValue("useHostIOCache", sctl.fUseHostIOCache); +} + +/** + * Reads in a \<Hardware\> block and stores it in the given structure. Used + * both directly from readMachine and from readSnapshot, since snapshots + * have their own hardware sections. + * + * For legacy pre-1.7 settings we also need a storage structure because + * the IDE and SATA controllers used to be defined under \<Hardware\>. + * + * @param elmHardware Hardware node to read from. + * @param hw Where to store the hardware settings. + */ +void MachineConfigFile::readHardware(const xml::ElementNode &elmHardware, + Hardware &hw) +{ + if (m->sv >= SettingsVersion_v1_16) + { + /* Starting with VirtualBox 5.1 the default is Default, before it was + * Legacy. This needs to matched by areParavirtDefaultSettings(). */ + hw.paravirtProvider = ParavirtProvider_Default; + /* The new default is disabled, before it was enabled by default. */ + hw.vrdeSettings.fEnabled = false; + /* The new default is disabled, before it was enabled by default. */ + hw.audioAdapter.fEnabled = false; + } + + if (m->sv >= SettingsVersion_v1_17) + { + /* Starting with VirtualBox 5.2 the default is disabled, before it was + * enabled. This needs to matched by AudioAdapter::areDefaultSettings(). */ + hw.audioAdapter.fEnabledIn = false; + /* The new default is disabled, before it was enabled by default. */ + hw.audioAdapter.fEnabledOut = false; + } + + if (!elmHardware.getAttributeValue("version", hw.strVersion)) + { + /* KLUDGE ALERT! For a while during the 3.1 development this was not + written because it was thought to have a default value of "2". For + sv <= 1.3 it defaults to "1" because the attribute didn't exist, + while for 1.4+ it is sort of mandatory. Now, the buggy XML writer + code only wrote 1.7 and later. So, if it's a 1.7+ XML file and it's + missing the hardware version, then it probably should be "2" instead + of "1". */ + if (m->sv < SettingsVersion_v1_7) + hw.strVersion = "1"; + else + hw.strVersion = "2"; + } + Utf8Str strUUID; + if (elmHardware.getAttributeValue("uuid", strUUID)) + parseUUID(hw.uuid, strUUID, &elmHardware); + + xml::NodesLoop nl1(elmHardware); + const xml::ElementNode *pelmHwChild; + while ((pelmHwChild = nl1.forAllNodes())) + { + if (pelmHwChild->nameEquals("CPU")) + { + if (!pelmHwChild->getAttributeValue("count", hw.cCPUs)) + { + // pre-1.5 variant; not sure if this actually exists in the wild anywhere + const xml::ElementNode *pelmCPUChild; + if ((pelmCPUChild = pelmHwChild->findChildElement("CPUCount"))) + pelmCPUChild->getAttributeValue("count", hw.cCPUs); + } + + pelmHwChild->getAttributeValue("hotplug", hw.fCpuHotPlug); + pelmHwChild->getAttributeValue("executionCap", hw.ulCpuExecutionCap); + + const xml::ElementNode *pelmCPUChild; + if (hw.fCpuHotPlug) + { + if ((pelmCPUChild = pelmHwChild->findChildElement("CpuTree"))) + readCpuTree(*pelmCPUChild, hw.llCpus); + } + + if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtEx"))) + { + pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirt); + } + if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExNestedPaging"))) + pelmCPUChild->getAttributeValue("enabled", hw.fNestedPaging); + if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExLargePages"))) + pelmCPUChild->getAttributeValue("enabled", hw.fLargePages); + if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExVPID"))) + pelmCPUChild->getAttributeValue("enabled", hw.fVPID); + if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExUX"))) + pelmCPUChild->getAttributeValue("enabled", hw.fUnrestrictedExecution); + if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtForce"))) + pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirtForce); + if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExUseNativeApi"))) + pelmCPUChild->getAttributeValue("enabled", hw.fUseNativeApi); + if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExVirtVmsaveVmload"))) + pelmCPUChild->getAttributeValue("enabled", hw.fVirtVmsaveVmload); + + if (!(pelmCPUChild = pelmHwChild->findChildElement("PAE"))) + { + /* The default for pre 3.1 was false, so we must respect that. */ + if (m->sv < SettingsVersion_v1_9) + hw.fPAE = false; + } + else + pelmCPUChild->getAttributeValue("enabled", hw.fPAE); + + bool fLongMode; + if ( (pelmCPUChild = pelmHwChild->findChildElement("LongMode")) + && pelmCPUChild->getAttributeValue("enabled", fLongMode) ) + hw.enmLongMode = fLongMode ? Hardware::LongMode_Enabled : Hardware::LongMode_Disabled; + else + hw.enmLongMode = Hardware::LongMode_Legacy; + + if ((pelmCPUChild = pelmHwChild->findChildElement("SyntheticCpu"))) + { + bool fSyntheticCpu = false; + pelmCPUChild->getAttributeValue("enabled", fSyntheticCpu); + hw.uCpuIdPortabilityLevel = fSyntheticCpu ? 1 : 0; + } + pelmHwChild->getAttributeValue("CpuIdPortabilityLevel", hw.uCpuIdPortabilityLevel); + pelmHwChild->getAttributeValue("CpuProfile", hw.strCpuProfile); + + if ((pelmCPUChild = pelmHwChild->findChildElement("TripleFaultReset"))) + pelmCPUChild->getAttributeValue("enabled", hw.fTripleFaultReset); + + if ((pelmCPUChild = pelmHwChild->findChildElement("APIC"))) + pelmCPUChild->getAttributeValue("enabled", hw.fAPIC); + if ((pelmCPUChild = pelmHwChild->findChildElement("X2APIC"))) + pelmCPUChild->getAttributeValue("enabled", hw.fX2APIC); + if (hw.fX2APIC) + hw.fAPIC = true; + pelmCPUChild = pelmHwChild->findChildElement("IBPBOn"); + if (pelmCPUChild) + { + pelmCPUChild->getAttributeValue("vmexit", hw.fIBPBOnVMExit); + pelmCPUChild->getAttributeValue("vmentry", hw.fIBPBOnVMEntry); + } + pelmCPUChild = pelmHwChild->findChildElement("SpecCtrl"); + if (pelmCPUChild) + pelmCPUChild->getAttributeValue("enabled", hw.fSpecCtrl); + pelmCPUChild = pelmHwChild->findChildElement("SpecCtrlByHost"); + if (pelmCPUChild) + pelmCPUChild->getAttributeValue("enabled", hw.fSpecCtrlByHost); + pelmCPUChild = pelmHwChild->findChildElement("L1DFlushOn"); + if (pelmCPUChild) + { + pelmCPUChild->getAttributeValue("scheduling", hw.fL1DFlushOnSched); + pelmCPUChild->getAttributeValue("vmentry", hw.fL1DFlushOnVMEntry); + } + pelmCPUChild = pelmHwChild->findChildElement("MDSClearOn"); + if (pelmCPUChild) + { + pelmCPUChild->getAttributeValue("scheduling", hw.fMDSClearOnSched); + pelmCPUChild->getAttributeValue("vmentry", hw.fMDSClearOnVMEntry); + } + pelmCPUChild = pelmHwChild->findChildElement("NestedHWVirt"); + if (pelmCPUChild) + pelmCPUChild->getAttributeValue("enabled", hw.fNestedHWVirt); + + if ((pelmCPUChild = pelmHwChild->findChildElement("CpuIdTree"))) + readCpuIdTree(*pelmCPUChild, hw.llCpuIdLeafs); + } + else if (pelmHwChild->nameEquals("Memory")) + { + pelmHwChild->getAttributeValue("RAMSize", hw.ulMemorySizeMB); + pelmHwChild->getAttributeValue("PageFusion", hw.fPageFusionEnabled); + } + else if (pelmHwChild->nameEquals("Firmware")) + { + Utf8Str strFirmwareType; + if (pelmHwChild->getAttributeValue("type", strFirmwareType)) + { + if ( (strFirmwareType == "BIOS") + || (strFirmwareType == "1") // some trunk builds used the number here + ) + hw.firmwareType = FirmwareType_BIOS; + else if ( (strFirmwareType == "EFI") + || (strFirmwareType == "2") // some trunk builds used the number here + ) + hw.firmwareType = FirmwareType_EFI; + else if ( strFirmwareType == "EFI32") + hw.firmwareType = FirmwareType_EFI32; + else if ( strFirmwareType == "EFI64") + hw.firmwareType = FirmwareType_EFI64; + else if ( strFirmwareType == "EFIDUAL") + hw.firmwareType = FirmwareType_EFIDUAL; + else + throw ConfigFileError(this, + pelmHwChild, + N_("Invalid value '%s' in Firmware/@type"), + strFirmwareType.c_str()); + } + } + else if (pelmHwChild->nameEquals("HID")) + { + Utf8Str strHIDType; + if (pelmHwChild->getAttributeValue("Keyboard", strHIDType)) + { + if (strHIDType == "None") + hw.keyboardHIDType = KeyboardHIDType_None; + else if (strHIDType == "USBKeyboard") + hw.keyboardHIDType = KeyboardHIDType_USBKeyboard; + else if (strHIDType == "PS2Keyboard") + hw.keyboardHIDType = KeyboardHIDType_PS2Keyboard; + else if (strHIDType == "ComboKeyboard") + hw.keyboardHIDType = KeyboardHIDType_ComboKeyboard; + else + throw ConfigFileError(this, + pelmHwChild, + N_("Invalid value '%s' in HID/Keyboard/@type"), + strHIDType.c_str()); + } + if (pelmHwChild->getAttributeValue("Pointing", strHIDType)) + { + if (strHIDType == "None") + hw.pointingHIDType = PointingHIDType_None; + else if (strHIDType == "USBMouse") + hw.pointingHIDType = PointingHIDType_USBMouse; + else if (strHIDType == "USBTablet") + hw.pointingHIDType = PointingHIDType_USBTablet; + else if (strHIDType == "PS2Mouse") + hw.pointingHIDType = PointingHIDType_PS2Mouse; + else if (strHIDType == "ComboMouse") + hw.pointingHIDType = PointingHIDType_ComboMouse; + else if (strHIDType == "USBMultiTouch") + hw.pointingHIDType = PointingHIDType_USBMultiTouch; + else if (strHIDType == "USBMTScreenPlusPad") + hw.pointingHIDType = PointingHIDType_USBMultiTouchScreenPlusPad; + else + throw ConfigFileError(this, + pelmHwChild, + N_("Invalid value '%s' in HID/Pointing/@type"), + strHIDType.c_str()); + } + } + else if (pelmHwChild->nameEquals("Chipset")) + { + Utf8Str strChipsetType; + if (pelmHwChild->getAttributeValue("type", strChipsetType)) + { + if (strChipsetType == "PIIX3") + hw.chipsetType = ChipsetType_PIIX3; + else if (strChipsetType == "ICH9") + hw.chipsetType = ChipsetType_ICH9; + else + throw ConfigFileError(this, + pelmHwChild, + N_("Invalid value '%s' in Chipset/@type"), + strChipsetType.c_str()); + } + } + else if (pelmHwChild->nameEquals("Iommu")) + { + Utf8Str strIommuType; + if (pelmHwChild->getAttributeValue("type", strIommuType)) + { + if (strIommuType == "None") + hw.iommuType = IommuType_None; + else if (strIommuType == "Automatic") + hw.iommuType = IommuType_Automatic; + else if (strIommuType == "AMD") + hw.iommuType = IommuType_AMD; + else if (strIommuType == "Intel") + hw.iommuType = IommuType_Intel; + else + throw ConfigFileError(this, + pelmHwChild, + N_("Invalid value '%s' in Iommu/@type"), + strIommuType.c_str()); + } + } + else if (pelmHwChild->nameEquals("Paravirt")) + { + Utf8Str strProvider; + if (pelmHwChild->getAttributeValue("provider", strProvider)) + { + if (strProvider == "None") + hw.paravirtProvider = ParavirtProvider_None; + else if (strProvider == "Default") + hw.paravirtProvider = ParavirtProvider_Default; + else if (strProvider == "Legacy") + hw.paravirtProvider = ParavirtProvider_Legacy; + else if (strProvider == "Minimal") + hw.paravirtProvider = ParavirtProvider_Minimal; + else if (strProvider == "HyperV") + hw.paravirtProvider = ParavirtProvider_HyperV; + else if (strProvider == "KVM") + hw.paravirtProvider = ParavirtProvider_KVM; + else + throw ConfigFileError(this, + pelmHwChild, + N_("Invalid value '%s' in Paravirt/@provider attribute"), + strProvider.c_str()); + } + + pelmHwChild->getAttributeValue("debug", hw.strParavirtDebug); + } + else if (pelmHwChild->nameEquals("HPET")) + { + pelmHwChild->getAttributeValue("enabled", hw.fHPETEnabled); + } + else if (pelmHwChild->nameEquals("Boot")) + { + hw.mapBootOrder.clear(); + + xml::NodesLoop nl2(*pelmHwChild, "Order"); + const xml::ElementNode *pelmOrder; + while ((pelmOrder = nl2.forAllNodes())) + { + uint32_t ulPos; + Utf8Str strDevice; + if (!pelmOrder->getAttributeValue("position", ulPos)) + throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@position attribute is missing")); + + if ( ulPos < 1 + || ulPos > SchemaDefs::MaxBootPosition + ) + throw ConfigFileError(this, + pelmOrder, + N_("Invalid value '%RU32' in Boot/Order/@position: must be greater than 0 and less than %RU32"), + ulPos, + SchemaDefs::MaxBootPosition + 1); + // XML is 1-based but internal data is 0-based + --ulPos; + + if (hw.mapBootOrder.find(ulPos) != hw.mapBootOrder.end()) + throw ConfigFileError(this, pelmOrder, N_("Invalid value '%RU32' in Boot/Order/@position: value is not unique"), ulPos); + + if (!pelmOrder->getAttributeValue("device", strDevice)) + throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@device attribute is missing")); + + DeviceType_T type; + if (strDevice == "None") + type = DeviceType_Null; + else if (strDevice == "Floppy") + type = DeviceType_Floppy; + else if (strDevice == "DVD") + type = DeviceType_DVD; + else if (strDevice == "HardDisk") + type = DeviceType_HardDisk; + else if (strDevice == "Network") + type = DeviceType_Network; + else + throw ConfigFileError(this, pelmOrder, N_("Invalid value '%s' in Boot/Order/@device attribute"), strDevice.c_str()); + hw.mapBootOrder[ulPos] = type; + } + } + else if (pelmHwChild->nameEquals("Display")) + { + Utf8Str strGraphicsControllerType; + if (!pelmHwChild->getAttributeValue("controller", strGraphicsControllerType)) + hw.graphicsAdapter.graphicsControllerType = GraphicsControllerType_VBoxVGA; + else + { + strGraphicsControllerType.toUpper(); + GraphicsControllerType_T type; + if (strGraphicsControllerType == "VBOXVGA") + type = GraphicsControllerType_VBoxVGA; + else if (strGraphicsControllerType == "VMSVGA") + type = GraphicsControllerType_VMSVGA; + else if (strGraphicsControllerType == "VBOXSVGA") + type = GraphicsControllerType_VBoxSVGA; + else if (strGraphicsControllerType == "NONE") + type = GraphicsControllerType_Null; + else + throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Display/@controller attribute"), strGraphicsControllerType.c_str()); + hw.graphicsAdapter.graphicsControllerType = type; + } + pelmHwChild->getAttributeValue("VRAMSize", hw.graphicsAdapter.ulVRAMSizeMB); + if (!pelmHwChild->getAttributeValue("monitorCount", hw.graphicsAdapter.cMonitors)) + pelmHwChild->getAttributeValue("MonitorCount", hw.graphicsAdapter.cMonitors); // pre-v1.5 variant + if (!pelmHwChild->getAttributeValue("accelerate3D", hw.graphicsAdapter.fAccelerate3D)) + pelmHwChild->getAttributeValue("Accelerate3D", hw.graphicsAdapter.fAccelerate3D); // pre-v1.5 variant + pelmHwChild->getAttributeValue("accelerate2DVideo", hw.graphicsAdapter.fAccelerate2DVideo); + } + else if (pelmHwChild->nameEquals("RemoteDisplay")) + { + pelmHwChild->getAttributeValue("enabled", hw.vrdeSettings.fEnabled); + + Utf8Str str; + if (pelmHwChild->getAttributeValue("port", str)) + hw.vrdeSettings.mapProperties["TCP/Ports"] = str; + if (pelmHwChild->getAttributeValue("netAddress", str)) + hw.vrdeSettings.mapProperties["TCP/Address"] = str; + + Utf8Str strAuthType; + if (pelmHwChild->getAttributeValue("authType", strAuthType)) + { + // settings before 1.3 used lower case so make sure this is case-insensitive + strAuthType.toUpper(); + if (strAuthType == "NULL") + hw.vrdeSettings.authType = AuthType_Null; + else if (strAuthType == "GUEST") + hw.vrdeSettings.authType = AuthType_Guest; + else if (strAuthType == "EXTERNAL") + hw.vrdeSettings.authType = AuthType_External; + else + throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in RemoteDisplay/@authType attribute"), strAuthType.c_str()); + } + + pelmHwChild->getAttributeValue("authLibrary", hw.vrdeSettings.strAuthLibrary); + pelmHwChild->getAttributeValue("authTimeout", hw.vrdeSettings.ulAuthTimeout); + pelmHwChild->getAttributeValue("allowMultiConnection", hw.vrdeSettings.fAllowMultiConnection); + pelmHwChild->getAttributeValue("reuseSingleConnection", hw.vrdeSettings.fReuseSingleConnection); + + /* 3.2 and 4.0 betas, 4.0 has this information in VRDEProperties. */ + const xml::ElementNode *pelmVideoChannel; + if ((pelmVideoChannel = pelmHwChild->findChildElement("VideoChannel"))) + { + bool fVideoChannel = false; + pelmVideoChannel->getAttributeValue("enabled", fVideoChannel); + hw.vrdeSettings.mapProperties["VideoChannel/Enabled"] = fVideoChannel? "true": "false"; + + uint32_t ulVideoChannelQuality = 75; + pelmVideoChannel->getAttributeValue("quality", ulVideoChannelQuality); + ulVideoChannelQuality = RT_CLAMP(ulVideoChannelQuality, 10, 100); + char *pszBuffer = NULL; + if (RTStrAPrintf(&pszBuffer, "%d", ulVideoChannelQuality) >= 0) + { + hw.vrdeSettings.mapProperties["VideoChannel/Quality"] = pszBuffer; + RTStrFree(pszBuffer); + } + else + hw.vrdeSettings.mapProperties["VideoChannel/Quality"] = "75"; + } + pelmHwChild->getAttributeValue("VRDEExtPack", hw.vrdeSettings.strVrdeExtPack); + + const xml::ElementNode *pelmProperties = pelmHwChild->findChildElement("VRDEProperties"); + if (pelmProperties != NULL) + { + xml::NodesLoop nl(*pelmProperties); + const xml::ElementNode *pelmProperty; + while ((pelmProperty = nl.forAllNodes())) + { + if (pelmProperty->nameEquals("Property")) + { + /* <Property name="TCP/Ports" value="3000-3002"/> */ + Utf8Str strName, strValue; + if ( pelmProperty->getAttributeValue("name", strName) + && pelmProperty->getAttributeValue("value", strValue)) + hw.vrdeSettings.mapProperties[strName] = strValue; + else + throw ConfigFileError(this, pelmProperty, N_("Required VRDE Property/@name or @value attribute is missing")); + } + } + } + } + else if (pelmHwChild->nameEquals("BIOS")) + { + const xml::ElementNode *pelmBIOSChild; + if ((pelmBIOSChild = pelmHwChild->findChildElement("ACPI"))) + pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fACPIEnabled); + if ((pelmBIOSChild = pelmHwChild->findChildElement("IOAPIC"))) + pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fIOAPICEnabled); + if ((pelmBIOSChild = pelmHwChild->findChildElement("APIC"))) + { + Utf8Str strAPIC; + if (pelmBIOSChild->getAttributeValue("mode", strAPIC)) + { + strAPIC.toUpper(); + if (strAPIC == "DISABLED") + hw.biosSettings.apicMode = APICMode_Disabled; + else if (strAPIC == "APIC") + hw.biosSettings.apicMode = APICMode_APIC; + else if (strAPIC == "X2APIC") + hw.biosSettings.apicMode = APICMode_X2APIC; + else + throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in APIC/@mode attribute"), strAPIC.c_str()); + } + } + if ((pelmBIOSChild = pelmHwChild->findChildElement("Logo"))) + { + pelmBIOSChild->getAttributeValue("fadeIn", hw.biosSettings.fLogoFadeIn); + pelmBIOSChild->getAttributeValue("fadeOut", hw.biosSettings.fLogoFadeOut); + pelmBIOSChild->getAttributeValue("displayTime", hw.biosSettings.ulLogoDisplayTime); + pelmBIOSChild->getAttributeValue("imagePath", hw.biosSettings.strLogoImagePath); + } + if ((pelmBIOSChild = pelmHwChild->findChildElement("BootMenu"))) + { + Utf8Str strBootMenuMode; + if (pelmBIOSChild->getAttributeValue("mode", strBootMenuMode)) + { + // settings before 1.3 used lower case so make sure this is case-insensitive + strBootMenuMode.toUpper(); + if (strBootMenuMode == "DISABLED") + hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_Disabled; + else if (strBootMenuMode == "MENUONLY") + hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MenuOnly; + else if (strBootMenuMode == "MESSAGEANDMENU") + hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MessageAndMenu; + else + throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in BootMenu/@mode attribute"), strBootMenuMode.c_str()); + } + } + if ((pelmBIOSChild = pelmHwChild->findChildElement("PXEDebug"))) + pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fPXEDebugEnabled); + if ((pelmBIOSChild = pelmHwChild->findChildElement("TimeOffset"))) + pelmBIOSChild->getAttributeValue("value", hw.biosSettings.llTimeOffset); + if ((pelmBIOSChild = pelmHwChild->findChildElement("NVRAM"))) + { + pelmBIOSChild->getAttributeValue("path", hw.nvramSettings.strNvramPath); + if (m->sv >= SettingsVersion_v1_19) + { + pelmBIOSChild->getAttributeValue("keyId", hw.nvramSettings.strKeyId); + pelmBIOSChild->getAttributeValue("keyStore", hw.nvramSettings.strKeyStore); + } + } + if ((pelmBIOSChild = pelmHwChild->findChildElement("SmbiosUuidLittleEndian"))) + pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fSmbiosUuidLittleEndian); + else + hw.biosSettings.fSmbiosUuidLittleEndian = false; /* Default for existing VMs. */ + + // legacy BIOS/IDEController (pre 1.7) + if ( (m->sv < SettingsVersion_v1_7) + && (pelmBIOSChild = pelmHwChild->findChildElement("IDEController")) + ) + { + StorageController sctl; + sctl.strName = "IDE Controller"; + sctl.storageBus = StorageBus_IDE; + + Utf8Str strType; + if (pelmBIOSChild->getAttributeValue("type", strType)) + { + if (strType == "PIIX3") + sctl.controllerType = StorageControllerType_PIIX3; + else if (strType == "PIIX4") + sctl.controllerType = StorageControllerType_PIIX4; + else if (strType == "ICH6") + sctl.controllerType = StorageControllerType_ICH6; + else + throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' for IDEController/@type attribute"), strType.c_str()); + } + sctl.ulPortCount = 2; + hw.storage.llStorageControllers.push_back(sctl); + } + } + else if (pelmHwChild->nameEquals("TrustedPlatformModule")) + { + Utf8Str strTpmType; + if (pelmHwChild->getAttributeValue("type", strTpmType)) + { + if (strTpmType == "None") + hw.tpmSettings.tpmType = TpmType_None; + else if (strTpmType == "v1_2") + hw.tpmSettings.tpmType = TpmType_v1_2; + else if (strTpmType == "v2_0") + hw.tpmSettings.tpmType = TpmType_v2_0; + else if (strTpmType == "Host") + hw.tpmSettings.tpmType = TpmType_Host; + else if (strTpmType == "Swtpm") + hw.tpmSettings.tpmType = TpmType_Swtpm; + else + throw ConfigFileError(this, + pelmHwChild, + N_("Invalid value '%s' in TrustedPlatformModule/@type"), + strTpmType.c_str()); + } + + pelmHwChild->getAttributeValue("location", hw.tpmSettings.strLocation); + } + else if ( (m->sv <= SettingsVersion_v1_14) + && pelmHwChild->nameEquals("USBController")) + { + bool fEnabled = false; + + pelmHwChild->getAttributeValue("enabled", fEnabled); + if (fEnabled) + { + /* Create OHCI controller with default name. */ + USBController ctrl; + + ctrl.strName = "OHCI"; + ctrl.enmType = USBControllerType_OHCI; + hw.usbSettings.llUSBControllers.push_back(ctrl); + } + + pelmHwChild->getAttributeValue("enabledEhci", fEnabled); + if (fEnabled) + { + /* Create OHCI controller with default name. */ + USBController ctrl; + + ctrl.strName = "EHCI"; + ctrl.enmType = USBControllerType_EHCI; + hw.usbSettings.llUSBControllers.push_back(ctrl); + } + + readUSBDeviceFilters(*pelmHwChild, + hw.usbSettings.llDeviceFilters); + } + else if (pelmHwChild->nameEquals("USB")) + { + const xml::ElementNode *pelmUSBChild; + + if ((pelmUSBChild = pelmHwChild->findChildElement("Controllers"))) + { + xml::NodesLoop nl2(*pelmUSBChild, "Controller"); + const xml::ElementNode *pelmCtrl; + + while ((pelmCtrl = nl2.forAllNodes())) + { + USBController ctrl; + com::Utf8Str strCtrlType; + + pelmCtrl->getAttributeValue("name", ctrl.strName); + + if (pelmCtrl->getAttributeValue("type", strCtrlType)) + { + if (strCtrlType == "OHCI") + ctrl.enmType = USBControllerType_OHCI; + else if (strCtrlType == "EHCI") + ctrl.enmType = USBControllerType_EHCI; + else if (strCtrlType == "XHCI") + ctrl.enmType = USBControllerType_XHCI; + else + throw ConfigFileError(this, pelmCtrl, N_("Invalid value '%s' for Controller/@type attribute"), strCtrlType.c_str()); + } + + hw.usbSettings.llUSBControllers.push_back(ctrl); + } + } + + if ((pelmUSBChild = pelmHwChild->findChildElement("DeviceFilters"))) + readUSBDeviceFilters(*pelmUSBChild, hw.usbSettings.llDeviceFilters); + } + else if ( m->sv < SettingsVersion_v1_7 + && pelmHwChild->nameEquals("SATAController")) + { + bool f; + if ( pelmHwChild->getAttributeValue("enabled", f) + && f) + { + StorageController sctl; + sctl.strName = "SATA Controller"; + sctl.storageBus = StorageBus_SATA; + sctl.controllerType = StorageControllerType_IntelAhci; + + readStorageControllerAttributes(*pelmHwChild, sctl); + + hw.storage.llStorageControllers.push_back(sctl); + } + } + else if (pelmHwChild->nameEquals("Network")) + readNetworkAdapters(*pelmHwChild, hw.llNetworkAdapters); + else if (pelmHwChild->nameEquals("RTC")) + { + Utf8Str strLocalOrUTC; + machineUserData.fRTCUseUTC = pelmHwChild->getAttributeValue("localOrUTC", strLocalOrUTC) + && strLocalOrUTC == "UTC"; + } + else if ( pelmHwChild->nameEquals("UART") + || pelmHwChild->nameEquals("Uart") // used before 1.3 + ) + readSerialPorts(*pelmHwChild, hw.llSerialPorts); + else if ( pelmHwChild->nameEquals("LPT") + || pelmHwChild->nameEquals("Lpt") // used before 1.3 + ) + readParallelPorts(*pelmHwChild, hw.llParallelPorts); + else if (pelmHwChild->nameEquals("AudioAdapter")) + readAudioAdapter(*pelmHwChild, hw.audioAdapter); + else if (pelmHwChild->nameEquals("SharedFolders")) + { + xml::NodesLoop nl2(*pelmHwChild, "SharedFolder"); + const xml::ElementNode *pelmFolder; + while ((pelmFolder = nl2.forAllNodes())) + { + SharedFolder sf; + pelmFolder->getAttributeValue("name", sf.strName); + pelmFolder->getAttributeValue("hostPath", sf.strHostPath); + pelmFolder->getAttributeValue("writable", sf.fWritable); + pelmFolder->getAttributeValue("autoMount", sf.fAutoMount); + pelmFolder->getAttributeValue("autoMountPoint", sf.strAutoMountPoint); + hw.llSharedFolders.push_back(sf); + } + } + else if (pelmHwChild->nameEquals("Clipboard")) + { + Utf8Str strTemp; + if (pelmHwChild->getAttributeValue("mode", strTemp)) + { + if (strTemp == "Disabled") + hw.clipboardMode = ClipboardMode_Disabled; + else if (strTemp == "HostToGuest") + hw.clipboardMode = ClipboardMode_HostToGuest; + else if (strTemp == "GuestToHost") + hw.clipboardMode = ClipboardMode_GuestToHost; + else if (strTemp == "Bidirectional") + hw.clipboardMode = ClipboardMode_Bidirectional; + else + throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Clipboard/@mode attribute"), strTemp.c_str()); + } + + pelmHwChild->getAttributeValue("fileTransfersEnabled", hw.fClipboardFileTransfersEnabled); + } + else if (pelmHwChild->nameEquals("DragAndDrop")) + { + Utf8Str strTemp; + if (pelmHwChild->getAttributeValue("mode", strTemp)) + { + if (strTemp == "Disabled") + hw.dndMode = DnDMode_Disabled; + else if (strTemp == "HostToGuest") + hw.dndMode = DnDMode_HostToGuest; + else if (strTemp == "GuestToHost") + hw.dndMode = DnDMode_GuestToHost; + else if (strTemp == "Bidirectional") + hw.dndMode = DnDMode_Bidirectional; + else + throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in DragAndDrop/@mode attribute"), strTemp.c_str()); + } + } + else if (pelmHwChild->nameEquals("Guest")) + { + if (!pelmHwChild->getAttributeValue("memoryBalloonSize", hw.ulMemoryBalloonSize)) + pelmHwChild->getAttributeValue("MemoryBalloonSize", hw.ulMemoryBalloonSize); // used before 1.3 + } + else if (pelmHwChild->nameEquals("GuestProperties")) + readGuestProperties(*pelmHwChild, hw); + else if (pelmHwChild->nameEquals("IO")) + { + const xml::ElementNode *pelmBwGroups; + const xml::ElementNode *pelmIOChild; + + if ((pelmIOChild = pelmHwChild->findChildElement("IoCache"))) + { + pelmIOChild->getAttributeValue("enabled", hw.ioSettings.fIOCacheEnabled); + pelmIOChild->getAttributeValue("size", hw.ioSettings.ulIOCacheSize); + } + + if ((pelmBwGroups = pelmHwChild->findChildElement("BandwidthGroups"))) + { + xml::NodesLoop nl2(*pelmBwGroups, "BandwidthGroup"); + const xml::ElementNode *pelmBandwidthGroup; + while ((pelmBandwidthGroup = nl2.forAllNodes())) + { + BandwidthGroup gr; + Utf8Str strTemp; + + pelmBandwidthGroup->getAttributeValue("name", gr.strName); + + if (pelmBandwidthGroup->getAttributeValue("type", strTemp)) + { + if (strTemp == "Disk") + gr.enmType = BandwidthGroupType_Disk; + else if (strTemp == "Network") + gr.enmType = BandwidthGroupType_Network; + else + throw ConfigFileError(this, pelmBandwidthGroup, N_("Invalid value '%s' in BandwidthGroup/@type attribute"), strTemp.c_str()); + } + else + throw ConfigFileError(this, pelmBandwidthGroup, N_("Missing BandwidthGroup/@type attribute")); + + if (!pelmBandwidthGroup->getAttributeValue("maxBytesPerSec", gr.cMaxBytesPerSec)) + { + pelmBandwidthGroup->getAttributeValue("maxMbPerSec", gr.cMaxBytesPerSec); + gr.cMaxBytesPerSec *= _1M; + } + hw.ioSettings.llBandwidthGroups.push_back(gr); + } + } + } + else if (pelmHwChild->nameEquals("HostPci")) + { + const xml::ElementNode *pelmDevices; + + if ((pelmDevices = pelmHwChild->findChildElement("Devices"))) + { + xml::NodesLoop nl2(*pelmDevices, "Device"); + const xml::ElementNode *pelmDevice; + while ((pelmDevice = nl2.forAllNodes())) + { + HostPCIDeviceAttachment hpda; + + if (!pelmDevice->getAttributeValue("host", hpda.uHostAddress)) + throw ConfigFileError(this, pelmDevice, N_("Missing Device/@host attribute")); + + if (!pelmDevice->getAttributeValue("guest", hpda.uGuestAddress)) + throw ConfigFileError(this, pelmDevice, N_("Missing Device/@guest attribute")); + + /* name is optional */ + pelmDevice->getAttributeValue("name", hpda.strDeviceName); + + hw.pciAttachments.push_back(hpda); + } + } + } + else if (pelmHwChild->nameEquals("EmulatedUSB")) + { + const xml::ElementNode *pelmCardReader; + + if ((pelmCardReader = pelmHwChild->findChildElement("CardReader"))) + { + pelmCardReader->getAttributeValue("enabled", hw.fEmulatedUSBCardReader); + } + } + else if (pelmHwChild->nameEquals("Frontend")) + { + const xml::ElementNode *pelmDefault; + + if ((pelmDefault = pelmHwChild->findChildElement("Default"))) + { + pelmDefault->getAttributeValue("type", hw.strDefaultFrontend); + } + } + else if (pelmHwChild->nameEquals("StorageControllers")) + readStorageControllers(*pelmHwChild, hw.storage); + } + + if (hw.ulMemorySizeMB == (uint32_t)-1) + throw ConfigFileError(this, &elmHardware, N_("Required Memory/@RAMSize element/attribute is missing")); +} + +/** + * This gets called instead of readStorageControllers() for legacy pre-1.7 settings + * files which have a \<HardDiskAttachments\> node and storage controller settings + * hidden in the \<Hardware\> settings. We set the StorageControllers fields just the + * same, just from different sources. + * @param elmHardDiskAttachments \<HardDiskAttachments\> XML node. + * @param strg + */ +void MachineConfigFile::readHardDiskAttachments_pre1_7(const xml::ElementNode &elmHardDiskAttachments, + Storage &strg) +{ + StorageController *pIDEController = NULL; + StorageController *pSATAController = NULL; + + for (StorageControllersList::iterator it = strg.llStorageControllers.begin(); + it != strg.llStorageControllers.end(); + ++it) + { + StorageController &s = *it; + if (s.storageBus == StorageBus_IDE) + pIDEController = &s; + else if (s.storageBus == StorageBus_SATA) + pSATAController = &s; + } + + xml::NodesLoop nl1(elmHardDiskAttachments, "HardDiskAttachment"); + const xml::ElementNode *pelmAttachment; + while ((pelmAttachment = nl1.forAllNodes())) + { + AttachedDevice att; + Utf8Str strUUID, strBus; + + if (!pelmAttachment->getAttributeValue("hardDisk", strUUID)) + throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@hardDisk attribute is missing")); + parseUUID(att.uuid, strUUID, pelmAttachment); + + if (!pelmAttachment->getAttributeValue("bus", strBus)) + throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@bus attribute is missing")); + // pre-1.7 'channel' is now port + if (!pelmAttachment->getAttributeValue("channel", att.lPort)) + throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@channel attribute is missing")); + // pre-1.7 'device' is still device + if (!pelmAttachment->getAttributeValue("device", att.lDevice)) + throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@device attribute is missing")); + + att.deviceType = DeviceType_HardDisk; + + if (strBus == "IDE") + { + if (!pIDEController) + throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'IDE' but cannot find IDE controller")); + pIDEController->llAttachedDevices.push_back(att); + } + else if (strBus == "SATA") + { + if (!pSATAController) + throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'SATA' but cannot find SATA controller")); + pSATAController->llAttachedDevices.push_back(att); + } + else + throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus attribute has illegal value '%s'"), strBus.c_str()); + } +} + +/** + * Reads in a \<StorageControllers\> block and stores it in the given Storage structure. + * Used both directly from readMachine and from readSnapshot, since snapshots + * have their own storage controllers sections. + * + * This is only called for settings version 1.7 and above; see readHardDiskAttachments_pre1_7() + * for earlier versions. + * + * @param elmStorageControllers + * @param strg + */ +void MachineConfigFile::readStorageControllers(const xml::ElementNode &elmStorageControllers, + Storage &strg) +{ + xml::NodesLoop nlStorageControllers(elmStorageControllers, "StorageController"); + const xml::ElementNode *pelmController; + while ((pelmController = nlStorageControllers.forAllNodes())) + { + StorageController sctl; + + if (!pelmController->getAttributeValue("name", sctl.strName)) + throw ConfigFileError(this, pelmController, N_("Required StorageController/@name attribute is missing")); + // canonicalize storage controller names for configs in the switchover + // period. + if (m->sv < SettingsVersion_v1_9) + { + if (sctl.strName == "IDE") + sctl.strName = "IDE Controller"; + else if (sctl.strName == "SATA") + sctl.strName = "SATA Controller"; + else if (sctl.strName == "SCSI") + sctl.strName = "SCSI Controller"; + } + + pelmController->getAttributeValue("Instance", sctl.ulInstance); + // default from constructor is 0 + + pelmController->getAttributeValue("Bootable", sctl.fBootable); + // default from constructor is true which is true + // for settings below version 1.11 because they allowed only + // one controller per type. + + Utf8Str strType; + if (!pelmController->getAttributeValue("type", strType)) + throw ConfigFileError(this, pelmController, N_("Required StorageController/@type attribute is missing")); + + if (strType == "AHCI") + { + sctl.storageBus = StorageBus_SATA; + sctl.controllerType = StorageControllerType_IntelAhci; + } + else if (strType == "LsiLogic") + { + sctl.storageBus = StorageBus_SCSI; + sctl.controllerType = StorageControllerType_LsiLogic; + } + else if (strType == "BusLogic") + { + sctl.storageBus = StorageBus_SCSI; + sctl.controllerType = StorageControllerType_BusLogic; + } + else if (strType == "PIIX3") + { + sctl.storageBus = StorageBus_IDE; + sctl.controllerType = StorageControllerType_PIIX3; + } + else if (strType == "PIIX4") + { + sctl.storageBus = StorageBus_IDE; + sctl.controllerType = StorageControllerType_PIIX4; + } + else if (strType == "ICH6") + { + sctl.storageBus = StorageBus_IDE; + sctl.controllerType = StorageControllerType_ICH6; + } + else if ( (m->sv >= SettingsVersion_v1_9) + && (strType == "I82078") + ) + { + sctl.storageBus = StorageBus_Floppy; + sctl.controllerType = StorageControllerType_I82078; + } + else if (strType == "LsiLogicSas") + { + sctl.storageBus = StorageBus_SAS; + sctl.controllerType = StorageControllerType_LsiLogicSas; + } + else if (strType == "USB") + { + sctl.storageBus = StorageBus_USB; + sctl.controllerType = StorageControllerType_USB; + } + else if (strType == "NVMe") + { + sctl.storageBus = StorageBus_PCIe; + sctl.controllerType = StorageControllerType_NVMe; + } + else if (strType == "VirtioSCSI") + { + sctl.storageBus = StorageBus_VirtioSCSI; + sctl.controllerType = StorageControllerType_VirtioSCSI; + } + else + throw ConfigFileError(this, pelmController, N_("Invalid value '%s' for StorageController/@type attribute"), strType.c_str()); + + readStorageControllerAttributes(*pelmController, sctl); + + xml::NodesLoop nlAttached(*pelmController, "AttachedDevice"); + const xml::ElementNode *pelmAttached; + while ((pelmAttached = nlAttached.forAllNodes())) + { + AttachedDevice att; + Utf8Str strTemp; + pelmAttached->getAttributeValue("type", strTemp); + + att.fDiscard = false; + att.fNonRotational = false; + att.fHotPluggable = false; + att.fPassThrough = false; + + if (strTemp == "HardDisk") + { + att.deviceType = DeviceType_HardDisk; + pelmAttached->getAttributeValue("nonrotational", att.fNonRotational); + pelmAttached->getAttributeValue("discard", att.fDiscard); + } + else if (m->sv >= SettingsVersion_v1_9) + { + // starting with 1.9 we list DVD and floppy drive info + attachments under <StorageControllers> + if (strTemp == "DVD") + { + att.deviceType = DeviceType_DVD; + pelmAttached->getAttributeValue("passthrough", att.fPassThrough); + pelmAttached->getAttributeValue("tempeject", att.fTempEject); + } + else if (strTemp == "Floppy") + att.deviceType = DeviceType_Floppy; + } + + if (att.deviceType != DeviceType_Null) + { + const xml::ElementNode *pelmImage; + // all types can have images attached, but for HardDisk it's required + if (!(pelmImage = pelmAttached->findChildElement("Image"))) + { + if (att.deviceType == DeviceType_HardDisk) + throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image element is missing")); + else + { + // DVDs and floppies can also have <HostDrive> instead of <Image> + const xml::ElementNode *pelmHostDrive; + if ((pelmHostDrive = pelmAttached->findChildElement("HostDrive"))) + if (!pelmHostDrive->getAttributeValue("src", att.strHostDriveSrc)) + throw ConfigFileError(this, pelmHostDrive, N_("Required AttachedDevice/HostDrive/@src attribute is missing")); + } + } + else + { + if (!pelmImage->getAttributeValue("uuid", strTemp)) + throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image/@uuid attribute is missing")); + parseUUID(att.uuid, strTemp, pelmImage); + } + + if (!pelmAttached->getAttributeValue("port", att.lPort)) + throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@port attribute is missing")); + if (!pelmAttached->getAttributeValue("device", att.lDevice)) + throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@device attribute is missing")); + + /* AHCI controller ports are hotpluggable by default, keep compatibility with existing settings. */ + if (m->sv >= SettingsVersion_v1_15) + pelmAttached->getAttributeValue("hotpluggable", att.fHotPluggable); + else if (sctl.controllerType == StorageControllerType_IntelAhci) + att.fHotPluggable = true; + + pelmAttached->getAttributeValue("bandwidthGroup", att.strBwGroup); + sctl.llAttachedDevices.push_back(att); + } + } + + strg.llStorageControllers.push_back(sctl); + } +} + +/** + * This gets called for legacy pre-1.9 settings files after having parsed the + * \<Hardware\> and \<StorageControllers\> sections to parse \<Hardware\> once more + * for the \<DVDDrive\> and \<FloppyDrive\> sections. + * + * Before settings version 1.9, DVD and floppy drives were specified separately + * under \<Hardware\>; we then need this extra loop to make sure the storage + * controller structs are already set up so we can add stuff to them. + * + * @param elmHardware + * @param strg + */ +void MachineConfigFile::readDVDAndFloppies_pre1_9(const xml::ElementNode &elmHardware, + Storage &strg) +{ + xml::NodesLoop nl1(elmHardware); + const xml::ElementNode *pelmHwChild; + while ((pelmHwChild = nl1.forAllNodes())) + { + if (pelmHwChild->nameEquals("DVDDrive")) + { + // create a DVD "attached device" and attach it to the existing IDE controller + AttachedDevice att; + att.deviceType = DeviceType_DVD; + // legacy DVD drive is always secondary master (port 1, device 0) + att.lPort = 1; + att.lDevice = 0; + pelmHwChild->getAttributeValue("passthrough", att.fPassThrough); + pelmHwChild->getAttributeValue("tempeject", att.fTempEject); + + const xml::ElementNode *pDriveChild; + Utf8Str strTmp; + if ( (pDriveChild = pelmHwChild->findChildElement("Image")) != NULL + && pDriveChild->getAttributeValue("uuid", strTmp)) + parseUUID(att.uuid, strTmp, pDriveChild); + else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive"))) + pDriveChild->getAttributeValue("src", att.strHostDriveSrc); + + // find the IDE controller and attach the DVD drive + bool fFound = false; + for (StorageControllersList::iterator it = strg.llStorageControllers.begin(); + it != strg.llStorageControllers.end(); + ++it) + { + StorageController &sctl = *it; + if (sctl.storageBus == StorageBus_IDE) + { + sctl.llAttachedDevices.push_back(att); + fFound = true; + break; + } + } + + if (!fFound) + throw ConfigFileError(this, pelmHwChild, N_("Internal error: found DVD drive but IDE controller does not exist")); + // shouldn't happen because pre-1.9 settings files always had at least one IDE controller in the settings + // which should have gotten parsed in <StorageControllers> before this got called + } + else if (pelmHwChild->nameEquals("FloppyDrive")) + { + bool fEnabled; + if ( pelmHwChild->getAttributeValue("enabled", fEnabled) + && fEnabled) + { + // create a new floppy controller and attach a floppy "attached device" + StorageController sctl; + sctl.strName = "Floppy Controller"; + sctl.storageBus = StorageBus_Floppy; + sctl.controllerType = StorageControllerType_I82078; + sctl.ulPortCount = 1; + + AttachedDevice att; + att.deviceType = DeviceType_Floppy; + att.lPort = 0; + att.lDevice = 0; + + const xml::ElementNode *pDriveChild; + Utf8Str strTmp; + if ( (pDriveChild = pelmHwChild->findChildElement("Image")) + && pDriveChild->getAttributeValue("uuid", strTmp) ) + parseUUID(att.uuid, strTmp, pDriveChild); + else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive"))) + pDriveChild->getAttributeValue("src", att.strHostDriveSrc); + + // store attachment with controller + sctl.llAttachedDevices.push_back(att); + // store controller with storage + strg.llStorageControllers.push_back(sctl); + } + } + } +} + +/** + * Called for reading the \<Teleporter\> element under \<Machine\>. + */ +void MachineConfigFile::readTeleporter(const xml::ElementNode &elmTeleporter, + MachineUserData &userData) +{ + elmTeleporter.getAttributeValue("enabled", userData.fTeleporterEnabled); + elmTeleporter.getAttributeValue("port", userData.uTeleporterPort); + elmTeleporter.getAttributeValue("address", userData.strTeleporterAddress); + elmTeleporter.getAttributeValue("password", userData.strTeleporterPassword); + + if ( userData.strTeleporterPassword.isNotEmpty() + && !VBoxIsPasswordHashed(&userData.strTeleporterPassword)) + VBoxHashPassword(&userData.strTeleporterPassword); +} + +/** + * Called for reading the \<Debugging\> element under \<Machine\> or \<Snapshot\>. + */ +void MachineConfigFile::readDebugging(const xml::ElementNode &elmDebugging, Debugging &dbg) +{ + if (m->sv < SettingsVersion_v1_13) + return; + + const xml::ElementNode *pelmTracing = elmDebugging.findChildElement("Tracing"); + if (pelmTracing) + { + pelmTracing->getAttributeValue("enabled", dbg.fTracingEnabled); + pelmTracing->getAttributeValue("allowTracingToAccessVM", dbg.fAllowTracingToAccessVM); + pelmTracing->getAttributeValue("config", dbg.strTracingConfig); + } + + const xml::ElementNode *pelmGuestDebug = elmDebugging.findChildElement("GuestDebug"); + if (pelmGuestDebug) + { + Utf8Str strTmp; + pelmGuestDebug->getAttributeValue("provider", strTmp); + if (strTmp == "None") + dbg.enmDbgProvider = GuestDebugProvider_None; + else if (strTmp == "GDB") + dbg.enmDbgProvider = GuestDebugProvider_GDB; + else if (strTmp == "KD") + dbg.enmDbgProvider = GuestDebugProvider_KD; + else + throw ConfigFileError(this, pelmGuestDebug, N_("Invalid value '%s' for GuestDebug/@provider attribute"), strTmp.c_str()); + + pelmGuestDebug->getAttributeValue("io", strTmp); + if (strTmp == "None") + dbg.enmIoProvider = GuestDebugIoProvider_None; + else if (strTmp == "TCP") + dbg.enmIoProvider = GuestDebugIoProvider_TCP; + else if (strTmp == "UDP") + dbg.enmIoProvider = GuestDebugIoProvider_UDP; + else if (strTmp == "IPC") + dbg.enmIoProvider = GuestDebugIoProvider_IPC; + else + throw ConfigFileError(this, pelmGuestDebug, N_("Invalid value '%s' for GuestDebug/@io attribute"), strTmp.c_str()); + + pelmGuestDebug->getAttributeValue("address", dbg.strAddress); + pelmGuestDebug->getAttributeValue("port", dbg.ulPort); + } +} + +/** + * Called for reading the \<Autostart\> element under \<Machine\> or \<Snapshot\>. + */ +void MachineConfigFile::readAutostart(const xml::ElementNode &elmAutostart, Autostart &autostrt) +{ + Utf8Str strAutostop; + + if (m->sv < SettingsVersion_v1_13) + return; + + elmAutostart.getAttributeValue("enabled", autostrt.fAutostartEnabled); + elmAutostart.getAttributeValue("delay", autostrt.uAutostartDelay); + elmAutostart.getAttributeValue("autostop", strAutostop); + if (strAutostop == "Disabled") + autostrt.enmAutostopType = AutostopType_Disabled; + else if (strAutostop == "SaveState") + autostrt.enmAutostopType = AutostopType_SaveState; + else if (strAutostop == "PowerOff") + autostrt.enmAutostopType = AutostopType_PowerOff; + else if (strAutostop == "AcpiShutdown") + autostrt.enmAutostopType = AutostopType_AcpiShutdown; + else + throw ConfigFileError(this, &elmAutostart, N_("Invalid value '%s' for Autostart/@autostop attribute"), strAutostop.c_str()); +} + +/** + * Called for reading the \<VideoCapture\> element under \<Machine|Hardware\>, + * or \<Recording\> under \<Machine\>, + */ +void MachineConfigFile::readRecordingSettings(const xml::ElementNode &elmRecording, uint32_t cMonitors, RecordingSettings &recording) +{ + if (cMonitors > 64) + throw ConfigFileError(this, &elmRecording, N_("Invalid monitor count given")); + + elmRecording.getAttributeValue("enabled", recording.common.fEnabled); + + /* Note: Since settings 1.19 the recording settings have a dedicated XML branch "Recording" outside of "Hardware". */ + if (m->sv >= SettingsVersion_v1_19 /* VBox >= 7.0 */) + { + uint32_t cScreens = 0; + elmRecording.getAttributeValue("screens", cScreens); + + xml::ElementNodesList plstScreens; + elmRecording.getChildElements(plstScreens, "Screen"); + + /* Sanity checks. */ + if (cScreens != plstScreens.size()) + throw ConfigFileError(this, &elmRecording, N_("Recording/@screens attribute does not match stored screen objects")); + if (cScreens > 64) + throw ConfigFileError(this, &elmRecording, N_("Recording/@screens attribute is invalid")); + + for (xml::ElementNodesList::iterator itScreen = plstScreens.begin(); + itScreen != plstScreens.end(); + ++itScreen) + { + /* The screen's stored ID is the monitor ID and also the key for the map. */ + uint32_t idxScreen; + (*itScreen)->getAttributeValue("id", idxScreen); + + RecordingScreenSettings &screenSettings = recording.mapScreens[idxScreen]; + + (*itScreen)->getAttributeValue("enabled", screenSettings.fEnabled); + Utf8Str strTemp; + (*itScreen)->getAttributeValue("featuresEnabled", strTemp); + RecordingScreenSettings::featuresFromString(strTemp, screenSettings.featureMap); + (*itScreen)->getAttributeValue("maxTimeS", screenSettings.ulMaxTimeS); + (*itScreen)->getAttributeValue("options", screenSettings.strOptions); + (*itScreen)->getAttributeValue("dest", (uint32_t &)screenSettings.enmDest); + if (screenSettings.enmDest == RecordingDestination_File) + (*itScreen)->getAttributeValuePath("file", screenSettings.File.strName); + else + throw ConfigFileError(this, (*itScreen), + N_("Not supported Recording/@dest attribute '%#x'"), screenSettings.enmDest); + (*itScreen)->getAttributeValue("maxSizeMB", screenSettings.File.ulMaxSizeMB); + if ((*itScreen)->getAttributeValue("videoCodec", strTemp)) /* Stick with default if not set. */ + RecordingScreenSettings::videoCodecFromString(strTemp, screenSettings.Video.enmCodec); + (*itScreen)->getAttributeValue("videoDeadline", (uint32_t &)screenSettings.Video.enmDeadline); + (*itScreen)->getAttributeValue("videoRateCtlMode", (uint32_t &)screenSettings.Video.enmRateCtlMode); + (*itScreen)->getAttributeValue("videoScalingMode", (uint32_t &)screenSettings.Video.enmScalingMode); + (*itScreen)->getAttributeValue("horzRes", screenSettings.Video.ulWidth); + (*itScreen)->getAttributeValue("vertRes", screenSettings.Video.ulHeight); + (*itScreen)->getAttributeValue("rateKbps", screenSettings.Video.ulRate); + (*itScreen)->getAttributeValue("fps", screenSettings.Video.ulFPS); + + if ((*itScreen)->getAttributeValue("audioCodec", strTemp)) /* Stick with default if not set. */ + RecordingScreenSettings::audioCodecFromString(strTemp, screenSettings.Audio.enmCodec); + (*itScreen)->getAttributeValue("audioDeadline", (uint32_t &)screenSettings.Audio.enmDeadline); + (*itScreen)->getAttributeValue("audioRateCtlMode", (uint32_t &)screenSettings.Audio.enmRateCtlMode); + (*itScreen)->getAttributeValue("audioHz", (uint32_t &)screenSettings.Audio.uHz); + (*itScreen)->getAttributeValue("audioBits", (uint32_t &)screenSettings.Audio.cBits); + (*itScreen)->getAttributeValue("audioChannels", (uint32_t &)screenSettings.Audio.cChannels); + } + } + else if ( m->sv >= SettingsVersion_v1_14 + && m->sv < SettingsVersion_v1_19 /* VBox < 7.0 */) + { + /* For settings < 1.19 (< VBox 7.0) we only support one recording configuration, that is, + * all screens have the same configuration. So load/save to/from screen 0. */ + RecordingScreenSettings &screen0 = recording.mapScreens[0]; + + elmRecording.getAttributeValue("maxTime", screen0.ulMaxTimeS); + elmRecording.getAttributeValue("options", screen0.strOptions); + elmRecording.getAttributeValuePath("file", screen0.File.strName); + elmRecording.getAttributeValue("maxSize", screen0.File.ulMaxSizeMB); + elmRecording.getAttributeValue("horzRes", screen0.Video.ulWidth); + elmRecording.getAttributeValue("vertRes", screen0.Video.ulHeight); + elmRecording.getAttributeValue("rate", screen0.Video.ulRate); + elmRecording.getAttributeValue("fps", screen0.Video.ulFPS); + + /* Convert the enabled screens to the former uint64_t bit array and vice versa. */ + uint64_t uScreensBitmap = 0; + elmRecording.getAttributeValue("screens", uScreensBitmap); + + /* Note: For settings < 1.19 the "screens" attribute is a bit field for all screens + * which are ENABLED for recording. The settings for recording are for all the same though. */ + for (unsigned i = 0; i < cMonitors; i++) + { + /* Apply settings of screen 0 to screen i and enable it. */ + recording.mapScreens[i] = screen0; + + /* Screen i enabled? */ + recording.mapScreens[i].idScreen = i; + recording.mapScreens[i].fEnabled = RT_BOOL(uScreensBitmap & RT_BIT_64(i)); + } + } +} + +/** + * Called for reading the \<Groups\> element under \<Machine\>. + */ +void MachineConfigFile::readGroups(const xml::ElementNode &elmGroups, StringsList &llGroups) +{ + llGroups.clear(); + if (m->sv < SettingsVersion_v1_13) + { + llGroups.push_back("/"); + return; + } + + xml::NodesLoop nlGroups(elmGroups); + const xml::ElementNode *pelmGroup; + while ((pelmGroup = nlGroups.forAllNodes())) + { + if (pelmGroup->nameEquals("Group")) + { + Utf8Str strGroup; + if (!pelmGroup->getAttributeValue("name", strGroup)) + throw ConfigFileError(this, pelmGroup, N_("Required Group/@name attribute is missing")); + llGroups.push_back(strGroup); + } + } +} + +/** + * Called initially for the \<Snapshot\> element under \<Machine\>, if present, + * to store the snapshot's data into the given Snapshot structure (which is + * then the one in the Machine struct). This might process further elements + * of the snapshot tree if a \<Snapshots\> (plural) element is found in the + * snapshot, which should contain a list of child snapshots; such lists are + * maintained in the Snapshot structure. + * + * @param curSnapshotUuid + * @param elmSnapshot + * @param snap + * @returns true if curSnapshotUuid is in this snapshot subtree, otherwise false + */ +bool MachineConfigFile::readSnapshot(const Guid &curSnapshotUuid, + const xml::ElementNode &elmSnapshot, + Snapshot &snap) +{ + std::list<const xml::ElementNode *> llElementsTodo; + llElementsTodo.push_back(&elmSnapshot); + std::list<Snapshot *> llSettingsTodo; + llSettingsTodo.push_back(&snap); + std::list<uint32_t> llDepthsTodo; + llDepthsTodo.push_back(1); + + bool foundCurrentSnapshot = false; + + while (llElementsTodo.size() > 0) + { + const xml::ElementNode *pElement = llElementsTodo.front(); + llElementsTodo.pop_front(); + Snapshot *pSnap = llSettingsTodo.front(); + llSettingsTodo.pop_front(); + uint32_t depth = llDepthsTodo.front(); + llDepthsTodo.pop_front(); + + if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX) + throw ConfigFileError(this, pElement, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX); + + Utf8Str strTemp; + if (!pElement->getAttributeValue("uuid", strTemp)) + throw ConfigFileError(this, pElement, N_("Required Snapshot/@uuid attribute is missing")); + parseUUID(pSnap->uuid, strTemp, pElement); + foundCurrentSnapshot |= (pSnap->uuid == curSnapshotUuid); + + if (!pElement->getAttributeValue("name", pSnap->strName)) + throw ConfigFileError(this, pElement, N_("Required Snapshot/@name attribute is missing")); + + // 3.1 dev builds added Description as an attribute, read it silently + // and write it back as an element + pElement->getAttributeValue("Description", pSnap->strDescription); + + if (!pElement->getAttributeValue("timeStamp", strTemp)) + throw ConfigFileError(this, pElement, N_("Required Snapshot/@timeStamp attribute is missing")); + parseTimestamp(pSnap->timestamp, strTemp, pElement); + + pElement->getAttributeValuePath("stateFile", pSnap->strStateFile); // online snapshots only + + // parse Hardware before the other elements because other things depend on it + const xml::ElementNode *pelmHardware; + if (!(pelmHardware = pElement->findChildElement("Hardware"))) + throw ConfigFileError(this, pElement, N_("Required Snapshot/@Hardware element is missing")); + readHardware(*pelmHardware, pSnap->hardware); + + const xml::ElementNode *pelmSnapshots = NULL; + + xml::NodesLoop nlSnapshotChildren(*pElement); + const xml::ElementNode *pelmSnapshotChild; + while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes())) + { + if (pelmSnapshotChild->nameEquals("Description")) + pSnap->strDescription = pelmSnapshotChild->getValue(); + else if ( m->sv < SettingsVersion_v1_7 + && pelmSnapshotChild->nameEquals("HardDiskAttachments")) + readHardDiskAttachments_pre1_7(*pelmSnapshotChild, pSnap->hardware.storage); + else if ( m->sv >= SettingsVersion_v1_7 + && pelmSnapshotChild->nameEquals("StorageControllers")) + readStorageControllers(*pelmSnapshotChild, pSnap->hardware.storage); + else if (pelmSnapshotChild->nameEquals("Snapshots")) + { + if (pelmSnapshots) + throw ConfigFileError(this, pelmSnapshotChild, N_("Just a single Snapshots element is allowed")); + pelmSnapshots = pelmSnapshotChild; + } + } + + if (m->sv < SettingsVersion_v1_9) + // go through Hardware once more to repair the settings controller structures + // with data from old DVDDrive and FloppyDrive elements + readDVDAndFloppies_pre1_9(*pelmHardware, pSnap->hardware.storage); + + const xml::ElementNode *pelmDebugging = elmSnapshot.findChildElement("Debugging"); /** @todo r=andy Shouldn't this be pElement instead of elmSnapshot? Re-visit this! */ + if (pelmDebugging) + readDebugging(*pelmDebugging, pSnap->debugging); + const xml::ElementNode *pelmAutostart = elmSnapshot.findChildElement("Autostart"); /** @todo r=andy Ditto. */ + if (pelmAutostart) + readAutostart(*pelmAutostart, pSnap->autostart); + if (m->sv < SettingsVersion_v1_19) + { + const xml::ElementNode *pelmVideoCapture = pElement->findChildElement("VideoCapture"); + if (pelmVideoCapture) + readRecordingSettings(*pelmVideoCapture, pSnap->hardware.graphicsAdapter.cMonitors, pSnap->recordingSettings); + } + else /* >= VBox 7.0 */ + { + const xml::ElementNode *pelmRecording = pElement->findChildElement("Recording"); + if (pelmRecording) + readRecordingSettings(*pelmRecording, pSnap->hardware.graphicsAdapter.cMonitors, pSnap->recordingSettings); + } + // note: Groups exist only for Machine, not for Snapshot + + // process all child snapshots + if (pelmSnapshots) + { + xml::NodesLoop nlChildSnapshots(*pelmSnapshots); + const xml::ElementNode *pelmChildSnapshot; + while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes())) + { + if (pelmChildSnapshot->nameEquals("Snapshot")) + { + llElementsTodo.push_back(pelmChildSnapshot); + pSnap->llChildSnapshots.push_back(Snapshot::Empty); + llSettingsTodo.push_back(&pSnap->llChildSnapshots.back()); + llDepthsTodo.push_back(depth + 1); + } + } + } + } + + return foundCurrentSnapshot; +} + +const struct { + const char *pcszOld; + const char *pcszNew; +} aConvertOSTypes[] = +{ + { "unknown", "Other" }, + { "dos", "DOS" }, + { "win31", "Windows31" }, + { "win95", "Windows95" }, + { "win98", "Windows98" }, + { "winme", "WindowsMe" }, + { "winnt4", "WindowsNT4" }, + { "win2k", "Windows2000" }, + { "winxp", "WindowsXP" }, + { "win2k3", "Windows2003" }, + { "winvista", "WindowsVista" }, + { "win2k8", "Windows2008" }, + { "os2warp3", "OS2Warp3" }, + { "os2warp4", "OS2Warp4" }, + { "os2warp45", "OS2Warp45" }, + { "ecs", "OS2eCS" }, + { "linux22", "Linux22" }, + { "linux24", "Linux24" }, + { "linux26", "Linux26" }, + { "archlinux", "ArchLinux" }, + { "debian", "Debian" }, + { "opensuse", "OpenSUSE" }, + { "fedoracore", "Fedora" }, + { "gentoo", "Gentoo" }, + { "mandriva", "Mandriva" }, + { "redhat", "RedHat" }, + { "ubuntu", "Ubuntu" }, + { "xandros", "Xandros" }, + { "freebsd", "FreeBSD" }, + { "openbsd", "OpenBSD" }, + { "netbsd", "NetBSD" }, + { "netware", "Netware" }, + { "solaris", "Solaris" }, + { "opensolaris", "OpenSolaris" }, + { "l4", "L4" } +}; + +void MachineConfigFile::convertOldOSType_pre1_5(Utf8Str &str) +{ + for (unsigned u = 0; + u < RT_ELEMENTS(aConvertOSTypes); + ++u) + { + if (str == aConvertOSTypes[u].pcszOld) + { + str = aConvertOSTypes[u].pcszNew; + break; + } + } +} + +/** + * Called from the constructor to actually read in the \<Machine\> element + * of a machine config file. + * @param elmMachine + */ +void MachineConfigFile::readMachine(const xml::ElementNode &elmMachine) +{ + Utf8Str strUUID; + if ( elmMachine.getAttributeValue("uuid", strUUID) + && elmMachine.getAttributeValue("name", machineUserData.strName)) + { + parseUUID(uuid, strUUID, &elmMachine); + + elmMachine.getAttributeValue("directoryIncludesUUID", machineUserData.fDirectoryIncludesUUID); + elmMachine.getAttributeValue("nameSync", machineUserData.fNameSync); + + Utf8Str str; + elmMachine.getAttributeValue("Description", machineUserData.strDescription); + elmMachine.getAttributeValue("OSType", machineUserData.strOsType); + if (m->sv < SettingsVersion_v1_5) + convertOldOSType_pre1_5(machineUserData.strOsType); + + elmMachine.getAttributeValue("stateKeyId", strStateKeyId); + elmMachine.getAttributeValue("stateKeyStore", strStateKeyStore); + elmMachine.getAttributeValuePath("stateFile", strStateFile); + + elmMachine.getAttributeValue("logKeyId", strLogKeyId); + elmMachine.getAttributeValue("logKeyStore", strLogKeyStore); + + if (elmMachine.getAttributeValue("currentSnapshot", str)) + parseUUID(uuidCurrentSnapshot, str, &elmMachine); + + elmMachine.getAttributeValuePath("snapshotFolder", machineUserData.strSnapshotFolder); + + if (!elmMachine.getAttributeValue("currentStateModified", fCurrentStateModified)) + fCurrentStateModified = true; + if (elmMachine.getAttributeValue("lastStateChange", str)) + parseTimestamp(timeLastStateChange, str, &elmMachine); + // constructor has called RTTimeNow(&timeLastStateChange) before + if (elmMachine.getAttributeValue("aborted", fAborted)) + fAborted = true; + + { + Utf8Str strVMPriority; + if (elmMachine.getAttributeValue("processPriority", strVMPriority)) + { + if (strVMPriority == "Flat") + machineUserData.enmVMPriority = VMProcPriority_Flat; + else if (strVMPriority == "Low") + machineUserData.enmVMPriority = VMProcPriority_Low; + else if (strVMPriority == "Normal") + machineUserData.enmVMPriority = VMProcPriority_Normal; + else if (strVMPriority == "High") + machineUserData.enmVMPriority = VMProcPriority_High; + else + machineUserData.enmVMPriority = VMProcPriority_Default; + } + } + + str.setNull(); + elmMachine.getAttributeValue("icon", str); + parseBase64(machineUserData.ovIcon, str, &elmMachine); + + // parse Hardware before the other elements because other things depend on it + const xml::ElementNode *pelmHardware; + if (!(pelmHardware = elmMachine.findChildElement("Hardware"))) + throw ConfigFileError(this, &elmMachine, N_("Required Machine/Hardware element is missing")); + readHardware(*pelmHardware, hardwareMachine); + + xml::NodesLoop nlRootChildren(elmMachine); + const xml::ElementNode *pelmMachineChild; + while ((pelmMachineChild = nlRootChildren.forAllNodes())) + { + if (pelmMachineChild->nameEquals("ExtraData")) + readExtraData(*pelmMachineChild, + mapExtraDataItems); + else if ( (m->sv < SettingsVersion_v1_7) + && (pelmMachineChild->nameEquals("HardDiskAttachments")) + ) + readHardDiskAttachments_pre1_7(*pelmMachineChild, hardwareMachine.storage); + else if ( (m->sv >= SettingsVersion_v1_7) + && (pelmMachineChild->nameEquals("StorageControllers")) + ) + readStorageControllers(*pelmMachineChild, hardwareMachine.storage); + else if (pelmMachineChild->nameEquals("Snapshot")) + { + if (uuidCurrentSnapshot.isZero()) + throw ConfigFileError(this, &elmMachine, N_("Snapshots present but required Machine/@currentSnapshot attribute is missing")); + bool foundCurrentSnapshot = false; + // Work directly with the target list, because otherwise + // the entire snapshot settings tree will need to be copied, + // and the usual STL implementation needs a lot of stack space. + llFirstSnapshot.push_back(Snapshot::Empty); + // this will also read all child snapshots + foundCurrentSnapshot = readSnapshot(uuidCurrentSnapshot, *pelmMachineChild, llFirstSnapshot.back()); + if (!foundCurrentSnapshot) + throw ConfigFileError(this, &elmMachine, N_("Snapshots present but none matches the UUID in the Machine/@currentSnapshot attribute")); + } + else if (pelmMachineChild->nameEquals("Description")) + machineUserData.strDescription = pelmMachineChild->getValue(); + else if (pelmMachineChild->nameEquals("Teleporter")) + readTeleporter(*pelmMachineChild, machineUserData); + else if (pelmMachineChild->nameEquals("MediaRegistry")) + readMediaRegistry(*pelmMachineChild, mediaRegistry); + else if (pelmMachineChild->nameEquals("Debugging")) + readDebugging(*pelmMachineChild, debugging); + else if (pelmMachineChild->nameEquals("Autostart")) + readAutostart(*pelmMachineChild, autostart); + else if (pelmMachineChild->nameEquals("Groups")) + readGroups(*pelmMachineChild, machineUserData.llGroups); + + if ( m->sv >= SettingsVersion_v1_14 + && m->sv < SettingsVersion_v1_19 + && pelmMachineChild->nameEquals("VideoCapture")) /* For settings >= 1.14 (< VBox 7.0). */ + readRecordingSettings(*pelmMachineChild, hardwareMachine.graphicsAdapter.cMonitors, recordingSettings); + else if ( m->sv >= SettingsVersion_v1_19 + && pelmMachineChild->nameEquals("Recording")) /* Only exists for settings >= 1.19 (VBox 7.0). */ + readRecordingSettings(*pelmMachineChild, hardwareMachine.graphicsAdapter.cMonitors, recordingSettings); + } + + if (m->sv < SettingsVersion_v1_9) + // go through Hardware once more to repair the settings controller structures + // with data from old DVDDrive and FloppyDrive elements + readDVDAndFloppies_pre1_9(*pelmHardware, hardwareMachine.storage); + } + else + throw ConfigFileError(this, &elmMachine, N_("Required Machine/@uuid or @name attributes is missing")); +} + +/** + * Called from the constructor to decrypt the machine config and read + * data from it. + * @param elmMachine + * @param pCryptoIf Pointer to the cryptographic interface. + * @param pszPassword The password to decrypt the config with. + */ +void MachineConfigFile::readMachineEncrypted(const xml::ElementNode &elmMachine, + PCVBOXCRYPTOIF pCryptoIf = NULL, + const char *pszPassword = NULL) +{ + Utf8Str strUUID; + if (elmMachine.getAttributeValue("uuid", strUUID)) + { + parseUUID(uuid, strUUID, &elmMachine); + if (!elmMachine.getAttributeValue("keyId", strKeyId)) + throw ConfigFileError(this, &elmMachine, N_("Required MachineEncrypted/@keyId attribute is missing")); + if (!elmMachine.getAttributeValue("keyStore", strKeyStore)) + throw ConfigFileError(this, &elmMachine, N_("Required MachineEncrypted/@keyStore attribute is missing")); + + if (!pszPassword) + { + enmParseState = ParseState_PasswordError; + return; + } + + VBOXCRYPTOCTX hCryptoCtx = NULL; + int rc = pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), pszPassword, &hCryptoCtx); + if (RT_SUCCESS(rc)) + { + com::Utf8Str str = elmMachine.getValue(); + IconBlob abEncrypted; /** @todo Rename IconBlob because this is not about icons. */ + /** @todo This is not nice. */ + try + { + parseBase64(abEncrypted, str, &elmMachine); + } + catch(...) + { + int rc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx); + AssertRC(rc2); + throw; + } + + IconBlob abDecrypted(abEncrypted.size()); + size_t cbDecrypted = 0; + rc = pCryptoIf->pfnCryptoCtxDecrypt(hCryptoCtx, false /*fPartial*/, + &abEncrypted[0], abEncrypted.size(), + uuid.raw(), sizeof(RTUUID), + &abDecrypted[0], abDecrypted.size(), &cbDecrypted); + int rc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx); + AssertRC(rc2); + + if (RT_SUCCESS(rc)) + { + abDecrypted.resize(cbDecrypted); + xml::XmlMemParser parser; + xml::Document *pDoc = new xml::Document; + parser.read(&abDecrypted[0], abDecrypted.size(), m->strFilename, *pDoc); + xml::ElementNode *pelmRoot = pDoc->getRootElement(); + if (!pelmRoot || !pelmRoot->nameEquals("Machine")) + throw ConfigFileError(this, pelmRoot, N_("Root element in Machine settings encrypted block must be \"Machine\"")); + readMachine(*pelmRoot); + delete pDoc; + } + } + + if (RT_FAILURE(rc)) + { + if (rc == VERR_ACCESS_DENIED) + enmParseState = ParseState_PasswordError; + else + throw ConfigFileError(this, &elmMachine, N_("Parsing config failed. (%Rrc)"), rc); + } + } + else + throw ConfigFileError(this, &elmMachine, N_("Required MachineEncrypted/@uuid attribute is missing")); +} + +/** + * Creates a \<Hardware\> node under elmParent and then writes out the XML + * keys under that. Called for both the \<Machine\> node and for snapshots. + * @param elmParent + * @param hw + * @param fl + * @param pllElementsWithUuidAttributes + */ +void MachineConfigFile::buildHardwareXML(xml::ElementNode &elmParent, + const Hardware &hw, + uint32_t fl, + std::list<xml::ElementNode*> *pllElementsWithUuidAttributes) +{ + xml::ElementNode *pelmHardware = elmParent.createChild("Hardware"); + + if ( m->sv >= SettingsVersion_v1_4 + && (m->sv < SettingsVersion_v1_7 ? hw.strVersion != "1" : hw.strVersion != "2")) + pelmHardware->setAttribute("version", hw.strVersion); + + if ((m->sv >= SettingsVersion_v1_9) + && !hw.uuid.isZero() + && hw.uuid.isValid() + ) + pelmHardware->setAttribute("uuid", hw.uuid.toStringCurly()); + + xml::ElementNode *pelmCPU = pelmHardware->createChild("CPU"); + + if (!hw.fHardwareVirt) + pelmCPU->createChild("HardwareVirtEx")->setAttribute("enabled", hw.fHardwareVirt); + if (!hw.fNestedPaging) + pelmCPU->createChild("HardwareVirtExNestedPaging")->setAttribute("enabled", hw.fNestedPaging); + if (!hw.fVPID) + pelmCPU->createChild("HardwareVirtExVPID")->setAttribute("enabled", hw.fVPID); + if (!hw.fUnrestrictedExecution) + pelmCPU->createChild("HardwareVirtExUX")->setAttribute("enabled", hw.fUnrestrictedExecution); + // PAE has too crazy default handling, must always save this setting. + pelmCPU->createChild("PAE")->setAttribute("enabled", hw.fPAE); + if (m->sv >= SettingsVersion_v1_16) + { + if (hw.fIBPBOnVMEntry || hw.fIBPBOnVMExit) + { + xml::ElementNode *pelmChild = pelmCPU->createChild("IBPBOn"); + if (hw.fIBPBOnVMExit) + pelmChild->setAttribute("vmexit", hw.fIBPBOnVMExit); + if (hw.fIBPBOnVMEntry) + pelmChild->setAttribute("vmentry", hw.fIBPBOnVMEntry); + } + if (hw.fSpecCtrl) + pelmCPU->createChild("SpecCtrl")->setAttribute("enabled", hw.fSpecCtrl); + if (hw.fSpecCtrlByHost) + pelmCPU->createChild("SpecCtrlByHost")->setAttribute("enabled", hw.fSpecCtrlByHost); + if (!hw.fL1DFlushOnSched || hw.fL1DFlushOnVMEntry) + { + xml::ElementNode *pelmChild = pelmCPU->createChild("L1DFlushOn"); + if (!hw.fL1DFlushOnSched) + pelmChild->setAttribute("scheduling", hw.fL1DFlushOnSched); + if (hw.fL1DFlushOnVMEntry) + pelmChild->setAttribute("vmentry", hw.fL1DFlushOnVMEntry); + } + if (!hw.fMDSClearOnSched || hw.fMDSClearOnVMEntry) + { + xml::ElementNode *pelmChild = pelmCPU->createChild("MDSClearOn"); + if (!hw.fMDSClearOnSched) + pelmChild->setAttribute("scheduling", hw.fMDSClearOnSched); + if (hw.fMDSClearOnVMEntry) + pelmChild->setAttribute("vmentry", hw.fMDSClearOnVMEntry); + } + } + if (m->sv >= SettingsVersion_v1_17 && hw.fNestedHWVirt) + pelmCPU->createChild("NestedHWVirt")->setAttribute("enabled", hw.fNestedHWVirt); + + if (m->sv >= SettingsVersion_v1_18 && !hw.fVirtVmsaveVmload) + pelmCPU->createChild("HardwareVirtExVirtVmsaveVmload")->setAttribute("enabled", hw.fVirtVmsaveVmload); + + if (m->sv >= SettingsVersion_v1_14 && hw.enmLongMode != Hardware::LongMode_Legacy) + { + // LongMode has too crazy default handling, must always save this setting. + pelmCPU->createChild("LongMode")->setAttribute("enabled", hw.enmLongMode == Hardware::LongMode_Enabled); + } + + if (hw.fTripleFaultReset) + pelmCPU->createChild("TripleFaultReset")->setAttribute("enabled", hw.fTripleFaultReset); + if (m->sv >= SettingsVersion_v1_14) + { + if (hw.fX2APIC) + pelmCPU->createChild("X2APIC")->setAttribute("enabled", hw.fX2APIC); + else if (!hw.fAPIC) + pelmCPU->createChild("APIC")->setAttribute("enabled", hw.fAPIC); + } + if (hw.cCPUs > 1) + pelmCPU->setAttribute("count", hw.cCPUs); + if (hw.ulCpuExecutionCap != 100) + pelmCPU->setAttribute("executionCap", hw.ulCpuExecutionCap); + if (hw.uCpuIdPortabilityLevel != 0) + pelmCPU->setAttribute("CpuIdPortabilityLevel", hw.uCpuIdPortabilityLevel); + if (!hw.strCpuProfile.equals("host") && hw.strCpuProfile.isNotEmpty()) + pelmCPU->setAttribute("CpuProfile", hw.strCpuProfile); + + // HardwareVirtExLargePages has too crazy default handling, must always save this setting. + pelmCPU->createChild("HardwareVirtExLargePages")->setAttribute("enabled", hw.fLargePages); + + if (m->sv >= SettingsVersion_v1_9) + { + if (hw.fHardwareVirtForce) + pelmCPU->createChild("HardwareVirtForce")->setAttribute("enabled", hw.fHardwareVirtForce); + } + + if (m->sv >= SettingsVersion_v1_9 && hw.fUseNativeApi) + pelmCPU->createChild("HardwareVirtExUseNativeApi")->setAttribute("enabled", hw.fUseNativeApi); + + if (m->sv >= SettingsVersion_v1_10) + { + if (hw.fCpuHotPlug) + pelmCPU->setAttribute("hotplug", hw.fCpuHotPlug); + + xml::ElementNode *pelmCpuTree = NULL; + for (CpuList::const_iterator it = hw.llCpus.begin(); + it != hw.llCpus.end(); + ++it) + { + const Cpu &cpu = *it; + + if (pelmCpuTree == NULL) + pelmCpuTree = pelmCPU->createChild("CpuTree"); + + xml::ElementNode *pelmCpu = pelmCpuTree->createChild("Cpu"); + pelmCpu->setAttribute("id", cpu.ulId); + } + } + + xml::ElementNode *pelmCpuIdTree = NULL; + for (CpuIdLeafsList::const_iterator it = hw.llCpuIdLeafs.begin(); + it != hw.llCpuIdLeafs.end(); + ++it) + { + const CpuIdLeaf &leaf = *it; + + if (pelmCpuIdTree == NULL) + pelmCpuIdTree = pelmCPU->createChild("CpuIdTree"); + + xml::ElementNode *pelmCpuIdLeaf = pelmCpuIdTree->createChild("CpuIdLeaf"); + pelmCpuIdLeaf->setAttribute("id", leaf.idx); + if (leaf.idxSub != 0) + pelmCpuIdLeaf->setAttribute("subleaf", leaf.idxSub); + pelmCpuIdLeaf->setAttribute("eax", leaf.uEax); + pelmCpuIdLeaf->setAttribute("ebx", leaf.uEbx); + pelmCpuIdLeaf->setAttribute("ecx", leaf.uEcx); + pelmCpuIdLeaf->setAttribute("edx", leaf.uEdx); + } + + xml::ElementNode *pelmMemory = pelmHardware->createChild("Memory"); + pelmMemory->setAttribute("RAMSize", hw.ulMemorySizeMB); + if (m->sv >= SettingsVersion_v1_10) + { + if (hw.fPageFusionEnabled) + pelmMemory->setAttribute("PageFusion", hw.fPageFusionEnabled); + } + + if ( (m->sv >= SettingsVersion_v1_9) + && (hw.firmwareType >= FirmwareType_EFI) + ) + { + xml::ElementNode *pelmFirmware = pelmHardware->createChild("Firmware"); + const char *pcszFirmware; + + switch (hw.firmwareType) + { + case FirmwareType_EFI: pcszFirmware = "EFI"; break; + case FirmwareType_EFI32: pcszFirmware = "EFI32"; break; + case FirmwareType_EFI64: pcszFirmware = "EFI64"; break; + case FirmwareType_EFIDUAL: pcszFirmware = "EFIDUAL"; break; + default: pcszFirmware = "None"; break; + } + pelmFirmware->setAttribute("type", pcszFirmware); + } + + if ( m->sv >= SettingsVersion_v1_10 + && ( hw.pointingHIDType != PointingHIDType_PS2Mouse + || hw.keyboardHIDType != KeyboardHIDType_PS2Keyboard)) + { + xml::ElementNode *pelmHID = pelmHardware->createChild("HID"); + const char *pcszHID; + + if (hw.pointingHIDType != PointingHIDType_PS2Mouse) + { + switch (hw.pointingHIDType) + { + case PointingHIDType_USBMouse: pcszHID = "USBMouse"; break; + case PointingHIDType_USBTablet: pcszHID = "USBTablet"; break; + case PointingHIDType_PS2Mouse: pcszHID = "PS2Mouse"; break; + case PointingHIDType_ComboMouse: pcszHID = "ComboMouse"; break; + case PointingHIDType_USBMultiTouch: pcszHID = "USBMultiTouch";break; + case PointingHIDType_USBMultiTouchScreenPlusPad: pcszHID = "USBMTScreenPlusPad";break; + case PointingHIDType_None: pcszHID = "None"; break; + default: Assert(false); pcszHID = "PS2Mouse"; break; + } + pelmHID->setAttribute("Pointing", pcszHID); + } + + if (hw.keyboardHIDType != KeyboardHIDType_PS2Keyboard) + { + switch (hw.keyboardHIDType) + { + case KeyboardHIDType_USBKeyboard: pcszHID = "USBKeyboard"; break; + case KeyboardHIDType_PS2Keyboard: pcszHID = "PS2Keyboard"; break; + case KeyboardHIDType_ComboKeyboard: pcszHID = "ComboKeyboard"; break; + case KeyboardHIDType_None: pcszHID = "None"; break; + default: Assert(false); pcszHID = "PS2Keyboard"; break; + } + pelmHID->setAttribute("Keyboard", pcszHID); + } + } + + if ( (m->sv >= SettingsVersion_v1_10) + && hw.fHPETEnabled + ) + { + xml::ElementNode *pelmHPET = pelmHardware->createChild("HPET"); + pelmHPET->setAttribute("enabled", hw.fHPETEnabled); + } + + if ( (m->sv >= SettingsVersion_v1_11) + ) + { + if (hw.chipsetType != ChipsetType_PIIX3) + { + xml::ElementNode *pelmChipset = pelmHardware->createChild("Chipset"); + const char *pcszChipset; + + switch (hw.chipsetType) + { + case ChipsetType_PIIX3: pcszChipset = "PIIX3"; break; + case ChipsetType_ICH9: pcszChipset = "ICH9"; break; + default: Assert(false); pcszChipset = "PIIX3"; break; + } + pelmChipset->setAttribute("type", pcszChipset); + } + } + + if ( (m->sv >= SettingsVersion_v1_15) + && !hw.areParavirtDefaultSettings(m->sv) + ) + { + const char *pcszParavirtProvider; + switch (hw.paravirtProvider) + { + case ParavirtProvider_None: pcszParavirtProvider = "None"; break; + case ParavirtProvider_Default: pcszParavirtProvider = "Default"; break; + case ParavirtProvider_Legacy: pcszParavirtProvider = "Legacy"; break; + case ParavirtProvider_Minimal: pcszParavirtProvider = "Minimal"; break; + case ParavirtProvider_HyperV: pcszParavirtProvider = "HyperV"; break; + case ParavirtProvider_KVM: pcszParavirtProvider = "KVM"; break; + default: Assert(false); pcszParavirtProvider = "None"; break; + } + + xml::ElementNode *pelmParavirt = pelmHardware->createChild("Paravirt"); + pelmParavirt->setAttribute("provider", pcszParavirtProvider); + + if ( m->sv >= SettingsVersion_v1_16 + && hw.strParavirtDebug.isNotEmpty()) + pelmParavirt->setAttribute("debug", hw.strParavirtDebug); + } + + if ( m->sv >= SettingsVersion_v1_19 + && hw.iommuType != IommuType_None) + { + const char *pcszIommuType; + switch (hw.iommuType) + { + case IommuType_None: pcszIommuType = "None"; break; + case IommuType_Automatic: pcszIommuType = "Automatic"; break; + case IommuType_AMD: pcszIommuType = "AMD"; break; + case IommuType_Intel: pcszIommuType = "Intel"; break; + default: Assert(false); pcszIommuType = "None"; break; + } + + xml::ElementNode *pelmIommu = pelmHardware->createChild("Iommu"); + pelmIommu->setAttribute("type", pcszIommuType); + } + + if (!hw.areBootOrderDefaultSettings()) + { + xml::ElementNode *pelmBoot = pelmHardware->createChild("Boot"); + for (BootOrderMap::const_iterator it = hw.mapBootOrder.begin(); + it != hw.mapBootOrder.end(); + ++it) + { + uint32_t i = it->first; + DeviceType_T type = it->second; + const char *pcszDevice; + + switch (type) + { + case DeviceType_Floppy: pcszDevice = "Floppy"; break; + case DeviceType_DVD: pcszDevice = "DVD"; break; + case DeviceType_HardDisk: pcszDevice = "HardDisk"; break; + case DeviceType_Network: pcszDevice = "Network"; break; + default: /*case DeviceType_Null:*/ pcszDevice = "None"; break; + } + + xml::ElementNode *pelmOrder = pelmBoot->createChild("Order"); + pelmOrder->setAttribute("position", + i + 1); // XML is 1-based but internal data is 0-based + pelmOrder->setAttribute("device", pcszDevice); + } + } + + if (!hw.graphicsAdapter.areDefaultSettings()) + { + xml::ElementNode *pelmDisplay = pelmHardware->createChild("Display"); + if (hw.graphicsAdapter.graphicsControllerType != GraphicsControllerType_VBoxVGA) + { + const char *pcszGraphics; + switch (hw.graphicsAdapter.graphicsControllerType) + { + case GraphicsControllerType_VBoxVGA: pcszGraphics = "VBoxVGA"; break; + case GraphicsControllerType_VMSVGA: pcszGraphics = "VMSVGA"; break; + case GraphicsControllerType_VBoxSVGA: pcszGraphics = "VBoxSVGA"; break; + default: /*case GraphicsControllerType_Null:*/ pcszGraphics = "None"; break; + } + pelmDisplay->setAttribute("controller", pcszGraphics); + } + if (hw.graphicsAdapter.ulVRAMSizeMB != 8) + pelmDisplay->setAttribute("VRAMSize", hw.graphicsAdapter.ulVRAMSizeMB); + if (hw.graphicsAdapter.cMonitors > 1) + pelmDisplay->setAttribute("monitorCount", hw.graphicsAdapter.cMonitors); + if (hw.graphicsAdapter.fAccelerate3D) + pelmDisplay->setAttribute("accelerate3D", hw.graphicsAdapter.fAccelerate3D); + + if (m->sv >= SettingsVersion_v1_8) + { + if (hw.graphicsAdapter.fAccelerate2DVideo) + pelmDisplay->setAttribute("accelerate2DVideo", hw.graphicsAdapter.fAccelerate2DVideo); + } + } + + if (!hw.vrdeSettings.areDefaultSettings(m->sv)) + { + xml::ElementNode *pelmVRDE = pelmHardware->createChild("RemoteDisplay"); + if (m->sv < SettingsVersion_v1_16 ? !hw.vrdeSettings.fEnabled : hw.vrdeSettings.fEnabled) + pelmVRDE->setAttribute("enabled", hw.vrdeSettings.fEnabled); + if (m->sv < SettingsVersion_v1_11) + { + /* In VBox 4.0 these attributes are replaced with "Properties". */ + Utf8Str strPort; + StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.find("TCP/Ports"); + if (it != hw.vrdeSettings.mapProperties.end()) + strPort = it->second; + if (!strPort.length()) + strPort = "3389"; + pelmVRDE->setAttribute("port", strPort); + + Utf8Str strAddress; + it = hw.vrdeSettings.mapProperties.find("TCP/Address"); + if (it != hw.vrdeSettings.mapProperties.end()) + strAddress = it->second; + if (strAddress.length()) + pelmVRDE->setAttribute("netAddress", strAddress); + } + if (hw.vrdeSettings.authType != AuthType_Null) + { + const char *pcszAuthType; + switch (hw.vrdeSettings.authType) + { + case AuthType_Guest: pcszAuthType = "Guest"; break; + case AuthType_External: pcszAuthType = "External"; break; + default: /*case AuthType_Null:*/ pcszAuthType = "Null"; break; + } + pelmVRDE->setAttribute("authType", pcszAuthType); + } + + if (hw.vrdeSettings.ulAuthTimeout != 0 && hw.vrdeSettings.ulAuthTimeout != 5000) + pelmVRDE->setAttribute("authTimeout", hw.vrdeSettings.ulAuthTimeout); + if (hw.vrdeSettings.fAllowMultiConnection) + pelmVRDE->setAttribute("allowMultiConnection", hw.vrdeSettings.fAllowMultiConnection); + if (hw.vrdeSettings.fReuseSingleConnection) + pelmVRDE->setAttribute("reuseSingleConnection", hw.vrdeSettings.fReuseSingleConnection); + + if (m->sv == SettingsVersion_v1_10) + { + xml::ElementNode *pelmVideoChannel = pelmVRDE->createChild("VideoChannel"); + + /* In 4.0 videochannel settings were replaced with properties, so look at properties. */ + Utf8Str str; + StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.find("VideoChannel/Enabled"); + if (it != hw.vrdeSettings.mapProperties.end()) + str = it->second; + bool fVideoChannel = RTStrICmp(str.c_str(), "true") == 0 + || RTStrCmp(str.c_str(), "1") == 0; + pelmVideoChannel->setAttribute("enabled", fVideoChannel); + + it = hw.vrdeSettings.mapProperties.find("VideoChannel/Quality"); + if (it != hw.vrdeSettings.mapProperties.end()) + str = it->second; + uint32_t ulVideoChannelQuality = RTStrToUInt32(str.c_str()); /* This returns 0 on invalid string which is ok. */ + if (ulVideoChannelQuality == 0) + ulVideoChannelQuality = 75; + else + ulVideoChannelQuality = RT_CLAMP(ulVideoChannelQuality, 10, 100); + pelmVideoChannel->setAttribute("quality", ulVideoChannelQuality); + } + if (m->sv >= SettingsVersion_v1_11) + { + if (hw.vrdeSettings.strAuthLibrary.length()) + pelmVRDE->setAttribute("authLibrary", hw.vrdeSettings.strAuthLibrary); + if (hw.vrdeSettings.strVrdeExtPack.isNotEmpty()) + pelmVRDE->setAttribute("VRDEExtPack", hw.vrdeSettings.strVrdeExtPack); + if (hw.vrdeSettings.mapProperties.size() > 0) + { + xml::ElementNode *pelmProperties = pelmVRDE->createChild("VRDEProperties"); + for (StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.begin(); + it != hw.vrdeSettings.mapProperties.end(); + ++it) + { + const Utf8Str &strName = it->first; + const Utf8Str &strValue = it->second; + xml::ElementNode *pelm = pelmProperties->createChild("Property"); + pelm->setAttribute("name", strName); + pelm->setAttribute("value", strValue); + } + } + } + } + + if (!hw.biosSettings.areDefaultSettings() || !hw.nvramSettings.areDefaultSettings()) + { + xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS"); + if (!hw.biosSettings.fACPIEnabled) + pelmBIOS->createChild("ACPI")->setAttribute("enabled", hw.biosSettings.fACPIEnabled); + if (hw.biosSettings.fIOAPICEnabled) + pelmBIOS->createChild("IOAPIC")->setAttribute("enabled", hw.biosSettings.fIOAPICEnabled); + if (hw.biosSettings.apicMode != APICMode_APIC) + { + const char *pcszAPIC; + switch (hw.biosSettings.apicMode) + { + case APICMode_Disabled: + pcszAPIC = "Disabled"; + break; + case APICMode_APIC: + default: + pcszAPIC = "APIC"; + break; + case APICMode_X2APIC: + pcszAPIC = "X2APIC"; + break; + } + pelmBIOS->createChild("APIC")->setAttribute("mode", pcszAPIC); + } + + if ( !hw.biosSettings.fLogoFadeIn + || !hw.biosSettings.fLogoFadeOut + || hw.biosSettings.ulLogoDisplayTime + || !hw.biosSettings.strLogoImagePath.isEmpty()) + { + xml::ElementNode *pelmLogo = pelmBIOS->createChild("Logo"); + pelmLogo->setAttribute("fadeIn", hw.biosSettings.fLogoFadeIn); + pelmLogo->setAttribute("fadeOut", hw.biosSettings.fLogoFadeOut); + pelmLogo->setAttribute("displayTime", hw.biosSettings.ulLogoDisplayTime); + if (!hw.biosSettings.strLogoImagePath.isEmpty()) + pelmLogo->setAttribute("imagePath", hw.biosSettings.strLogoImagePath); + } + + if (hw.biosSettings.biosBootMenuMode != BIOSBootMenuMode_MessageAndMenu) + { + const char *pcszBootMenu; + switch (hw.biosSettings.biosBootMenuMode) + { + case BIOSBootMenuMode_Disabled: pcszBootMenu = "Disabled"; break; + case BIOSBootMenuMode_MenuOnly: pcszBootMenu = "MenuOnly"; break; + default: /*BIOSBootMenuMode_MessageAndMenu*/ pcszBootMenu = "MessageAndMenu"; break; + } + pelmBIOS->createChild("BootMenu")->setAttribute("mode", pcszBootMenu); + } + if (hw.biosSettings.llTimeOffset) + pelmBIOS->createChild("TimeOffset")->setAttribute("value", hw.biosSettings.llTimeOffset); + if (hw.biosSettings.fPXEDebugEnabled) + pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled); + if (!hw.nvramSettings.areDefaultSettings()) + { + xml::ElementNode *pelmNvram = pelmBIOS->createChild("NVRAM"); + if (!hw.nvramSettings.strNvramPath.isEmpty()) + pelmNvram->setAttribute("path", hw.nvramSettings.strNvramPath); + if (m->sv >= SettingsVersion_v1_9) + { + if (hw.nvramSettings.strKeyId.isNotEmpty()) + pelmNvram->setAttribute("keyId", hw.nvramSettings.strKeyId); + if (hw.nvramSettings.strKeyStore.isNotEmpty()) + pelmNvram->setAttribute("keyStore", hw.nvramSettings.strKeyStore); + } + } + if (hw.biosSettings.fSmbiosUuidLittleEndian) + pelmBIOS->createChild("SmbiosUuidLittleEndian")->setAttribute("enabled", hw.biosSettings.fSmbiosUuidLittleEndian); + } + + if (!hw.tpmSettings.areDefaultSettings()) + { + xml::ElementNode *pelmTpm = pelmHardware->createChild("TrustedPlatformModule"); + + const char *pcszTpm; + switch (hw.tpmSettings.tpmType) + { + default: + case TpmType_None: + pcszTpm = "None"; + break; + case TpmType_v1_2: + pcszTpm = "v1_2"; + break; + case TpmType_v2_0: + pcszTpm = "v2_0"; + break; + case TpmType_Host: + pcszTpm = "Host"; + break; + case TpmType_Swtpm: + pcszTpm = "Swtpm"; + break; + } + pelmTpm->setAttribute("type", pcszTpm); + pelmTpm->setAttribute("location", hw.tpmSettings.strLocation); + } + + if (m->sv < SettingsVersion_v1_9) + { + // settings formats before 1.9 had separate DVDDrive and FloppyDrive items under Hardware; + // run thru the storage controllers to see if we have a DVD or floppy drives + size_t cDVDs = 0; + size_t cFloppies = 0; + + xml::ElementNode *pelmDVD = pelmHardware->createChild("DVDDrive"); + xml::ElementNode *pelmFloppy = pelmHardware->createChild("FloppyDrive"); + + for (StorageControllersList::const_iterator it = hw.storage.llStorageControllers.begin(); + it != hw.storage.llStorageControllers.end(); + ++it) + { + const StorageController &sctl = *it; + // in old settings format, the DVD drive could only have been under the IDE controller + if (sctl.storageBus == StorageBus_IDE) + { + for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin(); + it2 != sctl.llAttachedDevices.end(); + ++it2) + { + const AttachedDevice &att = *it2; + if (att.deviceType == DeviceType_DVD) + { + if (cDVDs > 0) + throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one DVD drive with old settings format")); + + ++cDVDs; + + pelmDVD->setAttribute("passthrough", att.fPassThrough); + if (att.fTempEject) + pelmDVD->setAttribute("tempeject", att.fTempEject); + + if (!att.uuid.isZero() && att.uuid.isValid()) + pelmDVD->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly()); + else if (att.strHostDriveSrc.length()) + pelmDVD->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc); + } + } + } + else if (sctl.storageBus == StorageBus_Floppy) + { + size_t cFloppiesHere = sctl.llAttachedDevices.size(); + if (cFloppiesHere > 1) + throw ConfigFileError(this, NULL, N_("Internal error: floppy controller cannot have more than one device attachment")); + if (cFloppiesHere) + { + const AttachedDevice &att = sctl.llAttachedDevices.front(); + pelmFloppy->setAttribute("enabled", true); + + if (!att.uuid.isZero() && att.uuid.isValid()) + pelmFloppy->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly()); + else if (att.strHostDriveSrc.length()) + pelmFloppy->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc); + } + + cFloppies += cFloppiesHere; + } + } + + if (cFloppies == 0) + pelmFloppy->setAttribute("enabled", false); + else if (cFloppies > 1) + throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one floppy drive with old settings format")); + } + + if (m->sv < SettingsVersion_v1_14) + { + bool fOhciEnabled = false; + bool fEhciEnabled = false; + xml::ElementNode *pelmUSB = pelmHardware->createChild("USBController"); + + for (USBControllerList::const_iterator it = hw.usbSettings.llUSBControllers.begin(); + it != hw.usbSettings.llUSBControllers.end(); + ++it) + { + const USBController &ctrl = *it; + + switch (ctrl.enmType) + { + case USBControllerType_OHCI: + fOhciEnabled = true; + break; + case USBControllerType_EHCI: + fEhciEnabled = true; + break; + default: + AssertMsgFailed(("Unknown USB controller type %d\n", ctrl.enmType)); + } + } + + pelmUSB->setAttribute("enabled", fOhciEnabled); + pelmUSB->setAttribute("enabledEhci", fEhciEnabled); + + buildUSBDeviceFilters(*pelmUSB, hw.usbSettings.llDeviceFilters, false /* fHostMode */); + } + else + { + if ( hw.usbSettings.llUSBControllers.size() + || hw.usbSettings.llDeviceFilters.size()) + { + xml::ElementNode *pelmUSB = pelmHardware->createChild("USB"); + if (hw.usbSettings.llUSBControllers.size()) + { + xml::ElementNode *pelmCtrls = pelmUSB->createChild("Controllers"); + + for (USBControllerList::const_iterator it = hw.usbSettings.llUSBControllers.begin(); + it != hw.usbSettings.llUSBControllers.end(); + ++it) + { + const USBController &ctrl = *it; + com::Utf8Str strType; + xml::ElementNode *pelmCtrl = pelmCtrls->createChild("Controller"); + + switch (ctrl.enmType) + { + case USBControllerType_OHCI: + strType = "OHCI"; + break; + case USBControllerType_EHCI: + strType = "EHCI"; + break; + case USBControllerType_XHCI: + strType = "XHCI"; + break; + default: + AssertMsgFailed(("Unknown USB controller type %d\n", ctrl.enmType)); + } + + pelmCtrl->setAttribute("name", ctrl.strName); + pelmCtrl->setAttribute("type", strType); + } + } + + if (hw.usbSettings.llDeviceFilters.size()) + { + xml::ElementNode *pelmFilters = pelmUSB->createChild("DeviceFilters"); + buildUSBDeviceFilters(*pelmFilters, hw.usbSettings.llDeviceFilters, false /* fHostMode */); + } + } + } + + if ( hw.llNetworkAdapters.size() + && !hw.areAllNetworkAdaptersDefaultSettings(m->sv)) + { + xml::ElementNode *pelmNetwork = pelmHardware->createChild("Network"); + for (NetworkAdaptersList::const_iterator it = hw.llNetworkAdapters.begin(); + it != hw.llNetworkAdapters.end(); + ++it) + { + const NetworkAdapter &nic = *it; + + if (!nic.areDefaultSettings(m->sv)) + { + xml::ElementNode *pelmAdapter = pelmNetwork->createChild("Adapter"); + pelmAdapter->setAttribute("slot", nic.ulSlot); + if (nic.fEnabled) + pelmAdapter->setAttribute("enabled", nic.fEnabled); + if (!nic.strMACAddress.isEmpty()) + pelmAdapter->setAttribute("MACAddress", nic.strMACAddress); + if ( (m->sv >= SettingsVersion_v1_16 && !nic.fCableConnected) + || (m->sv < SettingsVersion_v1_16 && nic.fCableConnected)) + pelmAdapter->setAttribute("cable", nic.fCableConnected); + if (nic.ulLineSpeed) + pelmAdapter->setAttribute("speed", nic.ulLineSpeed); + if (nic.ulBootPriority != 0) + pelmAdapter->setAttribute("bootPriority", nic.ulBootPriority); + if (nic.fTraceEnabled) + { + pelmAdapter->setAttribute("trace", nic.fTraceEnabled); + pelmAdapter->setAttribute("tracefile", nic.strTraceFile); + } + if (nic.strBandwidthGroup.isNotEmpty()) + pelmAdapter->setAttribute("bandwidthGroup", nic.strBandwidthGroup); + + const char *pszPolicy; + switch (nic.enmPromiscModePolicy) + { + case NetworkAdapterPromiscModePolicy_Deny: pszPolicy = NULL; break; + case NetworkAdapterPromiscModePolicy_AllowNetwork: pszPolicy = "AllowNetwork"; break; + case NetworkAdapterPromiscModePolicy_AllowAll: pszPolicy = "AllowAll"; break; + default: pszPolicy = NULL; AssertFailed(); break; + } + if (pszPolicy) + pelmAdapter->setAttribute("promiscuousModePolicy", pszPolicy); + + if ( (m->sv >= SettingsVersion_v1_16 && nic.type != NetworkAdapterType_Am79C973) + || (m->sv < SettingsVersion_v1_16 && nic.type != NetworkAdapterType_Am79C970A)) + { + const char *pcszType; + switch (nic.type) + { + case NetworkAdapterType_Am79C973: pcszType = "Am79C973"; break; + case NetworkAdapterType_Am79C960: pcszType = "Am79C960"; break; + case NetworkAdapterType_I82540EM: pcszType = "82540EM"; break; + case NetworkAdapterType_I82543GC: pcszType = "82543GC"; break; + case NetworkAdapterType_I82545EM: pcszType = "82545EM"; break; + case NetworkAdapterType_Virtio: pcszType = "virtio"; break; + case NetworkAdapterType_NE1000: pcszType = "NE1000"; break; + case NetworkAdapterType_NE2000: pcszType = "NE2000"; break; + case NetworkAdapterType_WD8003: pcszType = "WD8003"; break; + case NetworkAdapterType_WD8013: pcszType = "WD8013"; break; + case NetworkAdapterType_ELNK2: pcszType = "3C503"; break; + case NetworkAdapterType_ELNK1: pcszType = "3C501"; break; + default: /*case NetworkAdapterType_Am79C970A:*/ pcszType = "Am79C970A"; break; + } + pelmAdapter->setAttribute("type", pcszType); + } + + xml::ElementNode *pelmNAT; + if (m->sv < SettingsVersion_v1_10) + { + switch (nic.mode) + { + case NetworkAttachmentType_NAT: + pelmNAT = pelmAdapter->createChild("NAT"); + if (nic.nat.strNetwork.length()) + pelmNAT->setAttribute("network", nic.nat.strNetwork); + break; + + case NetworkAttachmentType_Bridged: + pelmAdapter->createChild("BridgedInterface")->setAttribute("name", nic.strBridgedName); + break; + + case NetworkAttachmentType_Internal: + pelmAdapter->createChild("InternalNetwork")->setAttribute("name", nic.strInternalNetworkName); + break; + + case NetworkAttachmentType_HostOnly: + pelmAdapter->createChild("HostOnlyInterface")->setAttribute("name", nic.strHostOnlyName); + break; + + default: /*case NetworkAttachmentType_Null:*/ + break; + } + } + else + { + /* m->sv >= SettingsVersion_v1_10 */ + if (!nic.areDisabledDefaultSettings(m->sv)) + { + xml::ElementNode *pelmDisabledNode = pelmAdapter->createChild("DisabledModes"); + if (nic.mode != NetworkAttachmentType_NAT) + buildNetworkXML(NetworkAttachmentType_NAT, false, *pelmDisabledNode, nic); + if (nic.mode != NetworkAttachmentType_Bridged) + buildNetworkXML(NetworkAttachmentType_Bridged, false, *pelmDisabledNode, nic); + if (nic.mode != NetworkAttachmentType_Internal) + buildNetworkXML(NetworkAttachmentType_Internal, false, *pelmDisabledNode, nic); + if (nic.mode != NetworkAttachmentType_HostOnly) + buildNetworkXML(NetworkAttachmentType_HostOnly, false, *pelmDisabledNode, nic); + if (nic.mode != NetworkAttachmentType_Generic) + buildNetworkXML(NetworkAttachmentType_Generic, false, *pelmDisabledNode, nic); + if (nic.mode != NetworkAttachmentType_NATNetwork) + buildNetworkXML(NetworkAttachmentType_NATNetwork, false, *pelmDisabledNode, nic); +#ifdef VBOX_WITH_CLOUD_NET + /// @todo Bump settings version! + if (nic.mode != NetworkAttachmentType_Cloud) + buildNetworkXML(NetworkAttachmentType_Cloud, false, *pelmDisabledNode, nic); +#endif /* VBOX_WITH_CLOUD_NET */ +#ifdef VBOX_WITH_VMNET + if (nic.mode != NetworkAttachmentType_HostOnlyNetwork) + buildNetworkXML(NetworkAttachmentType_HostOnlyNetwork, false, *pelmDisabledNode, nic); +#endif /* VBOX_WITH_VMNET */ + } + buildNetworkXML(nic.mode, true, *pelmAdapter, nic); + } + } + } + } + + if (hw.llSerialPorts.size()) + { + xml::ElementNode *pelmPorts = pelmHardware->createChild("UART"); + for (SerialPortsList::const_iterator it = hw.llSerialPorts.begin(); + it != hw.llSerialPorts.end(); + ++it) + { + const SerialPort &port = *it; + xml::ElementNode *pelmPort = pelmPorts->createChild("Port"); + pelmPort->setAttribute("slot", port.ulSlot); + pelmPort->setAttribute("enabled", port.fEnabled); + pelmPort->setAttributeHex("IOBase", port.ulIOBase); + pelmPort->setAttribute("IRQ", port.ulIRQ); + + const char *pcszHostMode; + switch (port.portMode) + { + case PortMode_HostPipe: pcszHostMode = "HostPipe"; break; + case PortMode_HostDevice: pcszHostMode = "HostDevice"; break; + case PortMode_TCP: pcszHostMode = "TCP"; break; + case PortMode_RawFile: pcszHostMode = "RawFile"; break; + default: /*case PortMode_Disconnected:*/ pcszHostMode = "Disconnected"; break; + } + switch (port.portMode) + { + case PortMode_TCP: + case PortMode_HostPipe: + pelmPort->setAttribute("server", port.fServer); + RT_FALL_THRU(); + case PortMode_HostDevice: + case PortMode_RawFile: + pelmPort->setAttribute("path", port.strPath); + break; + + default: + break; + } + pelmPort->setAttribute("hostMode", pcszHostMode); + + if ( m->sv >= SettingsVersion_v1_17 + && port.uartType != UartType_U16550A) + { + const char *pcszUartType; + + switch (port.uartType) + { + case UartType_U16450: pcszUartType = "16450"; break; + case UartType_U16550A: pcszUartType = "16550A"; break; + case UartType_U16750: pcszUartType = "16750"; break; + default: pcszUartType = "16550A"; break; + } + pelmPort->setAttribute("uartType", pcszUartType); + } + } + } + + if (hw.llParallelPorts.size()) + { + xml::ElementNode *pelmPorts = pelmHardware->createChild("LPT"); + for (ParallelPortsList::const_iterator it = hw.llParallelPorts.begin(); + it != hw.llParallelPorts.end(); + ++it) + { + const ParallelPort &port = *it; + xml::ElementNode *pelmPort = pelmPorts->createChild("Port"); + pelmPort->setAttribute("slot", port.ulSlot); + pelmPort->setAttribute("enabled", port.fEnabled); + pelmPort->setAttributeHex("IOBase", port.ulIOBase); + pelmPort->setAttribute("IRQ", port.ulIRQ); + if (port.strPath.length()) + pelmPort->setAttribute("path", port.strPath); + } + } + + /* Always write the AudioAdapter config, intentionally not checking if + * the settings are at the default, because that would be problematic + * for the configured host driver type, which would automatically change + * if the default host driver is detected differently. */ + { + xml::ElementNode *pelmAudio = pelmHardware->createChild("AudioAdapter"); + + const char *pcszController; + switch (hw.audioAdapter.controllerType) + { + case AudioControllerType_SB16: + pcszController = "SB16"; + break; + case AudioControllerType_HDA: + if (m->sv >= SettingsVersion_v1_11) + { + pcszController = "HDA"; + break; + } + RT_FALL_THRU(); + case AudioControllerType_AC97: + default: + pcszController = NULL; + break; + } + if (pcszController) + pelmAudio->setAttribute("controller", pcszController); + + const char *pcszCodec; + switch (hw.audioAdapter.codecType) + { + /* Only write out the setting for non-default AC'97 codec + * and leave the rest alone. + */ +#if 0 + case AudioCodecType_SB16: + pcszCodec = "SB16"; + break; + case AudioCodecType_STAC9221: + pcszCodec = "STAC9221"; + break; + case AudioCodecType_STAC9700: + pcszCodec = "STAC9700"; + break; +#endif + case AudioCodecType_AD1980: + pcszCodec = "AD1980"; + break; + default: + /* Don't write out anything if unknown. */ + pcszCodec = NULL; + } + if (pcszCodec) + pelmAudio->setAttribute("codec", pcszCodec); + + /* + * Keep settings >= 1.19 compatible with older VBox versions (on a best effort basis, of course). + * So use a dedicated attribute for the new "Default" audio driver type, which did not exist prior + * settings 1.19 (VBox 7.0) and explicitly set the driver type to something older VBox versions + * know about. + */ + AudioDriverType_T driverType = hw.audioAdapter.driverType; + + if (driverType == AudioDriverType_Default) + { + /* Only recognized by VBox >= 7.0. */ + pelmAudio->setAttribute("useDefault", true); + + /* Make sure to set the actual driver type to the OS' default driver type. + * This is required for VBox < 7.0. */ + driverType = getHostDefaultAudioDriver(); + } + + const char *pcszDriver = NULL; + switch (driverType) + { + case AudioDriverType_Default: /* Handled above. */ break; + case AudioDriverType_WinMM: pcszDriver = "WinMM"; break; + case AudioDriverType_DirectSound: pcszDriver = "DirectSound"; break; + case AudioDriverType_WAS: pcszDriver = "WAS"; break; + case AudioDriverType_ALSA: pcszDriver = "ALSA"; break; + case AudioDriverType_OSS: pcszDriver = "OSS"; break; + case AudioDriverType_Pulse: pcszDriver = "Pulse"; break; + case AudioDriverType_CoreAudio: pcszDriver = "CoreAudio"; break; + case AudioDriverType_SolAudio: pcszDriver = "SolAudio"; break; + case AudioDriverType_MMPM: pcszDriver = "MMPM"; break; + default: /*case AudioDriverType_Null:*/ pcszDriver = "Null"; break; + } + + /* Deliberately have the audio driver explicitly in the config file, + * otherwise an unwritten default driver triggers auto-detection. */ + AssertStmt(pcszDriver != NULL, pcszDriver = "Null"); + pelmAudio->setAttribute("driver", pcszDriver); + + if (hw.audioAdapter.fEnabled || m->sv < SettingsVersion_v1_16) + pelmAudio->setAttribute("enabled", hw.audioAdapter.fEnabled); + + if ( (m->sv <= SettingsVersion_v1_16 && !hw.audioAdapter.fEnabledIn) + || (m->sv > SettingsVersion_v1_16 && hw.audioAdapter.fEnabledIn)) + pelmAudio->setAttribute("enabledIn", hw.audioAdapter.fEnabledIn); + + if ( (m->sv <= SettingsVersion_v1_16 && !hw.audioAdapter.fEnabledOut) + || (m->sv > SettingsVersion_v1_16 && hw.audioAdapter.fEnabledOut)) + pelmAudio->setAttribute("enabledOut", hw.audioAdapter.fEnabledOut); + + if (m->sv >= SettingsVersion_v1_15 && hw.audioAdapter.properties.size() > 0) + { + for (StringsMap::const_iterator it = hw.audioAdapter.properties.begin(); + it != hw.audioAdapter.properties.end(); + ++it) + { + const Utf8Str &strName = it->first; + const Utf8Str &strValue = it->second; + xml::ElementNode *pelm = pelmAudio->createChild("Property"); + pelm->setAttribute("name", strName); + pelm->setAttribute("value", strValue); + } + } + } + + if (m->sv >= SettingsVersion_v1_10 && machineUserData.fRTCUseUTC) + { + xml::ElementNode *pelmRTC = pelmHardware->createChild("RTC"); + pelmRTC->setAttribute("localOrUTC", machineUserData.fRTCUseUTC ? "UTC" : "local"); + } + + if (hw.llSharedFolders.size()) + { + xml::ElementNode *pelmSharedFolders = pelmHardware->createChild("SharedFolders"); + for (SharedFoldersList::const_iterator it = hw.llSharedFolders.begin(); + it != hw.llSharedFolders.end(); + ++it) + { + const SharedFolder &sf = *it; + xml::ElementNode *pelmThis = pelmSharedFolders->createChild("SharedFolder"); + pelmThis->setAttribute("name", sf.strName); + pelmThis->setAttribute("hostPath", sf.strHostPath); + pelmThis->setAttribute("writable", sf.fWritable); + pelmThis->setAttribute("autoMount", sf.fAutoMount); + if (sf.strAutoMountPoint.isNotEmpty()) + pelmThis->setAttribute("autoMountPoint", sf.strAutoMountPoint); + } + } + + xml::ElementNode *pelmClip = pelmHardware->createChild("Clipboard"); + if (pelmClip) + { + if (hw.clipboardMode != ClipboardMode_Disabled) + { + const char *pcszClip; + switch (hw.clipboardMode) + { + default: /*case ClipboardMode_Disabled:*/ pcszClip = "Disabled"; break; + case ClipboardMode_HostToGuest: pcszClip = "HostToGuest"; break; + case ClipboardMode_GuestToHost: pcszClip = "GuestToHost"; break; + case ClipboardMode_Bidirectional: pcszClip = "Bidirectional"; break; + } + pelmClip->setAttribute("mode", pcszClip); + } + + if (hw.fClipboardFileTransfersEnabled) + pelmClip->setAttribute("fileTransfersEnabled", hw.fClipboardFileTransfersEnabled); + } + + if (hw.dndMode != DnDMode_Disabled) + { + xml::ElementNode *pelmDragAndDrop = pelmHardware->createChild("DragAndDrop"); + const char *pcszDragAndDrop; + switch (hw.dndMode) + { + default: /*case DnDMode_Disabled:*/ pcszDragAndDrop = "Disabled"; break; + case DnDMode_HostToGuest: pcszDragAndDrop = "HostToGuest"; break; + case DnDMode_GuestToHost: pcszDragAndDrop = "GuestToHost"; break; + case DnDMode_Bidirectional: pcszDragAndDrop = "Bidirectional"; break; + } + pelmDragAndDrop->setAttribute("mode", pcszDragAndDrop); + } + + if ( m->sv >= SettingsVersion_v1_10 + && !hw.ioSettings.areDefaultSettings()) + { + xml::ElementNode *pelmIO = pelmHardware->createChild("IO"); + xml::ElementNode *pelmIOCache; + + if (!hw.ioSettings.areDefaultSettings()) + { + pelmIOCache = pelmIO->createChild("IoCache"); + if (!hw.ioSettings.fIOCacheEnabled) + pelmIOCache->setAttribute("enabled", hw.ioSettings.fIOCacheEnabled); + if (hw.ioSettings.ulIOCacheSize != 5) + pelmIOCache->setAttribute("size", hw.ioSettings.ulIOCacheSize); + } + + if ( m->sv >= SettingsVersion_v1_11 + && hw.ioSettings.llBandwidthGroups.size()) + { + xml::ElementNode *pelmBandwidthGroups = pelmIO->createChild("BandwidthGroups"); + for (BandwidthGroupList::const_iterator it = hw.ioSettings.llBandwidthGroups.begin(); + it != hw.ioSettings.llBandwidthGroups.end(); + ++it) + { + const BandwidthGroup &gr = *it; + const char *pcszType; + xml::ElementNode *pelmThis = pelmBandwidthGroups->createChild("BandwidthGroup"); + pelmThis->setAttribute("name", gr.strName); + switch (gr.enmType) + { + case BandwidthGroupType_Network: pcszType = "Network"; break; + default: /* BandwidthGrouptype_Disk */ pcszType = "Disk"; break; + } + pelmThis->setAttribute("type", pcszType); + if (m->sv >= SettingsVersion_v1_13) + pelmThis->setAttribute("maxBytesPerSec", gr.cMaxBytesPerSec); + else + pelmThis->setAttribute("maxMbPerSec", gr.cMaxBytesPerSec / _1M); + } + } + } + + if ( m->sv >= SettingsVersion_v1_12 + && hw.pciAttachments.size()) + { + xml::ElementNode *pelmPCI = pelmHardware->createChild("HostPci"); + xml::ElementNode *pelmPCIDevices = pelmPCI->createChild("Devices"); + + for (HostPCIDeviceAttachmentList::const_iterator it = hw.pciAttachments.begin(); + it != hw.pciAttachments.end(); + ++it) + { + const HostPCIDeviceAttachment &hpda = *it; + + xml::ElementNode *pelmThis = pelmPCIDevices->createChild("Device"); + + pelmThis->setAttribute("host", hpda.uHostAddress); + pelmThis->setAttribute("guest", hpda.uGuestAddress); + pelmThis->setAttribute("name", hpda.strDeviceName); + } + } + + if ( m->sv >= SettingsVersion_v1_12 + && hw.fEmulatedUSBCardReader) + { + xml::ElementNode *pelmEmulatedUSB = pelmHardware->createChild("EmulatedUSB"); + + xml::ElementNode *pelmCardReader = pelmEmulatedUSB->createChild("CardReader"); + pelmCardReader->setAttribute("enabled", hw.fEmulatedUSBCardReader); + } + + if ( m->sv >= SettingsVersion_v1_14 + && !hw.strDefaultFrontend.isEmpty()) + { + xml::ElementNode *pelmFrontend = pelmHardware->createChild("Frontend"); + xml::ElementNode *pelmDefault = pelmFrontend->createChild("Default"); + pelmDefault->setAttribute("type", hw.strDefaultFrontend); + } + + if (hw.ulMemoryBalloonSize) + { + xml::ElementNode *pelmGuest = pelmHardware->createChild("Guest"); + pelmGuest->setAttribute("memoryBalloonSize", hw.ulMemoryBalloonSize); + } + + if (hw.llGuestProperties.size()) + { + xml::ElementNode *pelmGuestProps = pelmHardware->createChild("GuestProperties"); + for (GuestPropertiesList::const_iterator it = hw.llGuestProperties.begin(); + it != hw.llGuestProperties.end(); + ++it) + { + const GuestProperty &prop = *it; + xml::ElementNode *pelmProp = pelmGuestProps->createChild("GuestProperty"); + pelmProp->setAttribute("name", prop.strName); + pelmProp->setAttribute("value", prop.strValue); + pelmProp->setAttribute("timestamp", prop.timestamp); + pelmProp->setAttribute("flags", prop.strFlags); + } + } + + /* Starting with settings version of 6.0 (and only 6.1 and later does this, while + * 5.2 and 6.0 understand it), place storage controller settings under hardware, + * where it always should've been. */ + xml::ElementNode &elmStorageParent = (m->sv >= SettingsVersion_v1_17) ? *pelmHardware : elmParent; + buildStorageControllersXML(elmStorageParent, + hw.storage, + !!(fl & BuildMachineXML_SkipRemovableMedia), + pllElementsWithUuidAttributes); +} + +/** + * Fill a \<Network\> node. Only relevant for XML version >= v1_10. + * @param mode + * @param fEnabled + * @param elmParent + * @param nic + */ +void MachineConfigFile::buildNetworkXML(NetworkAttachmentType_T mode, + bool fEnabled, + xml::ElementNode &elmParent, + const NetworkAdapter &nic) +{ + switch (mode) + { + case NetworkAttachmentType_NAT: + // For the currently active network attachment type we have to + // generate the tag, otherwise the attachment type is lost. + if (fEnabled || !nic.nat.areDefaultSettings(m->sv)) + { + xml::ElementNode *pelmNAT = elmParent.createChild("NAT"); + + if (!nic.nat.areDefaultSettings(m->sv)) + { + if (nic.nat.strNetwork.length()) + pelmNAT->setAttribute("network", nic.nat.strNetwork); + if (nic.nat.strBindIP.length()) + pelmNAT->setAttribute("hostip", nic.nat.strBindIP); + if (nic.nat.u32Mtu) + pelmNAT->setAttribute("mtu", nic.nat.u32Mtu); + if (nic.nat.u32SockRcv) + pelmNAT->setAttribute("sockrcv", nic.nat.u32SockRcv); + if (nic.nat.u32SockSnd) + pelmNAT->setAttribute("socksnd", nic.nat.u32SockSnd); + if (nic.nat.u32TcpRcv) + pelmNAT->setAttribute("tcprcv", nic.nat.u32TcpRcv); + if (nic.nat.u32TcpSnd) + pelmNAT->setAttribute("tcpsnd", nic.nat.u32TcpSnd); + if (!nic.nat.areLocalhostReachableDefaultSettings(m->sv)) + pelmNAT->setAttribute("localhost-reachable", nic.nat.fLocalhostReachable); + if (!nic.nat.areDNSDefaultSettings()) + { + xml::ElementNode *pelmDNS = pelmNAT->createChild("DNS"); + if (!nic.nat.fDNSPassDomain) + pelmDNS->setAttribute("pass-domain", nic.nat.fDNSPassDomain); + if (nic.nat.fDNSProxy) + pelmDNS->setAttribute("use-proxy", nic.nat.fDNSProxy); + if (nic.nat.fDNSUseHostResolver) + pelmDNS->setAttribute("use-host-resolver", nic.nat.fDNSUseHostResolver); + } + + if (!nic.nat.areAliasDefaultSettings()) + { + xml::ElementNode *pelmAlias = pelmNAT->createChild("Alias"); + if (nic.nat.fAliasLog) + pelmAlias->setAttribute("logging", nic.nat.fAliasLog); + if (nic.nat.fAliasProxyOnly) + pelmAlias->setAttribute("proxy-only", nic.nat.fAliasProxyOnly); + if (nic.nat.fAliasUseSamePorts) + pelmAlias->setAttribute("use-same-ports", nic.nat.fAliasUseSamePorts); + } + + if (!nic.nat.areTFTPDefaultSettings()) + { + xml::ElementNode *pelmTFTP; + pelmTFTP = pelmNAT->createChild("TFTP"); + if (nic.nat.strTFTPPrefix.length()) + pelmTFTP->setAttribute("prefix", nic.nat.strTFTPPrefix); + if (nic.nat.strTFTPBootFile.length()) + pelmTFTP->setAttribute("boot-file", nic.nat.strTFTPBootFile); + if (nic.nat.strTFTPNextServer.length()) + pelmTFTP->setAttribute("next-server", nic.nat.strTFTPNextServer); + } + buildNATForwardRulesMap(*pelmNAT, nic.nat.mapRules); + } + } + break; + + case NetworkAttachmentType_Bridged: + // For the currently active network attachment type we have to + // generate the tag, otherwise the attachment type is lost. + if (fEnabled || !nic.strBridgedName.isEmpty()) + { + xml::ElementNode *pelmMode = elmParent.createChild("BridgedInterface"); + if (!nic.strBridgedName.isEmpty()) + pelmMode->setAttribute("name", nic.strBridgedName); + } + break; + + case NetworkAttachmentType_Internal: + // For the currently active network attachment type we have to + // generate the tag, otherwise the attachment type is lost. + if (fEnabled || !nic.strInternalNetworkName.isEmpty()) + { + xml::ElementNode *pelmMode = elmParent.createChild("InternalNetwork"); + if (!nic.strInternalNetworkName.isEmpty()) + pelmMode->setAttribute("name", nic.strInternalNetworkName); + } + break; + + case NetworkAttachmentType_HostOnly: + // For the currently active network attachment type we have to + // generate the tag, otherwise the attachment type is lost. + if (fEnabled || !nic.strHostOnlyName.isEmpty()) + { + xml::ElementNode *pelmMode = elmParent.createChild("HostOnlyInterface"); + if (!nic.strHostOnlyName.isEmpty()) + pelmMode->setAttribute("name", nic.strHostOnlyName); + } + break; + +#ifdef VBOX_WITH_VMNET + case NetworkAttachmentType_HostOnlyNetwork: + // For the currently active network attachment type we have to + // generate the tag, otherwise the attachment type is lost. + if (fEnabled || !nic.strHostOnlyNetworkName.isEmpty()) + { + xml::ElementNode *pelmMode = elmParent.createChild("HostOnlyNetwork"); + if (!nic.strHostOnlyNetworkName.isEmpty()) + pelmMode->setAttribute("name", nic.strHostOnlyNetworkName); + } + break; +#endif /* VBOX_WITH_VMNET */ + + case NetworkAttachmentType_Generic: + // For the currently active network attachment type we have to + // generate the tag, otherwise the attachment type is lost. + if (fEnabled || !nic.areGenericDriverDefaultSettings()) + { + xml::ElementNode *pelmMode = elmParent.createChild("GenericInterface"); + if (!nic.areGenericDriverDefaultSettings()) + { + pelmMode->setAttribute("driver", nic.strGenericDriver); + for (StringsMap::const_iterator it = nic.genericProperties.begin(); + it != nic.genericProperties.end(); + ++it) + { + xml::ElementNode *pelmProp = pelmMode->createChild("Property"); + pelmProp->setAttribute("name", it->first); + pelmProp->setAttribute("value", it->second); + } + } + } + break; + + case NetworkAttachmentType_NATNetwork: + // For the currently active network attachment type we have to + // generate the tag, otherwise the attachment type is lost. + if (fEnabled || !nic.strNATNetworkName.isEmpty()) + { + xml::ElementNode *pelmMode = elmParent.createChild("NATNetwork"); + if (!nic.strNATNetworkName.isEmpty()) + pelmMode->setAttribute("name", nic.strNATNetworkName); + } + break; + +#ifdef VBOX_WITH_CLOUD_NET + case NetworkAttachmentType_Cloud: + // For the currently active network attachment type we have to + // generate the tag, otherwise the attachment type is lost. + if (fEnabled || !nic.strCloudNetworkName.isEmpty()) + { + xml::ElementNode *pelmMode = elmParent.createChild("CloudNetwork"); + if (!nic.strCloudNetworkName.isEmpty()) + pelmMode->setAttribute("name", nic.strCloudNetworkName); + } + break; +#endif /* VBOX_WITH_CLOUD_NET */ + + default: /*case NetworkAttachmentType_Null:*/ + break; + } +} + +/** + * Creates a \<StorageControllers\> node under elmParent and then writes out the XML + * keys under that. Called for both the \<Machine\> node and for snapshots. + * @param elmParent + * @param st + * @param fSkipRemovableMedia If true, DVD and floppy attachments are skipped and + * an empty drive is always written instead. This is for the OVF export case. + * This parameter is ignored unless the settings version is at least v1.9, which + * is always the case when this gets called for OVF export. + * @param pllElementsWithUuidAttributes If not NULL, must point to a list of element node + * pointers to which we will append all elements that we created here that contain + * UUID attributes. This allows the OVF export code to quickly replace the internal + * media UUIDs with the UUIDs of the media that were exported. + */ +void MachineConfigFile::buildStorageControllersXML(xml::ElementNode &elmParent, + const Storage &st, + bool fSkipRemovableMedia, + std::list<xml::ElementNode*> *pllElementsWithUuidAttributes) +{ + if (!st.llStorageControllers.size()) + return; + xml::ElementNode *pelmStorageControllers = elmParent.createChild("StorageControllers"); + + for (StorageControllersList::const_iterator it = st.llStorageControllers.begin(); + it != st.llStorageControllers.end(); + ++it) + { + const StorageController &sc = *it; + + if ( (m->sv < SettingsVersion_v1_9) + && (sc.controllerType == StorageControllerType_I82078) + ) + // floppy controller already got written into <Hardware>/<FloppyController> in buildHardwareXML() + // for pre-1.9 settings + continue; + + xml::ElementNode *pelmController = pelmStorageControllers->createChild("StorageController"); + com::Utf8Str name = sc.strName; + if (m->sv < SettingsVersion_v1_8) + { + // pre-1.8 settings use shorter controller names, they are + // expanded when reading the settings + if (name == "IDE Controller") + name = "IDE"; + else if (name == "SATA Controller") + name = "SATA"; + else if (name == "SCSI Controller") + name = "SCSI"; + } + pelmController->setAttribute("name", sc.strName); + + const char *pcszType; + switch (sc.controllerType) + { + case StorageControllerType_IntelAhci: pcszType = "AHCI"; break; + case StorageControllerType_LsiLogic: pcszType = "LsiLogic"; break; + case StorageControllerType_BusLogic: pcszType = "BusLogic"; break; + case StorageControllerType_PIIX4: pcszType = "PIIX4"; break; + case StorageControllerType_ICH6: pcszType = "ICH6"; break; + case StorageControllerType_I82078: pcszType = "I82078"; break; + case StorageControllerType_LsiLogicSas: pcszType = "LsiLogicSas"; break; + case StorageControllerType_USB: pcszType = "USB"; break; + case StorageControllerType_NVMe: pcszType = "NVMe"; break; + case StorageControllerType_VirtioSCSI: pcszType = "VirtioSCSI"; break; + default: /*case StorageControllerType_PIIX3:*/ pcszType = "PIIX3"; break; + } + pelmController->setAttribute("type", pcszType); + + pelmController->setAttribute("PortCount", sc.ulPortCount); + + if (m->sv >= SettingsVersion_v1_9) + if (sc.ulInstance) + pelmController->setAttribute("Instance", sc.ulInstance); + + if (m->sv >= SettingsVersion_v1_10) + pelmController->setAttribute("useHostIOCache", sc.fUseHostIOCache); + + if (m->sv >= SettingsVersion_v1_11) + pelmController->setAttribute("Bootable", sc.fBootable); + + if (sc.controllerType == StorageControllerType_IntelAhci) + { + pelmController->setAttribute("IDE0MasterEmulationPort", 0); + pelmController->setAttribute("IDE0SlaveEmulationPort", 1); + pelmController->setAttribute("IDE1MasterEmulationPort", 2); + pelmController->setAttribute("IDE1SlaveEmulationPort", 3); + } + + for (AttachedDevicesList::const_iterator it2 = sc.llAttachedDevices.begin(); + it2 != sc.llAttachedDevices.end(); + ++it2) + { + const AttachedDevice &att = *it2; + + // For settings version before 1.9, DVDs and floppies are in hardware, not storage controllers, + // so we shouldn't write them here; we only get here for DVDs though because we ruled out + // the floppy controller at the top of the loop + if ( att.deviceType == DeviceType_DVD + && m->sv < SettingsVersion_v1_9 + ) + continue; + + xml::ElementNode *pelmDevice = pelmController->createChild("AttachedDevice"); + + pcszType = NULL; + + switch (att.deviceType) + { + case DeviceType_HardDisk: + pcszType = "HardDisk"; + if (att.fNonRotational) + pelmDevice->setAttribute("nonrotational", att.fNonRotational); + if (att.fDiscard) + pelmDevice->setAttribute("discard", att.fDiscard); + break; + + case DeviceType_DVD: + pcszType = "DVD"; + pelmDevice->setAttribute("passthrough", att.fPassThrough); + if (att.fTempEject) + pelmDevice->setAttribute("tempeject", att.fTempEject); + break; + + case DeviceType_Floppy: + pcszType = "Floppy"; + break; + + default: break; /* Shut up MSC. */ + } + + pelmDevice->setAttribute("type", pcszType); + + if (m->sv >= SettingsVersion_v1_15) + pelmDevice->setAttribute("hotpluggable", att.fHotPluggable); + + pelmDevice->setAttribute("port", att.lPort); + pelmDevice->setAttribute("device", att.lDevice); + + if (att.strBwGroup.length()) + pelmDevice->setAttribute("bandwidthGroup", att.strBwGroup); + + // attached image, if any + if (!att.uuid.isZero() + && att.uuid.isValid() + && (att.deviceType == DeviceType_HardDisk + || !fSkipRemovableMedia + ) + ) + { + xml::ElementNode *pelmImage = pelmDevice->createChild("Image"); + pelmImage->setAttribute("uuid", att.uuid.toStringCurly()); + + // if caller wants a list of UUID elements, give it to them + if (pllElementsWithUuidAttributes) + pllElementsWithUuidAttributes->push_back(pelmImage); + } + else if ( (m->sv >= SettingsVersion_v1_9) + && (att.strHostDriveSrc.length()) + ) + pelmDevice->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc); + } + } +} + +/** + * Creates a \<Debugging\> node under elmParent and then writes out the XML + * keys under that. Called for both the \<Machine\> node and for snapshots. + * + * @param elmParent Parent element. + * @param dbg Debugging settings. + */ +void MachineConfigFile::buildDebuggingXML(xml::ElementNode &elmParent, const Debugging &dbg) +{ + if (m->sv < SettingsVersion_v1_13 || dbg.areDefaultSettings()) + return; + + xml::ElementNode *pElmDebugging = elmParent.createChild("Debugging"); + xml::ElementNode *pElmTracing = pElmDebugging->createChild("Tracing"); + pElmTracing->setAttribute("enabled", dbg.fTracingEnabled); + pElmTracing->setAttribute("allowTracingToAccessVM", dbg.fAllowTracingToAccessVM); + pElmTracing->setAttribute("config", dbg.strTracingConfig); + + xml::ElementNode *pElmGuestDebug = pElmDebugging->createChild("GuestDebug"); + const char *pcszDebugProvider = NULL; + const char *pcszIoProvider = NULL; + + switch (dbg.enmDbgProvider) + { + case GuestDebugProvider_None: pcszDebugProvider = "None"; break; + case GuestDebugProvider_GDB: pcszDebugProvider = "GDB"; break; + case GuestDebugProvider_KD: pcszDebugProvider = "KD"; break; + default: AssertFailed(); pcszDebugProvider = "None"; break; + } + + switch (dbg.enmIoProvider) + { + case GuestDebugIoProvider_None: pcszIoProvider = "None"; break; + case GuestDebugIoProvider_TCP: pcszIoProvider = "TCP"; break; + case GuestDebugIoProvider_UDP: pcszIoProvider = "UDP"; break; + case GuestDebugIoProvider_IPC: pcszIoProvider = "IPC"; break; + default: AssertFailed(); pcszIoProvider = "None"; break; + } + + pElmGuestDebug->setAttribute("provider", pcszDebugProvider); + pElmGuestDebug->setAttribute("io", pcszIoProvider); + pElmGuestDebug->setAttribute("address", dbg.strAddress); + pElmGuestDebug->setAttribute("port", dbg.ulPort); +} + +/** + * Creates a \<Autostart\> node under elmParent and then writes out the XML + * keys under that. Called for both the \<Machine\> node and for snapshots. + * + * @param elmParent Parent element. + * @param autostrt Autostart settings. + */ +void MachineConfigFile::buildAutostartXML(xml::ElementNode &elmParent, const Autostart &autostrt) +{ + const char *pcszAutostop = NULL; + + if (m->sv < SettingsVersion_v1_13 || autostrt.areDefaultSettings()) + return; + + xml::ElementNode *pElmAutostart = elmParent.createChild("Autostart"); + pElmAutostart->setAttribute("enabled", autostrt.fAutostartEnabled); + pElmAutostart->setAttribute("delay", autostrt.uAutostartDelay); + + switch (autostrt.enmAutostopType) + { + case AutostopType_Disabled: pcszAutostop = "Disabled"; break; + case AutostopType_SaveState: pcszAutostop = "SaveState"; break; + case AutostopType_PowerOff: pcszAutostop = "PowerOff"; break; + case AutostopType_AcpiShutdown: pcszAutostop = "AcpiShutdown"; break; + default: Assert(false); pcszAutostop = "Disabled"; break; + } + pElmAutostart->setAttribute("autostop", pcszAutostop); +} + +void MachineConfigFile::buildRecordingXML(xml::ElementNode &elmParent, const RecordingSettings &recording) +{ + if (recording.areDefaultSettings()) /* Omit branch if we still have the default settings (i.e. nothing to save). */ + return; + + AssertReturnVoid(recording.mapScreens.size() <= 64); /* Make sure we never exceed the bitmap of 64 monitors. */ + + /* Note: Since settings 1.19 the recording settings have a dedicated XML branch outside of Hardware. */ + if (m->sv >= SettingsVersion_v1_19 /* VBox >= 7.0 */) + { + /* Note: elmParent is Machine or Snapshot. */ + xml::ElementNode *pelmRecording = elmParent.createChild("Recording"); + + if (!recordingSettings.common.areDefaultSettings()) + { + pelmRecording->setAttribute("enabled", recording.common.fEnabled); + } + + /* Only serialize screens which have non-default settings. */ + uint32_t cScreensToWrite = 0; + + RecordingScreenSettingsMap::const_iterator itScreen = recording.mapScreens.begin(); + while (itScreen != recording.mapScreens.end()) + { + if (!itScreen->second.areDefaultSettings()) + cScreensToWrite++; + ++itScreen; + } + + if (cScreensToWrite) + pelmRecording->setAttribute("screens", cScreensToWrite); + + itScreen = recording.mapScreens.begin(); + while (itScreen != recording.mapScreens.end()) + { + if (!itScreen->second.areDefaultSettings()) /* Skip serializing screen settings which have default settings. */ + { + xml::ElementNode *pelmScreen = pelmRecording->createChild("Screen"); + + pelmScreen->setAttribute("id", itScreen->first); /* The key equals the monitor ID. */ + pelmScreen->setAttribute("enabled", itScreen->second.fEnabled); + com::Utf8Str strTemp; + RecordingScreenSettings::featuresToString(itScreen->second.featureMap, strTemp); + pelmScreen->setAttribute("featuresEnabled", strTemp); + if (itScreen->second.ulMaxTimeS) + pelmScreen->setAttribute("maxTimeS", itScreen->second.ulMaxTimeS); + if (itScreen->second.strOptions.isNotEmpty()) + pelmScreen->setAttributePath("options", itScreen->second.strOptions); + pelmScreen->setAttribute("dest", itScreen->second.enmDest); + if (!itScreen->second.File.strName.isEmpty()) + pelmScreen->setAttributePath("file", itScreen->second.File.strName); + if (itScreen->second.File.ulMaxSizeMB) + pelmScreen->setAttribute("maxSizeMB", itScreen->second.File.ulMaxSizeMB); + + RecordingScreenSettings::videoCodecToString(itScreen->second.Video.enmCodec, strTemp); + pelmScreen->setAttribute("videoCodec", strTemp); + if (itScreen->second.Video.enmDeadline != RecordingCodecDeadline_Default) + pelmScreen->setAttribute("videoDeadline", itScreen->second.Video.enmDeadline); + if (itScreen->second.Video.enmRateCtlMode != RecordingRateControlMode_VBR) /* Is default. */ + pelmScreen->setAttribute("videoRateCtlMode", itScreen->second.Video.enmRateCtlMode); + if (itScreen->second.Video.enmScalingMode != RecordingVideoScalingMode_None) + pelmScreen->setAttribute("videoScalingMode",itScreen->second.Video.enmScalingMode); + if ( itScreen->second.Video.ulWidth != 1024 + || itScreen->second.Video.ulHeight != 768) + { + pelmScreen->setAttribute("horzRes", itScreen->second.Video.ulWidth); + pelmScreen->setAttribute("vertRes", itScreen->second.Video.ulHeight); + } + if (itScreen->second.Video.ulRate != 512) + pelmScreen->setAttribute("rateKbps", itScreen->second.Video.ulRate); + if (itScreen->second.Video.ulFPS) + pelmScreen->setAttribute("fps", itScreen->second.Video.ulFPS); + + RecordingScreenSettings::audioCodecToString(itScreen->second.Audio.enmCodec, strTemp); + pelmScreen->setAttribute("audioCodec", strTemp); + if (itScreen->second.Audio.enmDeadline != RecordingCodecDeadline_Default) + pelmScreen->setAttribute("audioDeadline", itScreen->second.Audio.enmDeadline); + if (itScreen->second.Audio.enmRateCtlMode != RecordingRateControlMode_VBR) /* Is default. */ + pelmScreen->setAttribute("audioRateCtlMode", itScreen->second.Audio.enmRateCtlMode); + if (itScreen->second.Audio.uHz != 22050) + pelmScreen->setAttribute("audioHz", itScreen->second.Audio.uHz); + if (itScreen->second.Audio.cBits != 16) + pelmScreen->setAttribute("audioBits", itScreen->second.Audio.cBits); + if (itScreen->second.Audio.cChannels != 2) + pelmScreen->setAttribute("audioChannels", itScreen->second.Audio.cChannels); + } + ++itScreen; + } + } + else if ( m->sv >= SettingsVersion_v1_14 + && m->sv < SettingsVersion_v1_19 /* VBox < 7.0 */) + { + /* Note: elmParent is Hardware or Snapshot. */ + xml::ElementNode *pelmVideoCapture = elmParent.createChild("VideoCapture"); + + if (!recordingSettings.common.areDefaultSettings()) + { + pelmVideoCapture->setAttribute("enabled", recording.common.fEnabled); + } + + /* Convert the enabled screens to the former uint64_t bit array and vice versa. */ + uint64_t uScreensBitmap = 0; + RecordingScreenSettingsMap::const_iterator itScreen = recording.mapScreens.begin(); + while (itScreen != recording.mapScreens.end()) + { + if (itScreen->second.fEnabled) + uScreensBitmap |= RT_BIT_64(itScreen->first); + ++itScreen; + } + + if (uScreensBitmap) + pelmVideoCapture->setAttribute("screens", uScreensBitmap); + + Assert(recording.mapScreens.size()); + const RecordingScreenSettingsMap::const_iterator itScreen0Settings = recording.mapScreens.find(0); + Assert(itScreen0Settings != recording.mapScreens.end()); + + if (itScreen0Settings->second.ulMaxTimeS) + pelmVideoCapture->setAttribute("maxTime", itScreen0Settings->second.ulMaxTimeS); + if (itScreen0Settings->second.strOptions.isNotEmpty()) + pelmVideoCapture->setAttributePath("options", itScreen0Settings->second.strOptions); + + if (!itScreen0Settings->second.File.strName.isEmpty()) + pelmVideoCapture->setAttributePath("file", itScreen0Settings->second.File.strName); + if (itScreen0Settings->second.File.ulMaxSizeMB) + pelmVideoCapture->setAttribute("maxSize", itScreen0Settings->second.File.ulMaxSizeMB); + + if ( itScreen0Settings->second.Video.ulWidth != 1024 + || itScreen0Settings->second.Video.ulHeight != 768) + { + pelmVideoCapture->setAttribute("horzRes", itScreen0Settings->second.Video.ulWidth); + pelmVideoCapture->setAttribute("vertRes", itScreen0Settings->second.Video.ulHeight); + } + if (itScreen0Settings->second.Video.ulRate != 512) + pelmVideoCapture->setAttribute("rate", itScreen0Settings->second.Video.ulRate); + if (itScreen0Settings->second.Video.ulFPS) + pelmVideoCapture->setAttribute("fps", itScreen0Settings->second.Video.ulFPS); + } +} + +/** + * Creates a \<Groups\> node under elmParent and then writes out the XML + * keys under that. Called for the \<Machine\> node only. + * + * @param elmParent Parent element. + * @param llGroups Groups list. + */ +void MachineConfigFile::buildGroupsXML(xml::ElementNode &elmParent, const StringsList &llGroups) +{ + if ( m->sv < SettingsVersion_v1_13 || llGroups.size() == 0 + || (llGroups.size() == 1 && llGroups.front() == "/")) + return; + + xml::ElementNode *pElmGroups = elmParent.createChild("Groups"); + for (StringsList::const_iterator it = llGroups.begin(); + it != llGroups.end(); + ++it) + { + const Utf8Str &group = *it; + xml::ElementNode *pElmGroup = pElmGroups->createChild("Group"); + pElmGroup->setAttribute("name", group); + } +} + +/** + * Writes a single snapshot into the DOM tree. Initially this gets called from + * MachineConfigFile::write() for the root snapshot of a machine, if present; + * elmParent then points to the \<Snapshots\> node under the \<Machine\> node + * to which \<Snapshot\> must be added. This may then continue processing the + * child snapshots. + * + * @param elmParent + * @param snap + */ +void MachineConfigFile::buildSnapshotXML(xml::ElementNode &elmParent, + const Snapshot &snap) +{ + std::list<const Snapshot *> llSettingsTodo; + llSettingsTodo.push_back(&snap); + std::list<xml::ElementNode *> llElementsTodo; + llElementsTodo.push_back(&elmParent); + std::list<uint32_t> llDepthsTodo; + llDepthsTodo.push_back(1); + + while (llSettingsTodo.size() > 0) + { + const Snapshot *pSnap = llSettingsTodo.front(); + llSettingsTodo.pop_front(); + xml::ElementNode *pElement = llElementsTodo.front(); + llElementsTodo.pop_front(); + uint32_t depth = llDepthsTodo.front(); + llDepthsTodo.pop_front(); + + if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX) + throw ConfigFileError(this, NULL, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX); + + xml::ElementNode *pelmSnapshot = pElement->createChild("Snapshot"); + + pelmSnapshot->setAttribute("uuid", pSnap->uuid.toStringCurly()); + pelmSnapshot->setAttribute("name", pSnap->strName); + pelmSnapshot->setAttribute("timeStamp", stringifyTimestamp(pSnap->timestamp)); + + if (pSnap->strStateFile.length()) + pelmSnapshot->setAttributePath("stateFile", pSnap->strStateFile); + + if (pSnap->strDescription.length()) + pelmSnapshot->createChild("Description")->addContent(pSnap->strDescription); + + // We only skip removable media for OVF, but OVF never includes snapshots. + buildHardwareXML(*pelmSnapshot, pSnap->hardware, 0 /* fl */, NULL /* pllElementsWithUuidAttributes */); + buildDebuggingXML(*pelmSnapshot, pSnap->debugging); + buildAutostartXML(*pelmSnapshot, pSnap->autostart); + buildRecordingXML(*pelmSnapshot, pSnap->recordingSettings); + // note: Groups exist only for Machine, not for Snapshot + + if (pSnap->llChildSnapshots.size()) + { + xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots"); + for (SnapshotsList::const_iterator it = pSnap->llChildSnapshots.begin(); + it != pSnap->llChildSnapshots.end(); + ++it) + { + llSettingsTodo.push_back(&*it); + llElementsTodo.push_back(pelmChildren); + llDepthsTodo.push_back(depth + 1); + } + } + } +} + +/** + * Builds the XML DOM tree for the machine config under the given XML element. + * + * This has been separated out from write() so it can be called from elsewhere, + * such as the OVF code, to build machine XML in an existing XML tree. + * + * As a result, this gets called from two locations: + * + * -- MachineConfigFile::write(); + * + * -- Appliance::buildXMLForOneVirtualSystem() + * + * In fl, the following flag bits are recognized: + * + * -- BuildMachineXML_MediaRegistry: If set, the machine's media registry will + * be written, if present. This is not set when called from OVF because OVF + * has its own variant of a media registry. This flag is ignored unless the + * settings version is at least v1.11 (VirtualBox 4.0). + * + * -- BuildMachineXML_IncludeSnapshots: If set, descend into the snapshots tree + * of the machine and write out \<Snapshot\> and possibly more snapshots under + * that, if snapshots are present. Otherwise all snapshots are suppressed + * (when called from OVF). + * + * -- BuildMachineXML_WriteVBoxVersionAttribute: If set, add a settingsVersion + * attribute to the machine tag with the vbox settings version. This is for + * the OVF export case in which we don't have the settings version set in + * the root element. + * + * -- BuildMachineXML_SkipRemovableMedia: If set, removable media attachments + * (DVDs, floppies) are silently skipped. This is for the OVF export case + * until we support copying ISO and RAW media as well. This flag is ignored + * unless the settings version is at least v1.9, which is always the case + * when this gets called for OVF export. + * + * -- BuildMachineXML_SuppressSavedState: If set, the Machine/stateFile + * attribute is never set. This is also for the OVF export case because we + * cannot save states with OVF. + * + * @param elmMachine XML \<Machine\> element to add attributes and elements to. + * @param fl Flags. + * @param pllElementsWithUuidAttributes pointer to list that should receive UUID elements or NULL; + * see buildStorageControllersXML() for details. + */ +void MachineConfigFile::buildMachineXML(xml::ElementNode &elmMachine, + uint32_t fl, + std::list<xml::ElementNode*> *pllElementsWithUuidAttributes) +{ + if (fl & BuildMachineXML_WriteVBoxVersionAttribute) + { + // add settings version attribute to machine element + setVersionAttribute(elmMachine); + LogRel(("Exporting settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str())); + } + + elmMachine.setAttribute("uuid", uuid.toStringCurly()); + elmMachine.setAttribute("name", machineUserData.strName); + if (machineUserData.fDirectoryIncludesUUID) + elmMachine.setAttribute("directoryIncludesUUID", machineUserData.fDirectoryIncludesUUID); + if (!machineUserData.fNameSync) + elmMachine.setAttribute("nameSync", machineUserData.fNameSync); + if (machineUserData.strDescription.length()) + elmMachine.createChild("Description")->addContent(machineUserData.strDescription); + elmMachine.setAttribute("OSType", machineUserData.strOsType); + + + if (m->sv >= SettingsVersion_v1_19) + { + if (strStateKeyId.length()) + elmMachine.setAttribute("stateKeyId", strStateKeyId); + if (strStateKeyStore.length()) + elmMachine.setAttribute("stateKeyStore", strStateKeyStore); + if (strLogKeyId.length()) + elmMachine.setAttribute("logKeyId", strLogKeyId); + if (strLogKeyStore.length()) + elmMachine.setAttribute("logKeyStore", strLogKeyStore); + } + if ( strStateFile.length() + && !(fl & BuildMachineXML_SuppressSavedState) + ) + elmMachine.setAttributePath("stateFile", strStateFile); + + if ((fl & BuildMachineXML_IncludeSnapshots) + && !uuidCurrentSnapshot.isZero() + && uuidCurrentSnapshot.isValid()) + elmMachine.setAttribute("currentSnapshot", uuidCurrentSnapshot.toStringCurly()); + + if (machineUserData.strSnapshotFolder.length()) + elmMachine.setAttributePath("snapshotFolder", machineUserData.strSnapshotFolder); + if (!fCurrentStateModified) + elmMachine.setAttribute("currentStateModified", fCurrentStateModified); + elmMachine.setAttribute("lastStateChange", stringifyTimestamp(timeLastStateChange)); + if (fAborted) + elmMachine.setAttribute("aborted", fAborted); + + switch (machineUserData.enmVMPriority) + { + case VMProcPriority_Flat: + elmMachine.setAttribute("processPriority", "Flat"); + break; + case VMProcPriority_Low: + elmMachine.setAttribute("processPriority", "Low"); + break; + case VMProcPriority_Normal: + elmMachine.setAttribute("processPriority", "Normal"); + break; + case VMProcPriority_High: + elmMachine.setAttribute("processPriority", "High"); + break; + default: + break; + } + // Please keep the icon last so that one doesn't have to check if there + // is anything in the line after this very long attribute in the XML. + if (machineUserData.ovIcon.size()) + { + Utf8Str strIcon; + toBase64(strIcon, machineUserData.ovIcon); + elmMachine.setAttribute("icon", strIcon); + } + if ( m->sv >= SettingsVersion_v1_9 + && ( machineUserData.fTeleporterEnabled + || machineUserData.uTeleporterPort + || !machineUserData.strTeleporterAddress.isEmpty() + || !machineUserData.strTeleporterPassword.isEmpty() + ) + ) + { + xml::ElementNode *pelmTeleporter = elmMachine.createChild("Teleporter"); + pelmTeleporter->setAttribute("enabled", machineUserData.fTeleporterEnabled); + pelmTeleporter->setAttribute("port", machineUserData.uTeleporterPort); + pelmTeleporter->setAttribute("address", machineUserData.strTeleporterAddress); + pelmTeleporter->setAttribute("password", machineUserData.strTeleporterPassword); + } + + if ( (fl & BuildMachineXML_MediaRegistry) + && (m->sv >= SettingsVersion_v1_11) + ) + buildMediaRegistry(elmMachine, mediaRegistry); + + buildExtraData(elmMachine, mapExtraDataItems); + + if ( (fl & BuildMachineXML_IncludeSnapshots) + && llFirstSnapshot.size()) + buildSnapshotXML(elmMachine, llFirstSnapshot.front()); + + buildHardwareXML(elmMachine, hardwareMachine, fl, pllElementsWithUuidAttributes); + buildDebuggingXML(elmMachine, debugging); + buildAutostartXML(elmMachine, autostart); + + /* Note: Must come *after* buildHardwareXML(), as the "Hardware" branch is needed. */ + if ( m->sv >= SettingsVersion_v1_14 + && m->sv < SettingsVersion_v1_19) /* < VBox 7.0. */ + { + xml::ElementNode *pelHardware = unconst(elmMachine.findChildElement("Hardware")); + if (pelHardware) + buildRecordingXML(*pelHardware, recordingSettings); + } + else if (m->sv >= SettingsVersion_v1_19) /* Now lives outside of "Hardware", in "Machine". */ + buildRecordingXML(elmMachine, recordingSettings); + + buildGroupsXML(elmMachine, machineUserData.llGroups); +} + + /** + * Builds encrypted config. + * + * @sa MachineConfigFile::buildMachineXML + */ +void MachineConfigFile::buildMachineEncryptedXML(xml::ElementNode &elmMachine, + uint32_t fl, + std::list<xml::ElementNode*> *pllElementsWithUuidAttributes, + PCVBOXCRYPTOIF pCryptoIf, + const char *pszPassword = NULL) +{ + if ( !pszPassword + || !pCryptoIf) + throw ConfigFileError(this, &elmMachine, N_("Password is required")); + + xml::Document *pDoc = new xml::Document; + xml::ElementNode *pelmRoot = pDoc->createRootElement("Machine"); + pelmRoot->setAttribute("xmlns", VBOX_XML_NAMESPACE); + // Have the code for producing a proper schema reference. Not used by most + // tools, so don't bother doing it. The schema is not on the server anyway. +#ifdef VBOX_WITH_SETTINGS_SCHEMA + pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); + pelmRoot->setAttribute("xsi:schemaLocation", VBOX_XML_NAMESPACE " " VBOX_XML_SCHEMA); +#endif + + buildMachineXML(*pelmRoot, fl, pllElementsWithUuidAttributes); + xml::XmlStringWriter writer; + com::Utf8Str strMachineXml; + int rc = writer.write(*pDoc, &strMachineXml); + delete pDoc; + if (RT_SUCCESS(rc)) + { + VBOXCRYPTOCTX hCryptoCtx; + if (strKeyStore.isEmpty()) + { + rc = pCryptoIf->pfnCryptoCtxCreate("AES-GCM256", pszPassword, &hCryptoCtx); + if (RT_SUCCESS(rc)) + { + char *pszNewKeyStore; + rc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszNewKeyStore); + if (RT_SUCCESS(rc)) + { + strKeyStore = pszNewKeyStore; + RTStrFree(pszNewKeyStore); + } + else + pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx); + } + } + else + rc = pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), pszPassword, &hCryptoCtx); + if (RT_SUCCESS(rc)) + { + IconBlob abEncrypted; + size_t cbEncrypted = 0; + rc = pCryptoIf->pfnCryptoCtxQueryEncryptedSize(hCryptoCtx, strMachineXml.length(), &cbEncrypted); + if (RT_SUCCESS(rc)) + { + abEncrypted.resize(cbEncrypted); + rc = pCryptoIf->pfnCryptoCtxEncrypt(hCryptoCtx, false /*fPartial*/, NULL /*pvIV*/, 0 /*cbIV*/, + strMachineXml.c_str(), strMachineXml.length(), + uuid.raw(), sizeof(RTUUID), + &abEncrypted[0], abEncrypted.size(), &cbEncrypted); + int rc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx); + AssertRC(rc2); + if (RT_SUCCESS(rc)) + { + abEncrypted.resize(cbEncrypted); + toBase64(strMachineXml, abEncrypted); + elmMachine.setAttribute("uuid", uuid.toStringCurly()); + elmMachine.setAttribute("keyId", strKeyId); + elmMachine.setAttribute("keyStore", strKeyStore); + elmMachine.setContent(strMachineXml.c_str()); + } + } + } + + if (RT_FAILURE(rc)) + throw ConfigFileError(this, &elmMachine, N_("Creating machine encrypted xml failed. (%Rrc)"), rc); + } + else + throw ConfigFileError(this, &elmMachine, N_("Creating machine xml failed. (%Rrc)"), rc); +} + +/** + * Returns true only if the given AudioDriverType is supported on + * the current host platform. For example, this would return false + * for AudioDriverType_DirectSound when compiled on a Linux host. + * +* @return @c true if the current host supports the driver, @c false if not. + * @param enmDrvType AudioDriverType_* enum to test. + */ +/*static*/ +bool MachineConfigFile::isAudioDriverAllowedOnThisHost(AudioDriverType_T enmDrvType) +{ + switch (enmDrvType) + { + case AudioDriverType_Default: + RT_FALL_THROUGH(); + case AudioDriverType_Null: + return true; /* Default and Null audio are always allowed. */ +#ifdef RT_OS_WINDOWS + case AudioDriverType_WAS: + /* We only support WAS on systems we tested so far (Vista+). */ + if (RTSystemGetNtVersion() < RTSYSTEM_MAKE_NT_VERSION(6,1,0)) + break; + RT_FALL_THROUGH(); + case AudioDriverType_DirectSound: +#endif +#ifdef VBOX_WITH_AUDIO_OSS + case AudioDriverType_OSS: +#endif +#ifdef VBOX_WITH_AUDIO_ALSA + case AudioDriverType_ALSA: +#endif +#ifdef VBOX_WITH_AUDIO_PULSE + case AudioDriverType_Pulse: +#endif +#ifdef RT_OS_DARWIN + case AudioDriverType_CoreAudio: +#endif +#ifdef RT_OS_OS2 + case AudioDriverType_MMPM: +#endif + return true; + default: break; /* Shut up MSC. */ + } + + return false; +} + +/** + * Returns the AudioDriverType_* which should be used by default on this + * host platform. On Linux, this will check at runtime whether PulseAudio + * or ALSA are actually supported on the first call. + * + * When more than one supported audio stack is available, choose the most suited + * (probably newest in most cases) one. + * + * @return Default audio driver type for this host platform. + */ +/*static*/ +AudioDriverType_T MachineConfigFile::getHostDefaultAudioDriver() +{ +#if defined(RT_OS_WINDOWS) + if (RTSystemGetNtVersion() >= RTSYSTEM_MAKE_NT_VERSION(6,1,0)) + return AudioDriverType_WAS; + return AudioDriverType_DirectSound; + +#elif defined(RT_OS_LINUX) + /* On Linux, we need to check at runtime what's actually supported. */ + static RTCLockMtx s_mtx; + static AudioDriverType_T s_enmLinuxDriver = AudioDriverType_Null; + RTCLock lock(s_mtx); + if (s_enmLinuxDriver == AudioDriverType_Null) + { +# ifdef VBOX_WITH_AUDIO_PULSE + /* Check for the pulse library & that the pulse audio daemon is running. */ + if (RTProcIsRunningByName("pulseaudio") && + RTLdrIsLoadable("libpulse.so.0")) + s_enmLinuxDriver = AudioDriverType_Pulse; + else +# endif /* VBOX_WITH_AUDIO_PULSE */ +# ifdef VBOX_WITH_AUDIO_ALSA + /* Check if we can load the ALSA library */ + if (RTLdrIsLoadable("libasound.so.2")) + s_enmLinuxDriver = AudioDriverType_ALSA; +# endif /* VBOX_WITH_AUDIO_ALSA */ +# ifdef VBOX_WITH_AUDIO_OSS + else + s_enmLinuxDriver = AudioDriverType_OSS; +# endif /* VBOX_WITH_AUDIO_OSS */ + } + return s_enmLinuxDriver; + +#elif defined(RT_OS_DARWIN) + return AudioDriverType_CoreAudio; + +#elif defined(RT_OS_OS2) + return AudioDriverType_MMPM; + +#else /* All other platforms. */ +# ifdef VBOX_WITH_AUDIO_OSS + return AudioDriverType_OSS; +# else + /* Return NULL driver as a fallback if nothing of the above is available. */ + return AudioDriverType_Null; +# endif +#endif +} + +/** + * Called from write() before calling ConfigFileBase::createStubDocument(). + * This adjusts the settings version in m->sv if incompatible settings require + * a settings bump, whereas otherwise we try to preserve the settings version + * to avoid breaking compatibility with older versions. + * + * We do the checks in here in reverse order: newest first, oldest last, so + * that we avoid unnecessary checks since some of these are expensive. + */ +void MachineConfigFile::bumpSettingsVersionIfNeeded() +{ + if (m->sv < SettingsVersion_v1_19) + { + // VirtualBox 7.0 adds iommu device and full VM encryption. + if ( hardwareMachine.iommuType != IommuType_None + || strKeyId.isNotEmpty() + || strKeyStore.isNotEmpty() + || strStateKeyId.isNotEmpty() + || strStateKeyStore.isNotEmpty() + || hardwareMachine.nvramSettings.strKeyId.isNotEmpty() + || hardwareMachine.nvramSettings.strKeyStore.isNotEmpty() + /* Default for newly created VMs in VBox 7.0. + * Older VMs might have a specific audio driver set (also for VMs created with < VBox 7.0). */ + || hardwareMachine.audioAdapter.driverType == AudioDriverType_Default + || recordingSettings.areDefaultSettings() == false + || strLogKeyId.isNotEmpty() + || strLogKeyStore.isEmpty()) + { + m->sv = SettingsVersion_v1_19; + return; + } + + // VirtualBox 7.0 adds a Trusted Platform Module. + if ( hardwareMachine.tpmSettings.tpmType != TpmType_None + || hardwareMachine.tpmSettings.strLocation.isNotEmpty()) + { + m->sv = SettingsVersion_v1_19; + return; + } + + NetworkAdaptersList::const_iterator netit; + for (netit = hardwareMachine.llNetworkAdapters.begin(); + netit != hardwareMachine.llNetworkAdapters.end(); + ++netit) + { + // VirtualBox 7.0 adds a flag if NAT can reach localhost. + if ( netit->fEnabled + && netit->mode == NetworkAttachmentType_NAT + && !netit->nat.fLocalhostReachable) + { + m->sv = SettingsVersion_v1_19; + break; + } + +#ifdef VBOX_WITH_VMNET + // VirtualBox 7.0 adds a host-only network attachment. + if (netit->mode == NetworkAttachmentType_HostOnlyNetwork) + { + m->sv = SettingsVersion_v1_19; + break; + } +#endif /* VBOX_WITH_VMNET */ + } + + // VirtualBox 7.0 adds guest debug settings. + if ( debugging.enmDbgProvider != GuestDebugProvider_None + || debugging.enmIoProvider != GuestDebugIoProvider_None + || debugging.strAddress.isNotEmpty() + || debugging.ulPort != 0) + { + m->sv = SettingsVersion_v1_19; + return; + } + } + + if (m->sv < SettingsVersion_v1_18) + { + if (!hardwareMachine.nvramSettings.strNvramPath.isEmpty()) + { + m->sv = SettingsVersion_v1_18; + return; + } + + // VirtualBox 6.1 adds AMD-V virtualized VMSAVE/VMLOAD setting. + if (hardwareMachine.fVirtVmsaveVmload == false) + { + m->sv = SettingsVersion_v1_18; + return; + } + + // VirtualBox 6.1 adds a virtio-scsi storage controller. + for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin(); + it != hardwareMachine.storage.llStorageControllers.end(); + ++it) + { + const StorageController &sctl = *it; + + if (sctl.controllerType == StorageControllerType_VirtioSCSI) + { + m->sv = SettingsVersion_v1_18; + return; + } + } + +#ifdef VBOX_WITH_CLOUD_NET + NetworkAdaptersList::const_iterator netit; + for (netit = hardwareMachine.llNetworkAdapters.begin(); + netit != hardwareMachine.llNetworkAdapters.end(); + ++netit) + { + // VirtualBox 6.1 adds support for cloud networks. + if ( netit->fEnabled + && netit->mode == NetworkAttachmentType_Cloud) + { + m->sv = SettingsVersion_v1_18; + break; + } + + } +#endif /* VBOX_WITH_CLOUD_NET */ + } + + if (m->sv < SettingsVersion_v1_17) + { + if (machineUserData.enmVMPriority != VMProcPriority_Default) + { + m->sv = SettingsVersion_v1_17; + return; + } + + // VirtualBox 6.0 adds nested hardware virtualization, using native API (NEM). + if ( hardwareMachine.fNestedHWVirt + || hardwareMachine.fUseNativeApi) + { + m->sv = SettingsVersion_v1_17; + return; + } + if (hardwareMachine.llSharedFolders.size()) + for (SharedFoldersList::const_iterator it = hardwareMachine.llSharedFolders.begin(); + it != hardwareMachine.llSharedFolders.end(); + ++it) + if (it->strAutoMountPoint.isNotEmpty()) + { + m->sv = SettingsVersion_v1_17; + return; + } + + /* + * Check if any serial port uses a non 16550A serial port. + */ + for (SerialPortsList::const_iterator it = hardwareMachine.llSerialPorts.begin(); + it != hardwareMachine.llSerialPorts.end(); + ++it) + { + const SerialPort &port = *it; + if (port.uartType != UartType_U16550A) + { + m->sv = SettingsVersion_v1_17; + return; + } + } + } + + if (m->sv < SettingsVersion_v1_16) + { + // VirtualBox 5.1 adds a NVMe storage controller, paravirt debug + // options, cpu profile, APIC settings (CPU capability and BIOS). + + if ( hardwareMachine.strParavirtDebug.isNotEmpty() + || (!hardwareMachine.strCpuProfile.equals("host") && hardwareMachine.strCpuProfile.isNotEmpty()) + || hardwareMachine.biosSettings.apicMode != APICMode_APIC + || !hardwareMachine.fAPIC + || hardwareMachine.fX2APIC + || hardwareMachine.fIBPBOnVMExit + || hardwareMachine.fIBPBOnVMEntry + || hardwareMachine.fSpecCtrl + || hardwareMachine.fSpecCtrlByHost + || !hardwareMachine.fL1DFlushOnSched + || hardwareMachine.fL1DFlushOnVMEntry + || !hardwareMachine.fMDSClearOnSched + || hardwareMachine.fMDSClearOnVMEntry) + { + m->sv = SettingsVersion_v1_16; + return; + } + + for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin(); + it != hardwareMachine.storage.llStorageControllers.end(); + ++it) + { + const StorageController &sctl = *it; + + if (sctl.controllerType == StorageControllerType_NVMe) + { + m->sv = SettingsVersion_v1_16; + return; + } + } + + for (CpuIdLeafsList::const_iterator it = hardwareMachine.llCpuIdLeafs.begin(); + it != hardwareMachine.llCpuIdLeafs.end(); + ++it) + if (it->idxSub != 0) + { + m->sv = SettingsVersion_v1_16; + return; + } + } + + if (m->sv < SettingsVersion_v1_15) + { + // VirtualBox 5.0 adds paravirt providers, explicit AHCI port hotplug + // setting, USB storage controller, xHCI, serial port TCP backend + // and VM process priority. + + /* + * Check simple configuration bits first, loopy stuff afterwards. + */ + if ( hardwareMachine.paravirtProvider != ParavirtProvider_Legacy + || hardwareMachine.uCpuIdPortabilityLevel != 0) + { + m->sv = SettingsVersion_v1_15; + return; + } + + /* + * Check whether the hotpluggable flag of all storage devices differs + * from the default for old settings. + * AHCI ports are hotpluggable by default every other device is not. + * Also check if there are USB storage controllers. + */ + for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin(); + it != hardwareMachine.storage.llStorageControllers.end(); + ++it) + { + const StorageController &sctl = *it; + + if (sctl.controllerType == StorageControllerType_USB) + { + m->sv = SettingsVersion_v1_15; + return; + } + + for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin(); + it2 != sctl.llAttachedDevices.end(); + ++it2) + { + const AttachedDevice &att = *it2; + + if ( ( att.fHotPluggable + && sctl.controllerType != StorageControllerType_IntelAhci) + || ( !att.fHotPluggable + && sctl.controllerType == StorageControllerType_IntelAhci)) + { + m->sv = SettingsVersion_v1_15; + return; + } + } + } + + /* + * Check if there is an xHCI (USB3) USB controller. + */ + for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin(); + it != hardwareMachine.usbSettings.llUSBControllers.end(); + ++it) + { + const USBController &ctrl = *it; + if (ctrl.enmType == USBControllerType_XHCI) + { + m->sv = SettingsVersion_v1_15; + return; + } + } + + /* + * Check if any serial port uses the TCP backend. + */ + for (SerialPortsList::const_iterator it = hardwareMachine.llSerialPorts.begin(); + it != hardwareMachine.llSerialPorts.end(); + ++it) + { + const SerialPort &port = *it; + if (port.portMode == PortMode_TCP) + { + m->sv = SettingsVersion_v1_15; + return; + } + } + } + + if (m->sv < SettingsVersion_v1_14) + { + // VirtualBox 4.3 adds default frontend setting, graphics controller + // setting, explicit long mode setting, (video) capturing and NAT networking. + if ( !hardwareMachine.strDefaultFrontend.isEmpty() + || hardwareMachine.graphicsAdapter.graphicsControllerType != GraphicsControllerType_VBoxVGA + || hardwareMachine.enmLongMode != Hardware::LongMode_Legacy + || machineUserData.ovIcon.size() > 0 + || recordingSettings.common.fEnabled) + { + m->sv = SettingsVersion_v1_14; + return; + } + NetworkAdaptersList::const_iterator netit; + for (netit = hardwareMachine.llNetworkAdapters.begin(); + netit != hardwareMachine.llNetworkAdapters.end(); + ++netit) + { + if (netit->mode == NetworkAttachmentType_NATNetwork) + { + m->sv = SettingsVersion_v1_14; + break; + } + } + } + + if (m->sv < SettingsVersion_v1_14) + { + unsigned cOhciCtrls = 0; + unsigned cEhciCtrls = 0; + bool fNonStdName = false; + + for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin(); + it != hardwareMachine.usbSettings.llUSBControllers.end(); + ++it) + { + const USBController &ctrl = *it; + + switch (ctrl.enmType) + { + case USBControllerType_OHCI: + cOhciCtrls++; + if (ctrl.strName != "OHCI") + fNonStdName = true; + break; + case USBControllerType_EHCI: + cEhciCtrls++; + if (ctrl.strName != "EHCI") + fNonStdName = true; + break; + default: + /* Anything unknown forces a bump. */ + fNonStdName = true; + } + + /* Skip checking other controllers if the settings bump is necessary. */ + if (cOhciCtrls > 1 || cEhciCtrls > 1 || fNonStdName) + { + m->sv = SettingsVersion_v1_14; + break; + } + } + } + + if (m->sv < SettingsVersion_v1_13) + { + // VirtualBox 4.2 adds tracing, autostart, UUID in directory and groups. + if ( !debugging.areDefaultSettings() + || !autostart.areDefaultSettings() + || machineUserData.fDirectoryIncludesUUID + || machineUserData.llGroups.size() > 1 + || machineUserData.llGroups.front() != "/") + m->sv = SettingsVersion_v1_13; + } + + if (m->sv < SettingsVersion_v1_13) + { + // VirtualBox 4.2 changes the units for bandwidth group limits. + for (BandwidthGroupList::const_iterator it = hardwareMachine.ioSettings.llBandwidthGroups.begin(); + it != hardwareMachine.ioSettings.llBandwidthGroups.end(); + ++it) + { + const BandwidthGroup &gr = *it; + if (gr.cMaxBytesPerSec % _1M) + { + // Bump version if a limit cannot be expressed in megabytes + m->sv = SettingsVersion_v1_13; + break; + } + } + } + + if (m->sv < SettingsVersion_v1_12) + { + // VirtualBox 4.1 adds PCI passthrough and emulated USB Smart Card reader + if ( hardwareMachine.pciAttachments.size() + || hardwareMachine.fEmulatedUSBCardReader) + m->sv = SettingsVersion_v1_12; + } + + if (m->sv < SettingsVersion_v1_12) + { + // VirtualBox 4.1 adds a promiscuous mode policy to the network + // adapters and a generic network driver transport. + NetworkAdaptersList::const_iterator netit; + for (netit = hardwareMachine.llNetworkAdapters.begin(); + netit != hardwareMachine.llNetworkAdapters.end(); + ++netit) + { + if ( netit->enmPromiscModePolicy != NetworkAdapterPromiscModePolicy_Deny + || netit->mode == NetworkAttachmentType_Generic + || !netit->areGenericDriverDefaultSettings() + ) + { + m->sv = SettingsVersion_v1_12; + break; + } + } + } + + if (m->sv < SettingsVersion_v1_11) + { + // VirtualBox 4.0 adds HD audio, CPU priorities, ~fault tolerance~, + // per-machine media registries, VRDE, JRockitVE, bandwidth groups, + // ICH9 chipset + if ( hardwareMachine.audioAdapter.controllerType == AudioControllerType_HDA + || hardwareMachine.ulCpuExecutionCap != 100 + || mediaRegistry.llHardDisks.size() + || mediaRegistry.llDvdImages.size() + || mediaRegistry.llFloppyImages.size() + || !hardwareMachine.vrdeSettings.strVrdeExtPack.isEmpty() + || !hardwareMachine.vrdeSettings.strAuthLibrary.isEmpty() + || machineUserData.strOsType == "JRockitVE" + || hardwareMachine.ioSettings.llBandwidthGroups.size() + || hardwareMachine.chipsetType == ChipsetType_ICH9 + ) + m->sv = SettingsVersion_v1_11; + } + + if (m->sv < SettingsVersion_v1_10) + { + /* If the properties contain elements other than "TCP/Ports" and "TCP/Address", + * then increase the version to at least VBox 3.2, which can have video channel properties. + */ + unsigned cOldProperties = 0; + + StringsMap::const_iterator it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Ports"); + if (it != hardwareMachine.vrdeSettings.mapProperties.end()) + cOldProperties++; + it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Address"); + if (it != hardwareMachine.vrdeSettings.mapProperties.end()) + cOldProperties++; + + if (hardwareMachine.vrdeSettings.mapProperties.size() != cOldProperties) + m->sv = SettingsVersion_v1_10; + } + + if (m->sv < SettingsVersion_v1_11) + { + /* If the properties contain elements other than "TCP/Ports", "TCP/Address", + * "VideoChannel/Enabled" and "VideoChannel/Quality" then increase the version to VBox 4.0. + */ + unsigned cOldProperties = 0; + + StringsMap::const_iterator it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Ports"); + if (it != hardwareMachine.vrdeSettings.mapProperties.end()) + cOldProperties++; + it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Address"); + if (it != hardwareMachine.vrdeSettings.mapProperties.end()) + cOldProperties++; + it = hardwareMachine.vrdeSettings.mapProperties.find("VideoChannel/Enabled"); + if (it != hardwareMachine.vrdeSettings.mapProperties.end()) + cOldProperties++; + it = hardwareMachine.vrdeSettings.mapProperties.find("VideoChannel/Quality"); + if (it != hardwareMachine.vrdeSettings.mapProperties.end()) + cOldProperties++; + + if (hardwareMachine.vrdeSettings.mapProperties.size() != cOldProperties) + m->sv = SettingsVersion_v1_11; + } + + // settings version 1.9 is required if there is not exactly one DVD + // or more than one floppy drive present or the DVD is not at the secondary + // master; this check is a bit more complicated + // + // settings version 1.10 is required if the host cache should be disabled + // + // settings version 1.11 is required for bandwidth limits and if more than + // one controller of each type is present. + if (m->sv < SettingsVersion_v1_11) + { + // count attached DVDs and floppies (only if < v1.9) + size_t cDVDs = 0; + size_t cFloppies = 0; + + // count storage controllers (if < v1.11) + size_t cSata = 0; + size_t cScsiLsi = 0; + size_t cScsiBuslogic = 0; + size_t cSas = 0; + size_t cIde = 0; + size_t cFloppy = 0; + + // need to run thru all the storage controllers and attached devices to figure this out + for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin(); + it != hardwareMachine.storage.llStorageControllers.end(); + ++it) + { + const StorageController &sctl = *it; + + // count storage controllers of each type; 1.11 is required if more than one + // controller of one type is present + switch (sctl.storageBus) + { + case StorageBus_IDE: + cIde++; + break; + case StorageBus_SATA: + cSata++; + break; + case StorageBus_SAS: + cSas++; + break; + case StorageBus_SCSI: + if (sctl.controllerType == StorageControllerType_LsiLogic) + cScsiLsi++; + else + cScsiBuslogic++; + break; + case StorageBus_Floppy: + cFloppy++; + break; + default: + // Do nothing + break; + } + + if ( cSata > 1 + || cScsiLsi > 1 + || cScsiBuslogic > 1 + || cSas > 1 + || cIde > 1 + || cFloppy > 1) + { + m->sv = SettingsVersion_v1_11; + break; // abort the loop -- we will not raise the version further + } + + for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin(); + it2 != sctl.llAttachedDevices.end(); + ++it2) + { + const AttachedDevice &att = *it2; + + // Bandwidth limitations are new in VirtualBox 4.0 (1.11) + if (m->sv < SettingsVersion_v1_11) + { + if (att.strBwGroup.length() != 0) + { + m->sv = SettingsVersion_v1_11; + break; // abort the loop -- we will not raise the version further + } + } + + // disabling the host IO cache requires settings version 1.10 + if ( (m->sv < SettingsVersion_v1_10) + && (!sctl.fUseHostIOCache) + ) + m->sv = SettingsVersion_v1_10; + + // we can only write the StorageController/@Instance attribute with v1.9 + if ( (m->sv < SettingsVersion_v1_9) + && (sctl.ulInstance != 0) + ) + m->sv = SettingsVersion_v1_9; + + if (m->sv < SettingsVersion_v1_9) + { + if (att.deviceType == DeviceType_DVD) + { + if ( (sctl.storageBus != StorageBus_IDE) // DVD at bus other than DVD? + || (att.lPort != 1) // DVDs not at secondary master? + || (att.lDevice != 0) + ) + m->sv = SettingsVersion_v1_9; + + ++cDVDs; + } + else if (att.deviceType == DeviceType_Floppy) + ++cFloppies; + } + } + + if (m->sv >= SettingsVersion_v1_11) + break; // abort the loop -- we will not raise the version further + } + + // VirtualBox before 3.1 had zero or one floppy and exactly one DVD, + // so any deviation from that will require settings version 1.9 + if ( (m->sv < SettingsVersion_v1_9) + && ( (cDVDs != 1) + || (cFloppies > 1) + ) + ) + m->sv = SettingsVersion_v1_9; + } + + // VirtualBox 3.2: Check for non default I/O settings + if (m->sv < SettingsVersion_v1_10) + { + if ( (hardwareMachine.ioSettings.fIOCacheEnabled != true) + || (hardwareMachine.ioSettings.ulIOCacheSize != 5) + // and page fusion + || (hardwareMachine.fPageFusionEnabled) + // and CPU hotplug, RTC timezone control, HID type and HPET + || machineUserData.fRTCUseUTC + || hardwareMachine.fCpuHotPlug + || hardwareMachine.pointingHIDType != PointingHIDType_PS2Mouse + || hardwareMachine.keyboardHIDType != KeyboardHIDType_PS2Keyboard + || hardwareMachine.fHPETEnabled + ) + m->sv = SettingsVersion_v1_10; + } + + // VirtualBox 3.2 adds NAT and boot priority to the NIC config in Main + // VirtualBox 4.0 adds network bandwitdth + if (m->sv < SettingsVersion_v1_11) + { + NetworkAdaptersList::const_iterator netit; + for (netit = hardwareMachine.llNetworkAdapters.begin(); + netit != hardwareMachine.llNetworkAdapters.end(); + ++netit) + { + if ( (m->sv < SettingsVersion_v1_12) + && (netit->strBandwidthGroup.isNotEmpty()) + ) + { + /* New in VirtualBox 4.1 */ + m->sv = SettingsVersion_v1_12; + break; + } + else if ( (m->sv < SettingsVersion_v1_10) + && (netit->fEnabled) + && (netit->mode == NetworkAttachmentType_NAT) + && ( netit->nat.u32Mtu != 0 + || netit->nat.u32SockRcv != 0 + || netit->nat.u32SockSnd != 0 + || netit->nat.u32TcpRcv != 0 + || netit->nat.u32TcpSnd != 0 + || !netit->nat.fDNSPassDomain + || netit->nat.fDNSProxy + || netit->nat.fDNSUseHostResolver + || netit->nat.fAliasLog + || netit->nat.fAliasProxyOnly + || netit->nat.fAliasUseSamePorts + || netit->nat.strTFTPPrefix.length() + || netit->nat.strTFTPBootFile.length() + || netit->nat.strTFTPNextServer.length() + || netit->nat.mapRules.size() + ) + ) + { + m->sv = SettingsVersion_v1_10; + // no break because we still might need v1.11 above + } + else if ( (m->sv < SettingsVersion_v1_10) + && (netit->fEnabled) + && (netit->ulBootPriority != 0) + ) + { + m->sv = SettingsVersion_v1_10; + // no break because we still might need v1.11 above + } + } + } + + // all the following require settings version 1.9 + if ( (m->sv < SettingsVersion_v1_9) + && ( (hardwareMachine.firmwareType >= FirmwareType_EFI) + || machineUserData.fTeleporterEnabled + || machineUserData.uTeleporterPort + || !machineUserData.strTeleporterAddress.isEmpty() + || !machineUserData.strTeleporterPassword.isEmpty() + || (!hardwareMachine.uuid.isZero() && hardwareMachine.uuid.isValid()) + ) + ) + m->sv = SettingsVersion_v1_9; + + // "accelerate 2d video" requires settings version 1.8 + if ( (m->sv < SettingsVersion_v1_8) + && (hardwareMachine.graphicsAdapter.fAccelerate2DVideo) + ) + m->sv = SettingsVersion_v1_8; + + // The hardware versions other than "1" requires settings version 1.4 (2.1+). + if ( m->sv < SettingsVersion_v1_4 + && hardwareMachine.strVersion != "1" + ) + m->sv = SettingsVersion_v1_4; +} + +/** + * Called from Main code to write a machine config file to disk. This builds a DOM tree from + * the member variables and then writes the XML file; it throws xml::Error instances on errors, + * in particular if the file cannot be written. + */ +void MachineConfigFile::write(const com::Utf8Str &strFilename, PCVBOXCRYPTOIF pCryptoIf, const char *pszPassword) +{ + try + { + // createStubDocument() sets the settings version to at least 1.7; however, + // we might need to enfore a later settings version if incompatible settings + // are present: + bumpSettingsVersionIfNeeded(); + + m->strFilename = strFilename; + /* + * Only create a backup if it is not encrypted. + * Otherwise we get an unencrypted copy of the settings. + */ + if (strKeyId.isEmpty() && strKeyStore.isEmpty()) + specialBackupIfFirstBump(); + createStubDocument(); + + if (strKeyStore.isNotEmpty()) + { + xml::ElementNode *pelmMachine = m->pelmRoot->createChild("MachineEncrypted"); + buildMachineEncryptedXML(*pelmMachine, + MachineConfigFile::BuildMachineXML_IncludeSnapshots + | MachineConfigFile::BuildMachineXML_MediaRegistry, + // but not BuildMachineXML_WriteVBoxVersionAttribute + NULL, /* pllElementsWithUuidAttributes */ + pCryptoIf, + pszPassword); + } + else + { + xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine"); + buildMachineXML(*pelmMachine, + MachineConfigFile::BuildMachineXML_IncludeSnapshots + | MachineConfigFile::BuildMachineXML_MediaRegistry, + // but not BuildMachineXML_WriteVBoxVersionAttribute + NULL); /* pllElementsWithUuidAttributes */ + } + + // now go write the XML + xml::XmlFileWriter writer(*m->pDoc); + writer.write(m->strFilename.c_str(), true /*fSafe*/); + + m->fFileExists = true; + clearDocument(); + LogRel(("Finished saving settings file \"%s\"\n", m->strFilename.c_str())); + } + catch (...) + { + clearDocument(); + LogRel(("Finished saving settings file \"%s\" with failure\n", m->strFilename.c_str())); + throw; + } +} diff --git a/src/VBox/Main/xml/VirtualBox-settings.xsd b/src/VBox/Main/xml/VirtualBox-settings.xsd new file mode 100644 index 00000000..eb7f98d6 --- /dev/null +++ b/src/VBox/Main/xml/VirtualBox-settings.xsd @@ -0,0 +1,1514 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * :tabSize=2:indentSize=2:noTabs=true: + * :folding=explicit:collapseFolds=1: + * + * Oracle VM VirtualBox Settings Schema + * Common definitions +--> +<!-- + Copyright (C) 2004-2022 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 +--> + +<xsd:schema + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns="http://www.virtualbox.org/" + xmlns:vb="http://www.virtualbox.org/" + targetNamespace="http://www.virtualbox.org/" + elementFormDefault="qualified" +> + +<xsd:annotation> + <xsd:documentation xml:lang="en"> + Oracle VM VirtualBox Settings Schema (common definitions). + Copyright (C) 2004-2022 Oracle and/or its affiliates. + </xsd:documentation> +</xsd:annotation> + +<!-- +// Simple types +///////////////////////////////////////////////////////////////////////// +--> + +<xsd:simpleType name="TUUID"> + <xsd:restriction base="xsd:token"> + <xsd:pattern value="\{[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\}"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TNonNullUUID"> + <xsd:restriction base="TUUID"> + <xsd:pattern value=".*[1-9A-Fa-f]+.*"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TUInt8"> + <xsd:union> + <xsd:simpleType> + <xsd:restriction base="xsd:unsignedByte"> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType> + <xsd:restriction base="xsd:string"> + <xsd:pattern value="0[xX][A-Fa-f0-9]{1,2}"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:union> +</xsd:simpleType> + +<xsd:simpleType name="TUInt16"> + <xsd:union> + <xsd:simpleType> + <xsd:restriction base="xsd:unsignedShort"> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType> + <xsd:restriction base="xsd:string"> + <xsd:pattern value="0[xX][A-Fa-f0-9]{1,4}"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:union> +</xsd:simpleType> + +<xsd:simpleType name="TUInt16Hex"> + <xsd:restriction base="xsd:string"> + <xsd:pattern value="0x[A-Fa-f0-9]{1,4}"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TUInt16HexNoBase"> + <xsd:restriction base="xsd:string"> + <xsd:pattern value="[A-Fa-f0-9]{1,4}"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TUInt32Hex"> + <xsd:restriction base="xsd:string"> + <xsd:pattern value="0x[A-Fa-f0-9]{1,8}"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TUInt64Hex"> + <xsd:restriction base="xsd:string"> + <xsd:pattern value="0x[A-Fa-f0-9]{1,16}"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TLocalFile"> + <xsd:restriction base="xsd:string"> + <xsd:pattern value=".+"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TDeviceType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="None"/> + <xsd:enumeration value="Floppy"/> + <xsd:enumeration value="DVD"/> + <xsd:enumeration value="HardDisk"/> + <xsd:enumeration value="Network"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TMediumDeviceType"> + <xsd:restriction base="TDeviceType"> + <xsd:enumeration value="Floppy"/> + <xsd:enumeration value="DVD"/> + <xsd:enumeration value="HardDisk"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TMediumType"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="Normal"/> + <xsd:enumeration value="Immutable"/> + <xsd:enumeration value="Writethrough"/> + <xsd:enumeration value="Shareable"/> + <xsd:enumeration value="Readonly"/> + <xsd:enumeration value="MultiAttach"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TVMProcPriority"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="Invalid"/> + <xsd:enumeration value="Default"/> + <xsd:enumeration value="Flat"/> + <xsd:enumeration value="Low"/> + <xsd:enumeration value="Normal"/> + <xsd:enumeration value="High"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TMonitorCount"> + <xsd:restriction base="xsd:unsignedInt"> + <xsd:minInclusive value="1"/> + <!-- This should be in sync with VBOX_VIDEO_MAX_SCREENS in VBoxVideo.h --> + <xsd:maxInclusive value="64"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TUSBDeviceFilterAction"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="Ignore"/> + <xsd:enumeration value="Hold"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TNonEmptyString"> + <xsd:restriction base="xsd:string"> + <xsd:pattern value=".+"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TPresentDateTimeUTC"> + <xsd:restriction base="xsd:dateTime"> + <xsd:minInclusive value="1900-01-01T00:00:00Z"/> + <xsd:maxInclusive value="199999999-12-31T23:59:59Z"/> + <xsd:pattern value=".+-.+-.+T.+:.+:[0-9]{2}Z"/> + </xsd:restriction> +</xsd:simpleType> + + +<xsd:simpleType name="TAuthType"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="null"/> <!-- deprecated --> + <xsd:enumeration value="Null"/> + <xsd:enumeration value="Guest"/> + <xsd:enumeration value="External"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TNetworkAdapterType"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="Am79C970A"/> + <xsd:enumeration value="Am79C973"/> + <xsd:enumeration value="Am79C960"/> + <xsd:enumeration value="82540EM"/> + <xsd:enumeration value="82543GC"/> + <xsd:enumeration value="82545EM"/> + <xsd:enumeration value="virtio"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TTriStateBoolType"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="false"/> + <xsd:enumeration value="true"/> + <xsd:enumeration value="default"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TBIOSBootMenuModeType"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="Disabled"/> + <xsd:enumeration value="MenuOnly"/> + <xsd:enumeration value="MessageAndMenu"/> + <xsd:enumeration value="messageandmenu"/> <!-- deprecated --> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TLocalOrUTC"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="local"/> + <xsd:enumeration value="UTC"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TClipboardMode"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="Disabled"/> + <xsd:enumeration value="HostToGuest"/> + <xsd:enumeration value="GuestToHost"/> + <xsd:enumeration value="Bidirectional"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TDragAndDropMode"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="Disabled"/> + <xsd:enumeration value="HostToGuest"/> + <xsd:enumeration value="GuestToHost"/> + <xsd:enumeration value="Bidirectional"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TBandwidthGroupType"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="Disk"/> + <xsd:enumeration value="Network"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TPortMode"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="Disconnected"/> + <xsd:enumeration value="RawFile"/> + <xsd:enumeration value="HostPipe"/> + <xsd:enumeration value="HostDevice"/> + <xsd:enumeration value="TCP"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TStorageControllerType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="AHCI"/> + <xsd:enumeration value="LsiLogic"/> + <xsd:enumeration value="BusLogic"/> + <xsd:enumeration value="PIIX3"/> + <xsd:enumeration value="PIIX4"/> + <xsd:enumeration value="ICH6"/> + <xsd:enumeration value="LsiLogicSas"/> + <xsd:enumeration value="I82078"/> + <xsd:enumeration value="USB"/> + <xsd:enumeration value="NVMe"/> + <xsd:enumeration value="VirtioSCSI"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:simpleType name="TDisplayControllerType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="VBoxVGA"/> + <xsd:enumeration value="VMSVGA"/> + <xsd:enumeration value="VBoxSVGA"/> + </xsd:restriction> +</xsd:simpleType> + +<!-- +// Complex types +///////////////////////////////////////////////////////////////////////// +--> +<xsd:complexType name="TDHCPServer"> + <xsd:sequence> + <xsd:element name="Options" minOccurs="0"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="Option" type="TDHCPOption" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + </xsd:sequence> + <xsd:attribute name="networkName" type="xsd:string" use="required"/> + <xsd:attribute name="lowerIP" type="xsd:string" use="required"/> + <xsd:attribute name="upperIP" type="xsd:string" use="required"/> + <xsd:attribute name="IPAddress" type="xsd:string" use="required"/> + <xsd:attribute name="networkMask" type="xsd:string" use="required"/> + <xsd:attribute name="enabled" type="xsd:boolean" use="required"/> +</xsd:complexType> + +<xsd:complexType name="TDHCPOption"> + <xsd:attribute name="name" type="xsd:string" use="required"/> + <xsd:attribute name="value" type="xsd:string" use="required"/> + <xsd:attribute name="encoding" type="xsd:integer" default="0"/> +</xsd:complexType> + +<xsd:complexType name="TNATNetwork"> + <xsd:sequence> + <xsd:element name="PortForwarding4" minOccurs="0"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="Forwarding" type="TNATPortForwarding" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + <xsd:element name="PortForwarding6" minOccurs="0"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="Forwarding" type="TNATPortForwarding" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + <xsd:element name="Mappings" minOccurs="0"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="Loopback4" type="TNATLoopback4" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + </xsd:sequence> + <xsd:attribute name="networkName" type="xsd:string" use="required"/> + <xsd:attribute name="enabled" type="xsd:boolean" use="required"/> + <xsd:attribute name="network" type="xsd:string" use="required"/> + <xsd:attribute name="ipv6" type="xsd:boolean" use="required"/> + <xsd:attribute name="ipv6prefix" type="xsd:string" use="required"/> + <xsd:attribute name="advertiseDefaultIPv6Route" type="xsd:boolean" use="required"/> + <xsd:attribute name="needDhcp" type="xsd:boolean" use="required"/> + <xsd:attribute name="loopback6" type="xsd:integer" default="0"/> +</xsd:complexType> + +<xsd:complexType name="TNATLoopback4"> + <xsd:attribute name="address" type="xsd:string" use="required"/> + <xsd:attribute name="offset" type="xsd:integer" use="required"/> +</xsd:complexType> + +<xsd:complexType name="TNATPortForwarding"> + <xsd:attribute name="name" type="xsd:string" use="required"/> + <xsd:attribute name="proto" type="xsd:integer" use="required"/> + <xsd:attribute name="hostip" type="xsd:string" default=""/> + <xsd:attribute name="hostport" type="xsd:integer" use="required"/> + <xsd:attribute name="guestip" type="xsd:string" use="required"/> + <xsd:attribute name="guestport" type="xsd:integer" use="required"/> +</xsd:complexType> + +<xsd:complexType name="TProperty"> + <xsd:attribute name="name" type="xsd:token" use="required"/> + <xsd:attribute name="value" type="xsd:string" use="required"/> +</xsd:complexType> + +<xsd:complexType name="THardDiskBase"> + <xsd:sequence> + <xsd:element name="Description" type="xsd:string" minOccurs="0"/> + <xsd:element name="Property" type="TProperty" minOccurs="0" maxOccurs="unbounded"/> + <xsd:element name="HardDisk" type="TDiffHardDisk" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + <xsd:attribute name="uuid" type="TNonNullUUID" use="required"/> + <xsd:attribute name="location" type="TLocalFile" use="required"/> + <xsd:attribute name="format" type="TNonEmptyString" use="required"/> +</xsd:complexType> + +<xsd:complexType name="TDiffHardDisk"> + <xsd:complexContent> + <xsd:extension base="THardDiskBase"> + <xsd:attribute name="autoReset" type="xsd:boolean" default="false"/> + </xsd:extension> + </xsd:complexContent> +</xsd:complexType> + +<xsd:complexType name="THardDisk"> + <xsd:complexContent> + <xsd:extension base="THardDiskBase"> + <xsd:attribute name="type" type="TMediumType" use="required"/> + </xsd:extension> + </xsd:complexContent> +</xsd:complexType> + +<xsd:complexType name="TImage2"> + <xsd:sequence> + <xsd:element name="Description" type="xsd:string" minOccurs="0"/> + </xsd:sequence> + <xsd:attribute name="uuid" type="TNonNullUUID" use="required"/> + <xsd:attribute name="location" type="TLocalFile" use="required"/> + <xsd:attribute name="type" type="TMediumType"/> +</xsd:complexType> + +<xsd:complexType name="TImageRef"> + <xsd:attribute name="uuid" type="TNonNullUUID" use="required"/> +</xsd:complexType> + +<xsd:complexType name="THostDrive"> + <xsd:attribute name="src" type="TLocalFile" use="required"/> +</xsd:complexType> + +<xsd:complexType name="TUSBDeviceFilter"> + <xsd:attribute name="name" type="TNonEmptyString" use="required"/> + <xsd:attribute name="active" type="xsd:boolean" use="required"/> + <xsd:attribute name="vendorId" type="xsd:token"/> + <xsd:attribute name="productId" type="xsd:token"/> + <xsd:attribute name="revision" type="xsd:token"/> + <xsd:attribute name="manufacturer" type="xsd:token"/> + <xsd:attribute name="product" type="xsd:token"/> + <xsd:attribute name="serialNumber" type="xsd:token"/> + <xsd:attribute name="port" type="xsd:token"/> + <xsd:attribute name="remote" type="xsd:token"/> + <xsd:attribute name="maskedInterfaces" type="xsd:unsignedInt" default="0"/> +</xsd:complexType> + +<xsd:complexType name="TUSBDeviceFilters"> + <xsd:sequence> + <xsd:element name="DeviceFilter" type="TUSBDeviceFilter" minOccurs="0"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="THostUSBDeviceFilter"> + <xsd:complexContent> + <xsd:extension base="TUSBDeviceFilter"> + <xsd:attribute name="action" type="TUSBDeviceFilterAction" use="required"/> + </xsd:extension> + </xsd:complexContent> +</xsd:complexType> + +<xsd:complexType name="TSystemProperties"> + <xsd:attribute name="defaultMachineFolder" type="TLocalFile"/> + <xsd:attribute name="defaultHardDiskFolder" type="TLocalFile"/> + <xsd:attribute name="defaultHardDiskFormat" type="TNonEmptyString"/> + <xsd:attribute name="VRDEAuthLibrary" type="TLocalFile"/> + <xsd:attribute name="webServiceAuthLibrary" type="TLocalFile"/> + <xsd:attribute name="defaultVRDELibrary" type="TLocalFile"/> + <xsd:attribute name="HWVirtExEnabled" type="xsd:boolean"/> + <xsd:attribute name="LogHistoryCount" type="xsd:unsignedInt" default="3"/> + <xsd:attribute name="defaultVRDEExtPack" type="xsd:string"/> + <xsd:attribute name="exclusiveHwVirt" type="xsd:boolean"/> + <xsd:attribute name="proxyMode" type="xsd:string"/> +</xsd:complexType> + +<xsd:complexType name="TExtraData"> + <xsd:sequence> + <xsd:element name="ExtraDataItem" minOccurs="0" maxOccurs="unbounded"> + <xsd:complexType> + <xsd:attribute name="name" type="xsd:token" use="required"/> + <xsd:attribute name="value" type="xsd:string" use="required"/> + </xsd:complexType> + </xsd:element> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="TMediaRegistry"> + <xsd:all> + <xsd:element name="HardDisks" minOccurs="0"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="HardDisk" type="THardDisk" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + <xsd:element name="DVDImages" minOccurs="0"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="Image" type="TImage2" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + <xsd:element name="FloppyImages" minOccurs="0"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="Image" type="TImage2" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + </xsd:all> +</xsd:complexType> + +<xsd:complexType name="TGlobal"> + <xsd:all> + <xsd:element name="MachineRegistry"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="MachineEntry" minOccurs="0" maxOccurs="unbounded"> + <xsd:complexType> + <xsd:attribute name="src" type="TLocalFile" use="required"/> + <xsd:attribute name="uuid" type="TNonNullUUID" use="required"/> + </xsd:complexType> + </xsd:element> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + <xsd:element name="MediaRegistry" type="TMediaRegistry" minOccurs="0"/> + <xsd:element name="NetserviceRegistry" minOccurs="0"> + <xsd:complexType> + <xsd:all> + <xsd:element name="DHCPServers" minOccurs="0"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="DHCPServer" type="TDHCPServer" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + <xsd:element name="NATNetworks" minOccurs="0"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="NATNetwork" type="TNATNetwork" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + </xsd:all> + </xsd:complexType> + </xsd:element> + <xsd:element name="USBDeviceFilters"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="DeviceFilter" type="THostUSBDeviceFilter" + minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + <xsd:element name="SystemProperties" type="TSystemProperties"/> + <xsd:element name="ExtraData" type="TExtraData" minOccurs="0"/> + </xsd:all> +</xsd:complexType> + +<xsd:complexType name="THWVirtExType"> + <xsd:attribute name="enabled" type="TTriStateBoolType"/> + <xsd:attribute name="exclusive" type="xsd:boolean"/> +</xsd:complexType> + +<xsd:complexType name="THWVirtExNestedPagingType"> + <xsd:attribute name="enabled" type="xsd:boolean"/> +</xsd:complexType> + +<xsd:complexType name="THWVirtExVPIDType"> + <xsd:attribute name="enabled" type="xsd:boolean"/> +</xsd:complexType> + +<xsd:complexType name="THWVirtExUXType"> + <xsd:attribute name="enabled" type="xsd:boolean"/> +</xsd:complexType> + +<xsd:complexType name="TSyntheticCpuType"> + <xsd:attribute name="enabled" type="xsd:boolean"/> +</xsd:complexType> + +<xsd:complexType name="TPAEType"> + <xsd:attribute name="enabled" type="xsd:boolean"/> +</xsd:complexType> + +<xsd:complexType name="TLongModeType"> + <xsd:attribute name="enabled" type="xsd:boolean"/> +</xsd:complexType> + +<xsd:complexType name="THardwareVirtExLargePages"> + <xsd:attribute name="enabled" type="xsd:boolean"/> +</xsd:complexType> + +<xsd:complexType name="THardwareVirtForce"> + <xsd:attribute name="enabled" type="xsd:boolean"/> +</xsd:complexType> + +<xsd:simpleType name="TCPUCount"> + <xsd:restriction base="xsd:unsignedInt"> + <xsd:minInclusive value="1"/> + <xsd:maxInclusive value="64"/> + </xsd:restriction> +</xsd:simpleType> + +<xsd:complexType name="TCpuIdLeaf"> + <xsd:attribute name="id" type="xsd:unsignedInt" use="required"/> + <xsd:attribute name="eax" type="xsd:unsignedInt" use="required"/> + <xsd:attribute name="ebx" type="xsd:unsignedInt" use="required"/> + <xsd:attribute name="ecx" type="xsd:unsignedInt" use="required"/> + <xsd:attribute name="edx" type="xsd:unsignedInt" use="required"/> +</xsd:complexType> + +<xsd:complexType name="TCpuIdTree"> + <xsd:sequence> + <xsd:element name="CpuIdLeaf" type="TCpuIdLeaf" + minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="TCPU"> + <xsd:sequence> + <xsd:element name="HardwareVirtEx" type="THWVirtExType" minOccurs="0"/> + <xsd:element name="HardwareVirtExNestedPaging" type="THWVirtExNestedPagingType" minOccurs="0"/> + <xsd:element name="HardwareVirtExVPID" type="THWVirtExVPIDType" minOccurs="0"/> + <xsd:element name="HardwareVirtExUX" type="THWVirtExUXType" minOccurs="0"/> + <xsd:element name="PAE" type="TPAEType" minOccurs="0"/> + <xsd:element name="LongMode" type="TLongModeType" minOccurs="0"/> + <xsd:element name="HardwareVirtExLargePages" type="THardwareVirtExLargePages" minOccurs="0"/> + <xsd:element name="HardwareVirtForce" type="THardwareVirtForce" minOccurs="0"/> + <xsd:element name="SyntheticCpu" type="TSyntheticCpuType" minOccurs="0"/> + <xsd:element name="CpuIdTree" type="TCpuIdTree" minOccurs="0"> + <xsd:unique name="TCPU-CpuIdTree-CpuIdLeaf"> + <xsd:selector xpath="vb:CpuIdLeaf"/> + <xsd:field xpath="@id"/> + </xsd:unique> + </xsd:element> + </xsd:sequence> + <xsd:attribute name="count" type="TCPUCount" default="1"/> + <xsd:attribute name="hotplug" type="xsd:boolean" default="false"/> +</xsd:complexType> + +<xsd:complexType name="TBoot"> + <xsd:sequence> + <xsd:element name="Order" minOccurs="0" maxOccurs="unbounded"> + <xsd:complexType> + <xsd:attribute name="position" use="required"> + <xsd:simpleType> + <xsd:restriction base="xsd:unsignedInt"> + <xsd:minInclusive value="1"/> + <xsd:maxInclusive value="4"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="device" type="TDeviceType" use="required"/> + </xsd:complexType> + </xsd:element> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="TDisplay"> + <xsd:attribute name="VRAMSize" default="8"> + <xsd:simpleType> + <xsd:restriction base="xsd:unsignedInt"> + <xsd:minInclusive value="0"/> + <xsd:maxInclusive value="256"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="monitorCount" type="TMonitorCount" default="1"/> + <xsd:attribute name="MonitorCount" type="TMonitorCount"/> <!-- deprecated --> + <xsd:attribute name="accelerate3D" type="xsd:boolean" default="false"/> + <xsd:attribute name="accelerate2DVideo" type="xsd:boolean" default="false"/> + <xsd:attribute name="controller" type="TDisplayControllerType" default="VBoxSVGA"/> +</xsd:complexType> + +<xsd:complexType name="TVideoRecording"> + <xsd:attribute name="enabled" type="xsd:boolean" default="false"/> + <xsd:attribute name="file" type="xsd:string"/> + <xsd:attribute name="horzRes"> + <xsd:simpleType> + <xsd:restriction base="xsd:unsignedInt"> + <xsd:minInclusive value="4"/> + <xsd:maxInclusive value="2097152"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="vertRes" type="xsd:unsignedInt"/> +</xsd:complexType> + +<xsd:complexType name="TVideoCapture"> + <xsd:attribute name="enabled" type="xsd:boolean" default="false"/> + <xsd:attribute name="file" type="xsd:string"/> + <xsd:attribute name="screens" type="xsd:unsignedLong"/> <!-- todo: fix writing of settings (writes -1) --> + <xsd:attribute name="horzRes"> + <xsd:simpleType> + <xsd:restriction base="xsd:unsignedInt"> + <xsd:minInclusive value="4"/> + <xsd:maxInclusive value="2097152"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="vertRes" type="xsd:unsignedInt"/> + <xsd:attribute name="rate" type="xsd:unsignedInt"/> + <xsd:attribute name="fps" type="xsd:unsignedInt"/> + <xsd:attribute name="maxTime" type="xsd:unsignedInt"/> + <xsd:attribute name="maxSize" type="xsd:unsignedInt"/> + <xsd:attribute name="options" type="xsd:string"/> +</xsd:complexType> + +<xsd:complexType name="TVRDEProperties"> + <xsd:sequence> + <xsd:element name="Property" type="TProperty" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="TVideoChannel"> + <xsd:attribute name="enabled" type="xsd:boolean" default="false"/> + <xsd:attribute name="quality" type="xsd:unsignedByte" default="75"/> +</xsd:complexType> + +<xsd:complexType name="TRemoteDisplay"> + <xsd:sequence> + <xsd:element name="VideoChannel" type="TVideoChannel" minOccurs="0"/> <!-- deprecated --> + <xsd:choice minOccurs="0"> + <xsd:element name="VRDEProperties" type="TVRDEProperties"/> + </xsd:choice> + </xsd:sequence> + <xsd:attribute name="enabled" type="xsd:boolean" default="false"/> + <xsd:attribute name="port" type="xsd:string" default="3389"/> <!-- deprecated --> + <xsd:attribute name="authType" type="TAuthType" default="Null"/> + <xsd:attribute name="authTimeout" type="xsd:unsignedInt" default="5000"/> + <xsd:attribute name="allowMultiConnection" type="xsd:boolean" default="false"/> + <xsd:attribute name="reuseSingleConnection" type="xsd:boolean" default="false"/> +</xsd:complexType> + +<xsd:complexType name="TBIOS"> + <xsd:all> + <xsd:element name="ACPI" minOccurs="0"> + <xsd:complexType> + <xsd:attribute name="enabled" type="xsd:boolean" use="required"/> + </xsd:complexType> + </xsd:element> + <xsd:element name="IOAPIC" minOccurs="0"> + <xsd:complexType> + <xsd:attribute name="enabled" type="xsd:boolean" default="false"/> + </xsd:complexType> + </xsd:element> + <xsd:element name="Logo" minOccurs="0"> + <xsd:complexType> + <xsd:attribute name="fadeIn" type="xsd:boolean" default="true"/> + <xsd:attribute name="fadeOut" type="xsd:boolean" default="true"/> + <xsd:attribute name="displayTime" type="xsd:unsignedInt" default="0"/> + <xsd:attribute name="imagePath" type="TLocalFile"/> + </xsd:complexType> + </xsd:element> + <xsd:element name="BootMenu" minOccurs="0"> + <xsd:complexType> + <xsd:attribute name="mode" type="TBIOSBootMenuModeType" default="MessageAndMenu"/> + </xsd:complexType> + </xsd:element> + <xsd:element name="TimeOffset" minOccurs="0"> + <xsd:complexType> + <xsd:attribute name="value" type="xsd:integer" default="0"/> + </xsd:complexType> + </xsd:element> + <xsd:element name="PXEDebug" minOccurs="0"> + <xsd:complexType> + <xsd:attribute name="enabled" type="xsd:boolean" default="false"/> + </xsd:complexType> + </xsd:element> + <xsd:element name="NVRAM" minOccurs="0"> + <xsd:complexType> + <xsd:attribute name="enabled" type="xsd:boolean" default="false"/> + <xsd:attribute name="path" type="xsd:string"/> + </xsd:complexType> + </xsd:element> + <xsd:element name="SmbiosUuidLittleEndian" minOccurs="0"> + <xsd:complexType> + <xsd:attribute name="enabled" type="xsd:boolean" default="false"/> + </xsd:complexType> + </xsd:element> + <xsd:element name="IDEController" minOccurs="0"> <!-- deprecated --> + <xsd:complexType> + <xsd:attribute name="type"> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="PIIX3"/> + <xsd:enumeration value="PIIX4"/> + <xsd:enumeration value="ICH6"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + </xsd:complexType> + </xsd:element> + </xsd:all> +</xsd:complexType> + +<xsd:complexType name="TStorageControllerDevice"> + <xsd:choice minOccurs="0"> + <xsd:element name="Image" type="TImageRef"/> + <xsd:element name="HostDrive" type="THostDrive"/> + </xsd:choice> + <xsd:attribute name="type" type="TMediumDeviceType"/> + <xsd:attribute name="port" type="xsd:unsignedInt" default="0"/> + <xsd:attribute name="device" type="xsd:unsignedInt" default="0"/> + <xsd:attribute name="passthrough" type="xsd:boolean" default="false"/> + <xsd:attribute name="tempeject" type="xsd:boolean" default="false"/> + <xsd:attribute name="nonrotational" type="xsd:boolean" default="false"/> + <xsd:attribute name="discard" type="xsd:boolean" default="false"/> + <xsd:attribute name="hotpluggable" type="xsd:boolean" default="false"/> +</xsd:complexType> + +<xsd:complexType name="TStorageController"> + <xsd:sequence> + <xsd:element name="AttachedDevice" type="TStorageControllerDevice" + minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required"/> + <xsd:attribute name="type" type="TStorageControllerType" use="required"/> + <xsd:attribute name="PortCount" type="xsd:unsignedInt" use="required"/> + <xsd:attribute name="useHostIOCache" type="xsd:boolean" use="optional" default="true"/> + <xsd:attribute name="Bootable" type="xsd:boolean" use="optional"/> + <xsd:attribute name="PCIBus" type="xsd:unsignedInt" use="optional"/> + <xsd:attribute name="PCIDevice" type="xsd:unsignedInt" use="optional"/> + <xsd:attribute name="PCIFunction" type="xsd:unsignedInt" use="optional"/> + <xsd:attribute name="IDE0MasterEmulationPort" type="xsd:unsignedInt" use="optional"/> + <xsd:attribute name="IDE0SlaveEmulationPort" type="xsd:unsignedInt" use="optional"/> + <xsd:attribute name="IDE1MasterEmulationPort" type="xsd:unsignedInt" use="optional"/> + <xsd:attribute name="IDE1SlaveEmulationPort" type="xsd:unsignedInt" use="optional"/> +</xsd:complexType> + +<xsd:complexType name="TSATAController"> <!-- deprecated --> + <xsd:sequence> + <xsd:element name="AttachedDevice" type="TStorageControllerDevice" + minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + <xsd:attribute name="enabled" type="xsd:boolean" default="false"/> + <xsd:attribute name="PortCount" type="xsd:unsignedInt" use="required"/> + <xsd:attribute name="IDE0MasterEmulationPort" type="xsd:unsignedInt" use="optional"/> + <xsd:attribute name="IDE0SlaveEmulationPort" type="xsd:unsignedInt" use="optional"/> + <xsd:attribute name="IDE1MasterEmulationPort" type="xsd:unsignedInt" use="optional"/> + <xsd:attribute name="IDE1SlaveEmulationPort" type="xsd:unsignedInt" use="optional"/> +</xsd:complexType> + +<xsd:complexType name="TStorageControllers"> + <xsd:sequence> + <xsd:element name="StorageController" type="TStorageController" + minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="THardDiskAttachment"> + <xsd:attribute name="hardDisk" type="TNonNullUUID"/> + <xsd:attribute name="bus" default="IDE"> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="IDE"/> + <xsd:enumeration value="SATA"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="channel" type="xsd:unsignedInt" default="0"/> + <xsd:attribute name="device" type="xsd:unsignedInt" default="0"/> +</xsd:complexType> + +<xsd:complexType name="THardDiskAttachments"> <!-- deprecated --> + <xsd:sequence> + <xsd:element name="HardDiskAttachment" type="THardDiskAttachment" + minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="TDVDDrive"> + <xsd:choice minOccurs="0"> + <xsd:element name="Image" type="TImageRef"/> + <xsd:element name="HostDrive" type="THostDrive"/> + </xsd:choice> + <xsd:attribute name="passthrough" type="xsd:boolean" default="false"/> +</xsd:complexType> + +<xsd:complexType name="TFloppyDrive"> + <xsd:choice minOccurs="0"> + <xsd:element name="Image" type="TImageRef"/> + <xsd:element name="HostDrive" type="THostDrive"/> + </xsd:choice> + <xsd:attribute name="enabled" type="xsd:boolean" default="true"/> +</xsd:complexType> + +<xsd:complexType name="TUSBController"> + <xsd:sequence> + <xsd:element name="DeviceFilter" type="TUSBDeviceFilter" + minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + <xsd:attribute name="enabled" type="xsd:boolean" use="required"/> + <xsd:attribute name="enabledEhci" type="xsd:boolean" default="false"/> +</xsd:complexType> + +<xsd:complexType name="TUSBController2"> + <xsd:attribute name="name" type="xsd:string" use="required"/> + <xsd:attribute name="type" use="required"> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="OHCI"/> + <xsd:enumeration value="EHCI"/> + <xsd:enumeration value="XHCI"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> +</xsd:complexType> + +<xsd:complexType name="TUSBControllers"> + <xsd:sequence> + <xsd:element name="Controller" type="TUSBController2" + minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="TUSB"> + <xsd:sequence> + <xsd:element name="Controllers" type="TUSBControllers" minOccurs="0"/> + <xsd:element name="DeviceFilters" type="TUSBDeviceFilters" minOccurs="0"/> + </xsd:sequence> +</xsd:complexType> + + +<xsd:complexType name="TAudioAdapter"> + <xsd:attribute name="enabled" type="xsd:boolean" default="false"/> + <xsd:attribute name="enabledIn" type="xsd:boolean" default="false"/> + <xsd:attribute name="enabledOut" type="xsd:boolean" default="false"/> + <xsd:attribute name="controller" default="AC97"> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="AC97"/> + <xsd:enumeration value="SB16"/> + <xsd:enumeration value="HDA"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="driver" use="required"> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="null"/> <!-- deprecated --> + <xsd:enumeration value="Null"/> <!-- all platforms --> + <xsd:enumeration value="OSS"/> <!-- Linux, Solaris, FreeBSD --> + <xsd:enumeration value="ALSA"/> <!-- Linux, FreeBSD --> + <xsd:enumeration value="Pulse"/> <!-- Linux --> + <xsd:enumeration value="CoreAudio"/> <!-- Mac OS X --> + <xsd:enumeration value="MMPM"/> <!-- OS/2 --> + <xsd:enumeration value="SolAudio"/> <!-- Solaris --> + <xsd:enumeration value="WinMM"/> <!-- Windows --> + <xsd:enumeration value="DirectSound"/> <!-- Windows --> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> +</xsd:complexType> + +<xsd:complexType name="TNetNAT"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="DNS" minOccurs="0"> + <xsd:complexType> + <xsd:attribute name="pass-domain" type="xsd:boolean" default="true"/> + <xsd:attribute name="use-proxy" type="xsd:boolean" default="false"/> + <xsd:attribute name="use-host-resolver" type="xsd:boolean" default="false"/> + </xsd:complexType> + </xsd:element> + <xsd:element name="Alias" minOccurs="0"> + <xsd:complexType> + <xsd:attribute name="logging" type="xsd:boolean" default="false"/> + <xsd:attribute name="proxy-only" type="xsd:boolean" default="false"/> + <xsd:attribute name="use-same-ports" type="xsd:boolean" default="false"/> + </xsd:complexType> + </xsd:element> + <xsd:element name="TFTP" minOccurs="0"> + <xsd:complexType> + <xsd:attribute name="prefix" type="xsd:string"/> + <xsd:attribute name="boot-file" type="xsd:string"/> + <xsd:attribute name="next-server" type="xsd:string"/> + </xsd:complexType> + </xsd:element> + <xsd:element name="Forwarding" minOccurs="0" maxOccurs="unbounded"> + <xsd:complexType> + <xsd:attribute name="name" type="xsd:string"/> + <xsd:attribute name="proto" type="xsd:unsignedInt"/> + <xsd:attribute name="hostip" type="xsd:string"/> + <xsd:attribute name="hostport" type="xsd:unsignedInt"/> + <xsd:attribute name="guestip" type="xsd:string"/> + <xsd:attribute name="guestport" type="xsd:unsignedInt"/> + </xsd:complexType> + </xsd:element> + </xsd:choice> + <xsd:attribute name="network" type="xsd:string"/> + <xsd:attribute name="hostip" type="xsd:string"/> + <xsd:attribute name="mtu" type="xsd:unsignedInt"/> + <xsd:attribute name="sockrcv" type="xsd:unsignedInt"/> + <xsd:attribute name="socksnd" type="xsd:unsignedInt"/> + <xsd:attribute name="tcprcv" type="xsd:unsignedInt"/> + <xsd:attribute name="tcpsnd" type="xsd:unsignedInt"/> +</xsd:complexType> + +<xsd:complexType name="TNetNATNetwork"> + <xsd:attribute name="name" type="xsd:string"/> +</xsd:complexType> + +<xsd:complexType name="TNetBridged"> + <xsd:attribute name="name" type="xsd:string"/> +</xsd:complexType> + +<xsd:complexType name="TNetInternal"> + <xsd:attribute name="name" type="xsd:string"/> +</xsd:complexType> + +<xsd:complexType name="TNetHostOnly"> + <xsd:attribute name="name" type="xsd:string"/> +</xsd:complexType> + +<xsd:complexType name="TNetGeneric"> + <xsd:sequence> + <xsd:element name="Property" type="TProperty" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + <xsd:attribute name="driver" type="xsd:string" use="required"/> +</xsd:complexType> + +<xsd:complexType name="TNetworkConfig"> + <xsd:choice maxOccurs="2"> + <xsd:choice minOccurs="0"> + <xsd:element name="NAT" type="TNetNAT"/> + <xsd:element name="NATNetwork" type="TNetNATNetwork"/> + <xsd:element name="HostInterface" type="TNetBridged"/> + <xsd:element name="BridgedInterface" type="TNetBridged"/> + <xsd:element name="InternalNetwork" type="TNetInternal"/> + <xsd:element name="HostOnlyInterface" type="TNetHostOnly"/> + <xsd:element name="GenericInterface" type="TNetGeneric"/> + </xsd:choice> + <xsd:element name="DisabledModes"> + <xsd:complexType> + <xsd:all> + <xsd:element name="NAT" type="TNetNAT" minOccurs="0"/> + <xsd:element name="NATNetwork" type="TNetNATNetwork" minOccurs="0"/> + <xsd:element name="HostInterface" type="TNetBridged" minOccurs="0"/> + <xsd:element name="BridgedInterface" type="TNetBridged" minOccurs="0"/> + <xsd:element name="InternalNetwork" type="TNetInternal" minOccurs="0"/> + <xsd:element name="HostOnlyInterface" type="TNetHostOnly" minOccurs="0"/> + <xsd:element name="GenericInterface" type="TNetGeneric" minOccurs="0"/> + </xsd:all> + </xsd:complexType> + </xsd:element> + </xsd:choice> +</xsd:complexType> + +<xsd:complexType name="TNetworkAdapter"> + <xsd:complexContent> + <xsd:extension base="TNetworkConfig"> + <xsd:attribute name="type" type="TNetworkAdapterType" default="Am79C973"/> + <xsd:attribute name="slot" type="xsd:unsignedInt" use="required"/> + <xsd:attribute name="enabled" type="xsd:boolean" default="false"/> + <xsd:attribute name="MACAddress"> + <xsd:simpleType> + <xsd:restriction base="xsd:hexBinary"> + <xsd:pattern value="[0-9A-Fa-f][02468ACEace][0-9A-Fa-f]{10}"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="cable" type="xsd:boolean" default="true"/> + <xsd:attribute name="speed" type="xsd:unsignedInt" default="1000000"/> + <xsd:attribute name="bootPriority" type="xsd:unsignedInt"/> + <xsd:attribute name="trace" type="xsd:boolean" default="false"/> + <xsd:attribute name="tracefile" type="xsd:string"/> + <xsd:attribute name="bandwidthGroup" type="xsd:string"/> + <xsd:attribute name="promiscuousModePolicy" default="Deny"> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="Deny"/> + <xsd:enumeration value="AllowNetwork"/> + <xsd:enumeration value="AllowAll"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + </xsd:extension> + </xsd:complexContent> +</xsd:complexType> + +<xsd:complexType name="TNetwork"> + <xsd:sequence> + <xsd:element name="Adapter" type="TNetworkAdapter" + minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="TUARTPort"> + <xsd:attribute name="slot" use="required"> + <xsd:simpleType> + <xsd:restriction base="xsd:unsignedInt"> + <xsd:minInclusive value="0"/> + <xsd:maxExclusive value="4"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="enabled" type="xsd:boolean" use="required"/> + <xsd:attribute name="IRQ" type="TUInt8" use="required"/> + <xsd:attribute name="IOBase" type="TUInt16" use="required"/> + <xsd:attribute name="hostMode" type="TPortMode" use="required"/> + <xsd:attribute name="path" type="TLocalFile"/> + <xsd:attribute name="server" type="xsd:boolean" default="false"/> +</xsd:complexType> + +<xsd:complexType name="TUART"> + <xsd:sequence> + <xsd:element name="Port" type="TUARTPort" + minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="TUartPort"> <!-- deprecated --> + <xsd:attribute name="slot" use="required"> + <xsd:simpleType> + <xsd:restriction base="xsd:unsignedInt"> + <xsd:minInclusive value="0"/> + <xsd:maxExclusive value="4"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="enabled" type="xsd:boolean" use="required"/> + <xsd:attribute name="IRQ" type="TUInt8" use="required"/> + <xsd:attribute name="IOBase" type="TUInt16HexNoBase" use="required"/> + <xsd:attribute name="hostMode" type="TPortMode" use="required"/> + <xsd:attribute name="path" type="TLocalFile"/> + <xsd:attribute name="server" type="xsd:boolean" default="false"/> +</xsd:complexType> + +<xsd:complexType name="TUart"> <!-- deprecated --> + <xsd:sequence> + <xsd:element name="Port" type="TUartPort" + minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="TLPTPort"> + <xsd:attribute name="slot" use="required"> + <xsd:simpleType> + <xsd:restriction base="xsd:unsignedInt"> + <xsd:minInclusive value="0"/> + <xsd:maxExclusive value="2"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="enabled" type="xsd:boolean" use="required"/> + <xsd:attribute name="IRQ" type="TUInt8" use="required"/> + <xsd:attribute name="IOBase" type="TUInt16" use="required"/> + <xsd:attribute name="path" type="TLocalFile"/> +</xsd:complexType> + +<xsd:complexType name="TLPT"> + <xsd:sequence> + <xsd:element name="Port" type="TLPTPort" + minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="TLptPort"> <!-- deprecated --> + <xsd:attribute name="slot" use="required"> + <xsd:simpleType> + <xsd:restriction base="xsd:unsignedInt"> + <xsd:minInclusive value="0"/> + <xsd:maxExclusive value="2"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="enabled" type="xsd:boolean" use="required"/> + <xsd:attribute name="IRQ" type="TUInt8" use="required"/> + <xsd:attribute name="IOBase" type="TUInt16HexNoBase" use="required"/> + <xsd:attribute name="path" type="TLocalFile"/> +</xsd:complexType> + +<xsd:complexType name="TLpt"> + <xsd:sequence> + <xsd:element name="Port" type="TLptPort" + minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="TRTC"> + <xsd:attribute name="localOrUTC" type="TLocalOrUTC" use="required"/> +</xsd:complexType> + +<xsd:complexType name="TSharedFolder"> + <xsd:attribute name="name" type="TNonEmptyString" use="required"/> + <xsd:attribute name="hostPath" type="TLocalFile" use="required"/> + <xsd:attribute name="writable" type="xsd:boolean" default="true"/> + <xsd:attribute name="autoMount" type="xsd:boolean" default="false"/> +</xsd:complexType> + +<xsd:complexType name="TSharedFolders"> + <xsd:sequence> + <xsd:element name="SharedFolder" type="TSharedFolder" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="TClipboard"> + <xsd:attribute name="mode" type="TClipboardMode" default="Disabled"/> +</xsd:complexType> + +<xsd:complexType name="TDragAndDrop"> + <xsd:attribute name="mode" type="TDragAndDropMode" use="required"/> +</xsd:complexType> + +<xsd:complexType name="TIoCache"> + <xsd:attribute name="enabled" type="xsd:boolean" default="true"/> + <xsd:attribute name="size" type="xsd:unsignedLong"/> +</xsd:complexType> + +<xsd:complexType name="TBandwidthGroup"> + <xsd:attribute name="name" type="xsd:token" use="required"/> + <xsd:attribute name="type" type="TBandwidthGroupType" use="required"/> + <xsd:attribute name="maxBytesPerSec" type="xsd:unsignedLong"/> + <xsd:attribute name="maxMbPerSec" type="xsd:unsignedLong"/> +</xsd:complexType> + +<xsd:complexType name="TBandwidthGroups"> + <xsd:sequence> + <xsd:element name="BandwidthGroup" type="TBandwidthGroup" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="TIO"> + <xsd:sequence> + <xsd:element name="IoCache" type="TIoCache" minOccurs="0"/> + <xsd:element name="BandwidthGroups" type="TBandwidthGroups" minOccurs="0"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="THostPciDevice"> + <xsd:attribute name="host" type="xsd:unsignedInt" use="required"/> + <xsd:attribute name="guest" type="xsd:unsignedInt" use="required"/> + <xsd:attribute name="name" type="xsd:token"/> +</xsd:complexType> + +<xsd:complexType name="THostPciDevices"> + <xsd:sequence> + <xsd:element name="Device" type="THostPciDevice" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="THostPci"> + <xsd:sequence> + <xsd:element name="Devices" type="THostPciDevices" minOccurs="0"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="TCardReader"> + <xsd:attribute name="enabled" type="xsd:boolean" default="false"/> +</xsd:complexType> + +<xsd:complexType name="TWebcam"> + <xsd:attribute name="enabled" type="xsd:boolean" default="false"/> +</xsd:complexType> + +<xsd:complexType name="TEmulatedUSB"> + <xsd:sequence> + <xsd:element name="CardReader" type="TCardReader" minOccurs="0"/> + <xsd:element name="Webcam" type="TWebcam" minOccurs="0"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="TGuest"> + <xsd:attribute name="memoryBalloonSize" type="xsd:unsignedInt" default="0"/> + <xsd:attribute name="MemoryBalloonSize" type="xsd:unsignedInt" default="0"/> <!-- deprecated --> + <xsd:attribute name="statisticsUpdateInterval" type="xsd:unsignedInt" default="0"/> + <xsd:attribute name="StatisticsUpdateInterval" type="xsd:unsignedInt" default="0"/> <!-- deprecated --> +</xsd:complexType> + +<xsd:complexType name="TGuestProperty"> + <xsd:attribute name="name" type="xsd:string" use="required"/> + <xsd:attribute name="value" type="xsd:string" use="required"/> + <xsd:attribute name="timestamp" type="xsd:unsignedLong" default="0"/> + <xsd:attribute name="flags" type="xsd:string" default=""/> +</xsd:complexType> + +<xsd:complexType name="TGuestProperties"> + <xsd:sequence> + <xsd:element name="GuestProperty" type="TGuestProperty" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + <xsd:attribute name="notificationPatterns" type="xsd:string" default=""/> +</xsd:complexType> + +<xsd:complexType name="TMemory"> + <xsd:attribute name="RAMSize" use="required"> + <xsd:simpleType> + <xsd:restriction base="xsd:unsignedInt"> + <xsd:minInclusive value="4"/> + <xsd:maxInclusive value="2097152"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="PageFusion" type="xsd:boolean" default="false"/> +</xsd:complexType> + +<xsd:complexType name="TFirmware"> + <xsd:attribute name="type" use="required"> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="BIOS"/> + <xsd:enumeration value="EFI"/> + <xsd:enumeration value="EFI32"/> + <xsd:enumeration value="EFI64"/> + <xsd:enumeration value="EFIDUAL"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> +</xsd:complexType> + +<xsd:complexType name="THID"> + <xsd:attribute name="Pointing" default="PS2Mouse"> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="USBMouse"/> + <xsd:enumeration value="USBTablet"/> + <xsd:enumeration value="PS2Mouse"/> + <xsd:enumeration value="ComboMouse"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="Keyboard" default="PS2Keyboard"> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="USBKeyboard"/> + <xsd:enumeration value="PS2Keyboard"/> + <xsd:enumeration value="ComboKeyboard"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> +</xsd:complexType> + +<xsd:complexType name="THPET"> + <xsd:attribute name="enabled" type="xsd:boolean" default="false"/> +</xsd:complexType> + +<xsd:complexType name="TChipset"> + <xsd:attribute name="type" use="required"> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="PIIX3"/> + <xsd:enumeration value="ICH9"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> +</xsd:complexType> + +<xsd:complexType name="TParavirt"> + <xsd:attribute name="provider" use="required"> + <xsd:simpleType> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="None"/> + <xsd:enumeration value="Default"/> + <xsd:enumeration value="Legacy"/> + <xsd:enumeration value="Minimal"/> + <xsd:enumeration value="HyperV"/> + <xsd:enumeration value="KVM"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="debug" type="xsd:string"/> +</xsd:complexType> + +<xsd:complexType name="TTeleporter"> + <xsd:attribute name="enabled" type="xsd:boolean" default="false"/> + <xsd:attribute name="port" type="xsd:unsignedShort"/> + <xsd:attribute name="address" type="xsd:string"/> + <xsd:attribute name="password" type="xsd:string"/> +</xsd:complexType> + +<xsd:complexType name="THardware"> + <xsd:all> + <xsd:element name="CPU" type="TCPU" minOccurs="0"/> + <xsd:element name="Memory" type="TMemory"/> + <xsd:element name="Firmware" type="TFirmware" minOccurs="0"/> + <xsd:element name="HID" type="THID" minOccurs="0"/> + <xsd:element name="HPET" type="THPET" minOccurs="0"/> + <xsd:element name="Chipset" type="TChipset" minOccurs="0"/> + <xsd:element name="Paravirt" type="TParavirt" minOccurs="0"/> + <xsd:element name="Boot" type="TBoot" minOccurs="0"> + <xsd:unique name="THardware-Boot-Order"> + <xsd:selector xpath="vb:Order"/> + <xsd:field xpath="@position"/> + </xsd:unique> + </xsd:element> + <xsd:element name="Display" type="TDisplay" minOccurs="0"/> + <xsd:element name="VideoRecording" type="TVideoRecording" minOccurs="0"/> + <xsd:element name="VideoCapture" type="TVideoCapture" minOccurs="0"/> + <xsd:element name="RemoteDisplay" type="TRemoteDisplay" minOccurs="0"/> + <xsd:element name="BIOS" type="TBIOS" minOccurs="0"/> + <xsd:element name="DVDDrive" type="TDVDDrive" minOccurs="0"/> + <xsd:element name="FloppyDrive" type="TFloppyDrive" minOccurs="0"/> + <xsd:element name="USBController" type="TUSBController" minOccurs="0"/> + <xsd:element name="USB" type="TUSB" minOccurs="0"/> + <xsd:element name="SATAController" type="TSATAController" minOccurs="0"/> <!-- deprecated --> + <xsd:element name="Network" type="TNetwork" minOccurs="0"> + <xsd:unique name="THardware-Network-Adapter"> + <xsd:selector xpath="vb:Adapter"/> + <xsd:field xpath="@slot"/> + </xsd:unique> + </xsd:element> + <xsd:element name="UART" type="TUART" minOccurs="0"> + <xsd:unique name="THardware-UART-Port"> + <xsd:selector xpath="vb:Port"/> + <xsd:field xpath="@slot"/> + </xsd:unique> + </xsd:element> + <xsd:element name="Uart" type="TUart" minOccurs="0"> <!-- deprecated --> + <xsd:unique name="THardware-Uart-Port"> + <xsd:selector xpath="vb:Port"/> + <xsd:field xpath="@slot"/> + </xsd:unique> + </xsd:element> + <xsd:element name="LPT" type="TLPT" minOccurs="0"> + <xsd:unique name="THardware-LPT-Port"> + <xsd:selector xpath="vb:Port"/> + <xsd:field xpath="@slot"/> + </xsd:unique> + </xsd:element> + <xsd:element name="Lpt" type="TLpt" minOccurs="0"> <!-- deprecated --> + <xsd:unique name="THardware-Lpt-Port"> + <xsd:selector xpath="vb:Port"/> + <xsd:field xpath="@slot"/> + </xsd:unique> + </xsd:element> + <xsd:element name="AudioAdapter" type="TAudioAdapter" minOccurs="0"/> + <xsd:element name="RTC" type="TRTC" minOccurs="0"/> + <xsd:element name="SharedFolders" type="TSharedFolders" minOccurs="0"> + <xsd:unique name="THardware-SharedFolders-SharedFolder"> + <xsd:selector xpath="vb:SharedFolder"/> + <xsd:field xpath="@name"/> + </xsd:unique> + </xsd:element> + <xsd:element name="Clipboard" type="TClipboard" minOccurs="0"/> + <xsd:element name="DragAndDrop" type="TDragAndDrop" minOccurs="0"/> + <xsd:element name="IO" type="TIO" minOccurs="0"/> + <xsd:element name="HostPci" type="THostPci" minOccurs="0"/> + <xsd:element name="EmulatedUSB" type="TEmulatedUSB" minOccurs="0"/> + <xsd:element name="Guest" type="TGuest" minOccurs="0"/> + <xsd:element name="GuestProperties" type="TGuestProperties" minOccurs="0"> + <xsd:unique name="THardware-GuestProperties-GuestProperty"> + <xsd:selector xpath="vb:GuestProperty"/> + <xsd:field xpath="@name"/> + </xsd:unique> + </xsd:element> + <xsd:element name="StorageControllers" type="TStorageControllers" minOccurs="0"/> + </xsd:all> + <xsd:attribute name="version" type="xsd:string" default="2"/> +</xsd:complexType> + +<xsd:complexType name="TGroup"> + <xsd:attribute name="name" type="xsd:string" use="required"/> +</xsd:complexType> + +<xsd:complexType name="TGroups"> + <xsd:sequence> + <xsd:element name="Group" type="TGroup" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> +</xsd:complexType> + +<xsd:complexType name="TMachine"> + <xsd:all> + <xsd:element name="MediaRegistry" type="TMediaRegistry" minOccurs="0"/> + <xsd:element name="Description" type="xsd:string" minOccurs="0"/> + <xsd:element name="Teleporter" type="TTeleporter" minOccurs="0"/> + <xsd:element name="Hardware" type="THardware"/> + <xsd:element name="StorageControllers" type="TStorageControllers" minOccurs="0"/> + <xsd:element name="HardDiskAttachments" type="THardDiskAttachments" minOccurs="0"/> <!-- deprecated --> + <xsd:element name="Groups" type="TGroups" minOccurs="0"/> + <xsd:element name="ExtraData" type="TExtraData" minOccurs="0"/> + <xsd:element name="Snapshot" type="TSnapshot" minOccurs="0"/> + </xsd:all> + <xsd:attribute name="name" type="TNonEmptyString" use="required"/> + <xsd:attribute name="nameSync" type="xsd:boolean" default="true"/> + <xsd:attribute name="directoryIncludesUUID" type="xsd:boolean" default="false"/> + <xsd:attribute name="OSType" type="TNonEmptyString" use="required"/> + <xsd:attribute name="uuid" type="TNonNullUUID" use="required"/> + <xsd:attribute name="stateFile" type="TLocalFile"/> + <xsd:attribute name="currentSnapshot" type="TNonNullUUID"/> + <xsd:attribute name="snapshotFolder" type="TLocalFile"/> + <xsd:attribute name="lastStateChange" type="TPresentDateTimeUTC"/> + <xsd:attribute name="aborted" type="xsd:boolean" default="false"/> + <xsd:attribute name="currentStateModified" type="xsd:boolean" default="true"/> + <xsd:attribute name="version" type="xsd:string" default="1.15"/> <!-- Used for OVF files only, must not be present in normal settings files. The default corresponds to settings created by 5.0, which covers many older versions but not newer ones. --> + <xsd:attribute name="VMProcessPriority" type="TVMProcPriority"/> +</xsd:complexType> + +<xsd:complexType name="TSnapshot"> + <xsd:all> + <xsd:element name="Description" type="xsd:string" minOccurs="0"/> + <xsd:element name="Hardware" type="THardware"/> + <xsd:element name="StorageControllers" type="TStorageControllers" minOccurs="0"/> + <xsd:element name="Snapshots" minOccurs="0"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="Snapshot" type="TSnapshot" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + </xsd:all> + <xsd:attribute name="name" type="xsd:token" use="required"/> + <xsd:attribute name="uuid" type="TNonNullUUID" use="required"/> + <xsd:attribute name="timeStamp" type="TPresentDateTimeUTC" use="required"/> + <xsd:attribute name="stateFile" type="TLocalFile"/> +</xsd:complexType> + +<xsd:complexType name="TVirtualBox"> + <xsd:choice> + <xsd:element name="Global" type="TGlobal"/> + <xsd:element name="Machine" type="TMachine"> + <!-- @currentSnapshot must refer to an existing Snapshot/@uuid --> + <xsd:key name="snapshot"> + <xsd:selector xpath=".//vb:Snapshot"/> + <xsd:field xpath="@uuid"/> + </xsd:key> + <xsd:keyref name="currentSnapshot" refer="vb:snapshot"> + <xsd:selector xpath="."/> + <xsd:field xpath="@currentSnapshot"/> + </xsd:keyref> + </xsd:element> + </xsd:choice> + <xsd:attribute name="version" type="xsd:string" use="required"/> +</xsd:complexType> + +<!-- Root element for all VirtualBox config files --> +<xsd:element name="VirtualBox" type="TVirtualBox"/> + +</xsd:schema> diff --git a/src/VBox/Main/xml/ovfreader.cpp b/src/VBox/Main/xml/ovfreader.cpp new file mode 100644 index 00000000..7d13d7be --- /dev/null +++ b/src/VBox/Main/xml/ovfreader.cpp @@ -0,0 +1,1078 @@ +/* $Id: ovfreader.cpp $ */ +/** @file + * OVF reader declarations. + * + * Depends only on IPRT, including the RTCString and IPRT XML classes. + */ + +/* + * Copyright (C) 2008-2022 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 + */ + +#define LOG_GROUP LOG_GROUP_MAIN_APPLIANCE +#include "ovfreader.h" +#include <VBox/log.h> +#include <vector> + +using namespace std; +using namespace ovf; + + + +//////////////////////////////////////////////////////////////////////////////// +// +// OVF reader implementation +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Default Constructor. + * Should be used if you don't have an OVF file, but want to fill the data + * m_mapDisks, m_llVirtualSystems manually + */ +OVFReader::OVFReader() +{ +} + +/** + * Constructor. This parses the given XML file out of the memory. Throws lots of exceptions + * on XML or OVF invalidity. + * @param pvBuf the memory buffer to parse + * @param cbSize the size of the memory buffer + * @param path path to a filename for error messages. + */ +OVFReader::OVFReader(const void *pvBuf, size_t cbSize, const RTCString &path) + : m_strPath(path) +{ + xml::XmlMemParser parser; + parser.read(pvBuf, cbSize, + m_strPath, + m_doc); + /* Start the parsing */ + parse(); +} + +/** + * Constructor. This opens the given XML file and parses it. Throws lots of exceptions + * on XML or OVF invalidity. + * @param path + */ +OVFReader::OVFReader(const RTCString &path) + : m_strPath(path) +{ + xml::XmlFileParser parser; + parser.read(m_strPath, + m_doc); + /* Start the parsing */ + parse(); +} + +void OVFReader::parse() +{ + const xml::ElementNode *pRootElem = m_doc.getRootElement(); + const xml::AttributeNode *pTypeAttr; + const char *pcszTypeAttr = ""; + RTCString pcszNamespaceURI; + + if (!pRootElem || strcmp(pRootElem->getName(), "Envelope")) + throw OVFLogicError(N_("Root element in OVF file must be 'Envelope'.")); + + pcszNamespaceURI = pRootElem->getNamespaceURI(); + if (pcszNamespaceURI.isEmpty()) + { + throw OVFLogicError(N_("Error reading namespace URI in 'Envelope' element, line %d"), pRootElem->getLineNumber()); + } + + if (strncmp(ovf::OVF20_URI_string, pcszNamespaceURI.c_str(), pcszNamespaceURI.length()) == 0) + { + m_envelopeData.setOVFVersion(ovf::OVFVersion_2_0); + } + else if (strncmp(OVF10_URI_string, pcszNamespaceURI.c_str(), pcszNamespaceURI.length()) == 0) + { + m_envelopeData.setOVFVersion(ovf::OVFVersion_1_0); + } + else + { + m_envelopeData.setOVFVersion(ovf::OVFVersion_0_9); + } + + if ((pTypeAttr = pRootElem->findAttribute("lang", "xml"))) + { + pcszTypeAttr = pTypeAttr->getValueN(RT_XML_ATTR_TINY); + m_envelopeData.lang = pcszTypeAttr; + } + + // OVF has the following rough layout: + /* + -- <References> .... files referenced from other parts of the file, such as VMDK images + -- Metadata, comprised of several section commands + -- virtual machines, either a single <VirtualSystem>, or a <VirtualSystemCollection> + -- optionally <Strings> for localization + */ + + // get all "File" child elements of "References" section so we can look up files easily; + // first find the "References" sections so we can look up files + xml::ElementNodesList listFileElements; // receives all /Envelope/References/File nodes + const xml::ElementNode *pReferencesElem; + if ((pReferencesElem = pRootElem->findChildElement("References"))) + pReferencesElem->getChildElements(listFileElements, "File"); + + // now go though the sections + LoopThruSections(pReferencesElem, pRootElem); +} + +/** + * Private helper method that goes thru the elements of the given "current" element in the OVF XML + * and handles the contained child elements (which can be "Section" or "Content" elements). + * + * @param pReferencesElem "References" element from OVF, for looking up file specifications; + * can be NULL if no such element is present. + * @param pCurElem Element whose children are to be analyzed here. + * @return + */ +void OVFReader::LoopThruSections(const xml::ElementNode *pReferencesElem, + const xml::ElementNode *pCurElem) +{ + xml::NodesLoop loopChildren(*pCurElem); + const xml::ElementNode *pElem; + while ((pElem = loopChildren.forAllNodes())) + { + const char *pcszElemName = pElem->getName(); + const xml::AttributeNode *pTypeAttr = pElem->findAttribute("type"); + const char *pcszTypeAttr = pTypeAttr ? pTypeAttr->getValueN(RT_XML_ATTR_TINY) : ""; + + if ( !strcmp(pcszElemName, "DiskSection") + || ( !strcmp(pcszElemName, "Section") + && !strcmp(pcszTypeAttr, "ovf:DiskSection_Type") + ) + ) + { + HandleDiskSection(pReferencesElem, pElem); + } + else if ( !strcmp(pcszElemName, "NetworkSection") + || ( !strcmp(pcszElemName, "Section") + && !strcmp(pcszTypeAttr, "ovf:NetworkSection_Type") + ) + ) + { + HandleNetworkSection(pElem); + } + else if ( !strcmp(pcszElemName, "DeploymentOptionSection")) + { + /// @todo + } + else if ( !strcmp(pcszElemName, "Info")) + { + // child of VirtualSystemCollection -- TODO + } + else if ( !strcmp(pcszElemName, "ResourceAllocationSection")) + { + // child of VirtualSystemCollection -- TODO + } + else if ( !strcmp(pcszElemName, "StartupSection")) + { + // child of VirtualSystemCollection -- TODO + } + else if ( !strcmp(pcszElemName, "VirtualSystem") + || ( !strcmp(pcszElemName, "Content") + && !strcmp(pcszTypeAttr, "ovf:VirtualSystem_Type") + ) + ) + { + HandleVirtualSystemContent(pElem); + } + else if ( !strcmp(pcszElemName, "VirtualSystemCollection") + || ( !strcmp(pcszElemName, "Content") + && !strcmp(pcszTypeAttr, "ovf:VirtualSystemCollection_Type") + ) + ) + { + /// @todo ResourceAllocationSection + + // recurse for this, since it has VirtualSystem elements as children + LoopThruSections(pReferencesElem, pElem); + } + } +} + +/** + * Private helper method that handles disk sections in the OVF XML. + * + * Gets called indirectly from IAppliance::read(). + * + * @param pReferencesElem "References" element from OVF, for looking up file + * specifications; can be NULL if no such element is + * present. + * @param pSectionElem Section element for which this helper is getting called. + */ +void OVFReader::HandleDiskSection(const xml::ElementNode *pReferencesElem, + const xml::ElementNode *pSectionElem) +{ + // contains "Disk" child elements + xml::NodesLoop loopDisks(*pSectionElem, "Disk"); + const xml::ElementNode *pelmDisk; + while ((pelmDisk = loopDisks.forAllNodes())) + { + DiskImage d; + const char *pcszBad = NULL; + const char *pcszDiskId; + const char *pcszFormat; + if (!pelmDisk->getAttributeValueN("diskId", pcszDiskId, RT_XML_ATTR_TINY)) + pcszBad = "diskId"; + else if (!pelmDisk->getAttributeValueN("format", pcszFormat, RT_XML_ATTR_SMALL)) + pcszBad = "format"; + else if (!pelmDisk->getAttributeValue("capacity", d.iCapacity)) + pcszBad = "capacity"; + else + { + d.strDiskId = pcszDiskId; + d.strFormat = pcszFormat; + + if (!pelmDisk->getAttributeValue("populatedSize", d.iPopulatedSize)) + // optional + d.iPopulatedSize = -1; + + // optional vbox:uuid attribute (if OVF was exported by VirtualBox != 3.2) + pelmDisk->getAttributeValueN("uuid", d.uuidVBox, RT_XML_ATTR_TINY, "vbox"); + + const char *pcszFileRef; + if (pelmDisk->getAttributeValueN("fileRef", pcszFileRef, RT_XML_ATTR_SMALL)) // optional + { + // look up corresponding /References/File nodes (list built above) + const xml::ElementNode *pFileElem; + if ( pReferencesElem + && (pFileElem = pReferencesElem->findChildElementFromId(pcszFileRef)) != NULL + ) + { + + // copy remaining values from file node then + const char *pcszBadInFile = NULL; + const char *pcszHref; + if (!pFileElem->getAttributeValueN("href", pcszHref, RT_XML_ATTR_SMALL)) + pcszBadInFile = "href"; + else if (!pFileElem->getAttributeValue("size", d.iSize)) + d.iSize = -1; // optional + + d.strHref = pcszHref; + + // if (!(pFileElem->getAttributeValue("size", d.iChunkSize))) TODO + d.iChunkSize = -1; // optional + const char *pcszCompression; + if (pFileElem->getAttributeValueN("compression", pcszCompression, RT_XML_ATTR_TINY)) + d.strCompression = pcszCompression; + + if (pcszBadInFile) + throw OVFLogicError(N_("Error reading \"%s\": missing or invalid attribute '%s' in 'File' element, line %d"), + m_strPath.c_str(), + pcszBadInFile, + pFileElem->getLineNumber()); + } + else + throw OVFLogicError(N_("Error reading \"%s\": cannot find References/File element for ID \"%s\" referenced by 'Disk' element, line %d"), + m_strPath.c_str(), + pcszFileRef, + pelmDisk->getLineNumber()); + } + } + + if (pcszBad) + throw OVFLogicError(N_("Error reading \"%s\": missing or invalid attribute '%s' in 'DiskSection' element, line %d"), + m_strPath.c_str(), + pcszBad, + pelmDisk->getLineNumber()); + + // suggest a size in megabytes to help callers with progress reports + d.ulSuggestedSizeMB = 0; + if (d.iCapacity != -1) + d.ulSuggestedSizeMB = (uint32_t)(d.iCapacity / _1M); + else if (d.iPopulatedSize != -1) + d.ulSuggestedSizeMB = (uint32_t)(d.iPopulatedSize / _1M); + else if (d.iSize != -1) + d.ulSuggestedSizeMB = (uint32_t)(d.iSize / _1M); + if (d.ulSuggestedSizeMB == 0) + d.ulSuggestedSizeMB = 10000; // assume 10 GB, this is for the progress bar only anyway + + m_mapDisks[d.strDiskId] = d; + } +} + +/** + * Private helper method that handles network sections in the OVF XML. + * Gets called indirectly from IAppliance::read(). + * + * @return + */ +void OVFReader::HandleNetworkSection(const xml::ElementNode * /* pSectionElem */) +{ + // we ignore network sections for now + +// xml::NodesLoop loopNetworks(*pSectionElem, "Network"); +// const xml::Node *pelmNetwork; +// while ((pelmNetwork = loopNetworks.forAllNodes())) +// { +// Network n; +// if (!(pelmNetwork->getAttributeValue("name", n.strNetworkName))) +// return setError(VBOX_E_FILE_ERROR, +// tr("Error reading \"%s\": missing 'name' attribute in 'Network', line %d"), +// pcszPath, +// pelmNetwork->getLineNumber()); +// +// m->mapNetworks[n.strNetworkName] = n; +// } +} + +/** + * Private helper method that handles a "VirtualSystem" element in the OVF XML. + * Gets called indirectly from IAppliance::read(). + * + * @param pelmVirtualSystem + * @return + */ +void OVFReader::HandleVirtualSystemContent(const xml::ElementNode *pelmVirtualSystem) +{ + /* Create a new virtual system and work directly on the list copy. */ + m_llVirtualSystems.push_back(VirtualSystem()); + VirtualSystem &vsys = m_llVirtualSystems.back(); + + // peek under the <VirtualSystem> node whether we have a <vbox:Machine> node; + // that case case, the caller can completely ignore the OVF but only load the VBox machine XML + vsys.pelmVBoxMachine = pelmVirtualSystem->findChildElementNS("vbox", "Machine"); + + // now look for real OVF + const xml::AttributeNode *pIdAttr = pelmVirtualSystem->findAttribute("id"); + if (pIdAttr) + vsys.strName = pIdAttr->getValueN(RT_XML_ATTR_SMALL); + + xml::NodesLoop loop(*pelmVirtualSystem); // all child elements + const xml::ElementNode *pelmThis; + while ((pelmThis = loop.forAllNodes())) + { + const char *pcszElemName = pelmThis->getName(); + const char *pcszTypeAttr = ""; + if (!strcmp(pcszElemName, "Section")) // OVF 0.9 used "Section" element always with a varying "type" attribute + { + const xml::AttributeNode *pTypeAttr = pelmThis->findAttribute("type"); + if (pTypeAttr) + pcszTypeAttr = pTypeAttr->getValueN(RT_XML_ATTR_TINY); + else + throw OVFLogicError(N_("Error reading \"%s\": element 'Section' has no 'type' attribute, line %d"), + m_strPath.c_str(), + pelmThis->getLineNumber()); + } + + if ( !strcmp(pcszElemName, "EulaSection") + || !strcmp(pcszTypeAttr, "ovf:EulaSection_Type") + ) + { + /* <EulaSection> + <Info ovf:msgid="6">License agreement for the Virtual System.</Info> + <License ovf:msgid="1">License terms can go in here.</License> + </EulaSection> */ + + const xml::ElementNode *pelmLicense; + if ((pelmLicense = pelmThis->findChildElement("License"))) + vsys.strLicenseText = pelmLicense->getValueN(RT_XML_CONTENT_LARGE); + } + if ( !strcmp(pcszElemName, "ProductSection") + || !strcmp(pcszTypeAttr, "ovf:ProductSection_Type") + ) + { + /* <Section ovf:required="false" xsi:type="ovf:ProductSection_Type"> + <Info>Meta-information about the installed software</Info> + <Product>VAtest</Product> + <Vendor>SUN Microsystems</Vendor> + <Version>10.0</Version> + <ProductUrl>http://blogs.sun.com/VirtualGuru</ProductUrl> + <VendorUrl>http://www.sun.com</VendorUrl> + </Section> */ + const xml::ElementNode *pelmProduct; + if ((pelmProduct = pelmThis->findChildElement("Product"))) + vsys.strProduct = pelmProduct->getValueN(RT_XML_CONTENT_SMALL); + const xml::ElementNode *pelmVendor; + if ((pelmVendor = pelmThis->findChildElement("Vendor"))) + vsys.strVendor = pelmVendor->getValueN(RT_XML_CONTENT_SMALL); + const xml::ElementNode *pelmVersion; + if ((pelmVersion = pelmThis->findChildElement("Version"))) + vsys.strVersion = pelmVersion->getValueN(RT_XML_CONTENT_SMALL); + const xml::ElementNode *pelmProductUrl; + if ((pelmProductUrl = pelmThis->findChildElement("ProductUrl"))) + vsys.strProductUrl = pelmProductUrl->getValueN(RT_XML_CONTENT_SMALL); + const xml::ElementNode *pelmVendorUrl; + if ((pelmVendorUrl = pelmThis->findChildElement("VendorUrl"))) + vsys.strVendorUrl = pelmVendorUrl->getValueN(RT_XML_CONTENT_SMALL); + } + else if ( !strcmp(pcszElemName, "VirtualHardwareSection") + || !strcmp(pcszTypeAttr, "ovf:VirtualHardwareSection_Type") + ) + { + const xml::ElementNode *pelmSystem, *pelmVirtualSystemType; + if ((pelmSystem = pelmThis->findChildElement("System"))) + { + /* <System> + <vssd:Description>Description of the virtual hardware section.</vssd:Description> + <vssd:ElementName>vmware</vssd:ElementName> + <vssd:InstanceID>1</vssd:InstanceID> + <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier> + <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType> + </System>*/ + if ((pelmVirtualSystemType = pelmSystem->findChildElement("VirtualSystemType"))) + vsys.strVirtualSystemType = pelmVirtualSystemType->getValueN(RT_XML_CONTENT_SMALL); + } + + /* Parse the items into the hardware item vector. */ + { + std::map<RTCString, const VirtualHardwareItem *> mapHardwareItems; + xml::NodesLoop childrenIterator(*pelmThis); + const xml::ElementNode *pelmItem; + while ((pelmItem = childrenIterator.forAllNodes()) != NULL) + { + /* Parse according to type. */ + VirtualHardwareItem *pItem; + const char *pszName = pelmItem->getName(); + if (RTStrCmp(pszName, "Item") == 0) + pItem = new VirtualHardwareItem(); + else if (RTStrCmp(pszName, "StorageItem") == 0) + pItem = new StorageItem(); + else if (RTStrCmp(pszName, "EthernetPortItem") == 0) + pItem = new EthernetPortItem(); + else + continue; + vsys.vecHardwareItems.push_back(pItem); + pItem->m_iLineNumber = pelmItem->getLineNumber(); + pItem->fillItem(pelmItem); + + /* validate */ + try + { + pItem->checkConsistencyAndCompliance(); + } + catch (OVFLogicError &e) + { + throw OVFLogicError(N_("Error reading \"%s\": \"%s\""), m_strPath.c_str(), e.what()); + } + + /* Add to mapping vector (for parent ID lookups) if it has a valid instance ID. */ + if (!pItem->strInstanceID.isEmpty()) + { + std::map<RTCString, const VirtualHardwareItem *>::const_iterator itDup; + itDup = mapHardwareItems.find(pItem->strInstanceID); + if (itDup == mapHardwareItems.end()) + mapHardwareItems[pItem->strInstanceID] = pItem; + else +#if 1 + LogRel(("OVFREADER: Warning reading \"%s\": Duplicate InstanceID \"%s\" on line %d, previous at %d!\n", + m_strPath.c_str(), pItem->strInstanceID.c_str(), pItem->m_iLineNumber, itDup->second->m_iLineNumber)); +#else + throw OVFLogicError(N_("Error reading \"%s\": Duplicate InstanceID \"%s\" on line %d, previous at %d"), + m_strPath.c_str(), pItem->strInstanceID.c_str(), + pItem->m_iLineNumber, itDup->second->m_iLineNumber); +#endif + } + } + } + + HardDiskController *pPrimaryIDEController = NULL;// will be set once found + + // now go thru all hardware items and handle them according to their type; + // in this first loop we handle all items _except_ hard disk images, + // which we'll handle in a second loop below + HardwareItemVector::const_iterator itH; + for (itH = vsys.vecHardwareItems.begin(); itH != vsys.vecHardwareItems.end(); ++itH) + { + const VirtualHardwareItem &i = **itH; + + // do some analysis + switch (i.resourceType) + { + case ResourceType_Processor: // 3 + /* <rasd:Caption>1 virtual CPU</rasd:Caption> + <rasd:Description>Number of virtual CPUs</rasd:Description> + <rasd:ElementName>virtual CPU</rasd:ElementName> + <rasd:InstanceID>1</rasd:InstanceID> + <rasd:ResourceType>3</rasd:ResourceType> + <rasd:VirtualQuantity>1</rasd:VirtualQuantity>*/ + if (i.ullVirtualQuantity < UINT16_MAX) + vsys.cCPUs = (uint16_t)i.ullVirtualQuantity; + else + throw OVFLogicError(N_("Error reading \"%s\": CPU count %RI64 is larger than %d, line %d"), + m_strPath.c_str(), + i.ullVirtualQuantity, + UINT16_MAX, + i.m_iLineNumber); + break; + + case ResourceType_Memory: // 4 + if ( i.strAllocationUnits == "MegaBytes" // found in OVF created by OVF toolkit + || i.strAllocationUnits == "MB" // found in MS docs + || i.strAllocationUnits == "byte * 2^20" // suggested by OVF spec DSP0243 page 21 + ) + vsys.ullMemorySize = i.ullVirtualQuantity * _1M; + else + throw OVFLogicError(N_("Error reading \"%s\": Invalid allocation unit \"%s\" specified with memory size item, line %d"), + m_strPath.c_str(), + i.strAllocationUnits.c_str(), + i.m_iLineNumber); + break; + + case ResourceType_IDEController: // 5 + { + /* <Item> + <rasd:Caption>ideController0</rasd:Caption> + <rasd:Description>IDE Controller</rasd:Description> + <rasd:InstanceId>5</rasd:InstanceId> + <rasd:ResourceType>5</rasd:ResourceType> + <rasd:Address>0</rasd:Address> + <rasd:BusNumber>0</rasd:BusNumber> + </Item> */ + HardDiskController hdc; + hdc.system = HardDiskController::IDE; + hdc.strIdController = i.strInstanceID; + hdc.strControllerType = i.strResourceSubType; + + hdc.lAddress = i.lAddress; + + if (!pPrimaryIDEController) + // this is the first IDE controller found: then mark it as "primary" + hdc.fPrimary = true; + else + { + // this is the second IDE controller found: If VMware exports two + // IDE controllers, it seems that they are given an "Address" of 0 + // an 1, respectively, so assume address=0 means primary controller + if ( pPrimaryIDEController->lAddress == 0 + && hdc.lAddress == 1 + ) + { + pPrimaryIDEController->fPrimary = true; + hdc.fPrimary = false; + } + else if ( pPrimaryIDEController->lAddress == 1 + && hdc.lAddress == 0 + ) + { + pPrimaryIDEController->fPrimary = false; + hdc.fPrimary = false; + } + else + // then we really can't tell, just hope for the best + hdc.fPrimary = false; + } + + vsys.mapControllers[i.strInstanceID] = hdc; + if (!pPrimaryIDEController) + pPrimaryIDEController = &vsys.mapControllers[i.strInstanceID]; + break; + } + + case ResourceType_ParallelSCSIHBA: // 6 SCSI controller + { + /* <Item> + <rasd:Caption>SCSI Controller 0 - LSI Logic</rasd:Caption> + <rasd:Description>SCI Controller</rasd:Description> + <rasd:ElementName>SCSI controller</rasd:ElementName> + <rasd:InstanceID>4</rasd:InstanceID> + <rasd:ResourceSubType>LsiLogic</rasd:ResourceSubType> + <rasd:ResourceType>6</rasd:ResourceType> + </Item> */ + HardDiskController hdc; + hdc.system = HardDiskController::SCSI; + hdc.strIdController = i.strInstanceID; + hdc.strControllerType = i.strResourceSubType; + + vsys.mapControllers[i.strInstanceID] = hdc; + break; + } + + case ResourceType_EthernetAdapter: // 10 + { + /* <Item> + <rasd:Caption>Ethernet adapter on 'Bridged'</rasd:Caption> + <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation> + <rasd:Connection>Bridged</rasd:Connection> + <rasd:InstanceID>6</rasd:InstanceID> + <rasd:ResourceType>10</rasd:ResourceType> + <rasd:ResourceSubType>E1000</rasd:ResourceSubType> + </Item> + + OVF spec DSP 0243 page 21: + "For an Ethernet adapter, this specifies the abstract network connection name + for the virtual machine. All Ethernet adapters that specify the same abstract + network connection name within an OVF package shall be deployed on the same + network. The abstract network connection name shall be listed in the NetworkSection + at the outermost envelope level." */ + + // only store the name + EthernetAdapter ea; + ea.strAdapterType = i.strResourceSubType; + ea.strNetworkName = i.strConnection; + vsys.llEthernetAdapters.push_back(ea); + break; + } + + case ResourceType_FloppyDrive: // 14 + vsys.fHasFloppyDrive = true; // we have no additional information + break; + + case ResourceType_CDDrive: // 15 + /* <Item ovf:required="false"> + <rasd:Caption>cdrom1</rasd:Caption> + <rasd:InstanceId>7</rasd:InstanceId> + <rasd:ResourceType>15</rasd:ResourceType> + <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation> + <rasd:Parent>5</rasd:Parent> + <rasd:AddressOnParent>0</rasd:AddressOnParent> + </Item> */ + // I tried to see what happens if I set an ISO for the CD-ROM in VMware Workstation, + // but then the ovftool dies with "Device backing not supported". So I guess if + // VMware can't export ISOs, then we don't need to be able to import them right now. + vsys.fHasCdromDrive = true; // we have no additional information + break; + + case ResourceType_HardDisk: // 17 + // handled separately in second loop below + break; + + case ResourceType_OtherStorageDevice: // 20 SATA controller + { + /* <Item> + <rasd:Description>SATA Controller</rasd:Description> + <rasd:Caption>sataController0</rasd:Caption> + <rasd:InstanceID>4</rasd:InstanceID> + <rasd:ResourceType>20</rasd:ResourceType> + <rasd:ResourceSubType>AHCI</rasd:ResourceSubType> + <rasd:Address>0</rasd:Address> + <rasd:BusNumber>0</rasd:BusNumber> + </Item> */ + if ( i.strResourceSubType.compare("AHCI", RTCString::CaseInsensitive) == 0 + || i.strResourceSubType.compare("vmware.sata.ahci", RTCString::CaseInsensitive) == 0) + { + HardDiskController hdc; + hdc.system = HardDiskController::SATA; + hdc.strIdController = i.strInstanceID; + hdc.strControllerType = i.strResourceSubType; + + vsys.mapControllers[i.strInstanceID] = hdc; + } + else if ( i.strResourceSubType.compare("VirtioSCSI", RTCString::CaseInsensitive) == 0 + || i.strResourceSubType.compare("virtio-scsi", RTCString::CaseInsensitive) == 0 ) + { + HardDiskController hdc; + hdc.system = HardDiskController::VIRTIOSCSI; /**< r=klaus: GUI needs to learn about this in the import dialog, currently shown as "Unknown Hardware Item". */ + hdc.strIdController = i.strInstanceID; + //<rasd:ResourceSubType>VirtioSCSI</rasd:ResourceSubType> + hdc.strControllerType = i.strResourceSubType; + vsys.mapControllers[i.strInstanceID] = hdc; + } + else + throw OVFLogicError(N_("Error reading \"%s\": Host resource of type \"Other Storage Device (%d)\" is supported with SATA AHCI or virtio-scsi controllers only, line %d (subtype:%s)"), + m_strPath.c_str(), + ResourceType_OtherStorageDevice, + i.m_iLineNumber, i.strResourceSubType.c_str() ); + break; + } + + case ResourceType_USBController: // 23 + /* <Item ovf:required="false"> + <rasd:Caption>usb</rasd:Caption> + <rasd:Description>USB Controller</rasd:Description> + <rasd:InstanceId>3</rasd:InstanceId> + <rasd:ResourceType>23</rasd:ResourceType> + <rasd:Address>0</rasd:Address> + <rasd:BusNumber>0</rasd:BusNumber> + </Item> */ + vsys.fHasUsbController = true; // we have no additional information + break; + + case ResourceType_SoundCard: // 35 + /* <Item ovf:required="false"> + <rasd:Caption>sound</rasd:Caption> + <rasd:Description>Sound Card</rasd:Description> + <rasd:InstanceId>10</rasd:InstanceId> + <rasd:ResourceType>35</rasd:ResourceType> + <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType> + <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation> + <rasd:AddressOnParent>3</rasd:AddressOnParent> + </Item> */ + vsys.strSoundCardType = i.strResourceSubType; + break; + + default: + { + /* If this unknown resource type isn't required, we simply skip it. */ + if (i.fResourceRequired) + { + throw OVFLogicError(N_("Error reading \"%s\": Unknown resource type %d in hardware item, line %d"), + m_strPath.c_str(), + i.resourceType, + i.m_iLineNumber); + } + } + } // end switch + } + + // now run through the items for a second time, but handle only + // hard disk images; otherwise the code would fail if a hard + // disk image appears in the OVF before its hard disk controller + for (itH = vsys.vecHardwareItems.begin(); itH != vsys.vecHardwareItems.end(); ++itH) + { + const VirtualHardwareItem &i = **itH; + + // do some analysis + switch (i.resourceType) + { + case ResourceType_CDDrive: // 15 + /* <Item ovf:required="false"> + <rasd:Caption>cdrom1</rasd:Caption> + <rasd:InstanceId>7</rasd:InstanceId> + <rasd:ResourceType>15</rasd:ResourceType> + <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation> + <rasd:Parent>5</rasd:Parent> + <rasd:AddressOnParent>0</rasd:AddressOnParent> + </Item> */ + case ResourceType_HardDisk: // 17 + { + /* <Item> + <rasd:Caption>Harddisk 1</rasd:Caption> + <rasd:Description>HD</rasd:Description> + <rasd:ElementName>Hard Disk</rasd:ElementName> + <rasd:HostResource>ovf://disk/lamp</rasd:HostResource> + <rasd:InstanceID>5</rasd:InstanceID> + <rasd:Parent>4</rasd:Parent> + <rasd:ResourceType>17</rasd:ResourceType> + </Item> */ + + // look up the hard disk controller element whose InstanceID equals our Parent; + // this is how the connection is specified in OVF + ControllersMap::const_iterator it = vsys.mapControllers.find(i.strParent); + if (it == vsys.mapControllers.end()) + throw OVFLogicError(N_("Error reading \"%s\": Disk item with instance ID \"%s\" specifies invalid parent \"%s\", line %d"), + m_strPath.c_str(), + i.strInstanceID.c_str(), + i.strParent.c_str(), + i.m_iLineNumber); + + VirtualDisk vd; + vd.strIdController = i.strParent; + i.strAddressOnParent.toInt(vd.ulAddressOnParent); + // ovf://disk/lamp + // 123456789012345 + if (i.strHostResource.startsWith("ovf://disk/")) + vd.strDiskId = i.strHostResource.substr(11); + else if (i.strHostResource.startsWith("ovf:/disk/")) + vd.strDiskId = i.strHostResource.substr(10); + else if (i.strHostResource.startsWith("/disk/")) + vd.strDiskId = i.strHostResource.substr(6); + + //the error may be missed for CD, because CD can be empty + if ((vd.strDiskId.isEmpty() || (m_mapDisks.find(vd.strDiskId) == m_mapDisks.end())) + && i.resourceType == ResourceType_HardDisk) + throw OVFLogicError(N_("Error reading \"%s\": Disk item with instance ID \"%s\" specifies invalid host resource \"%s\", line %d"), + m_strPath.c_str(), + i.strInstanceID.c_str(), + i.strHostResource.c_str(), + i.m_iLineNumber); + + vsys.mapVirtualDisks[vd.strDiskId] = vd; + break; + } + default: + break; + } + } + } + else if ( !strcmp(pcszElemName, "OperatingSystemSection") + || !strcmp(pcszTypeAttr, "ovf:OperatingSystemSection_Type") + ) + { + uint64_t cimos64; + if (!(pelmThis->getAttributeValue("id", cimos64))) + throw OVFLogicError(N_("Error reading \"%s\": missing or invalid 'ovf:id' attribute in operating system section element, line %d"), + m_strPath.c_str(), + pelmThis->getLineNumber()); + + vsys.cimos = (CIMOSType_T)cimos64; + const xml::ElementNode *pelmCIMOSDescription; + if ((pelmCIMOSDescription = pelmThis->findChildElement("Description"))) + vsys.strCimosDesc = pelmCIMOSDescription->getValueN(RT_XML_CONTENT_SMALL); + + const xml::ElementNode *pelmVBoxOSType; + if ((pelmVBoxOSType = pelmThis->findChildElementNS("vbox", // namespace + "OSType"))) // element name + vsys.strTypeVBox = pelmVBoxOSType->getValueN(RT_XML_CONTENT_SMALL); + } + else if ( (!strcmp(pcszElemName, "AnnotationSection")) + || (!strcmp(pcszTypeAttr, "ovf:AnnotationSection_Type")) + ) + { + const xml::ElementNode *pelmAnnotation; + if ((pelmAnnotation = pelmThis->findChildElement("Annotation"))) + vsys.strDescription = pelmAnnotation->getValueN(RT_XML_CONTENT_SMALL); + } + } +} + +void VirtualHardwareItem::fillItem(const xml::ElementNode *item) +{ + xml::NodesLoop loopItemChildren(*item);// all child elements + const xml::ElementNode *pelmItemChild; + while ((pelmItemChild = loopItemChildren.forAllNodes())) + { + const char *pcszItemChildName = pelmItemChild->getName(); + if (!strcmp(pcszItemChildName, "Description")) + strDescription = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if (!strcmp(pcszItemChildName, "Caption")) + strCaption = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if (!strcmp(pcszItemChildName, "ElementName")) + strElementName = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if ( !strcmp(pcszItemChildName, "InstanceID") + || !strcmp(pcszItemChildName, "InstanceId") ) + strInstanceID = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if (!strcmp(pcszItemChildName, "HostResource")) + strHostResource = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if (!strcmp(pcszItemChildName, "ResourceType")) + { + uint32_t ulType; + pelmItemChild->copyValue(ulType); + if (ulType > 0xffff) + ulType = 0xffff; + resourceType = (ResourceType_T)ulType; + fResourceRequired = true; + const char *pcszAttValue; + if (item->getAttributeValueN("required", pcszAttValue, RT_XML_ATTR_TINY)) + { + if (!strcmp(pcszAttValue, "false")) + fResourceRequired = false; + } + } + else if (!strcmp(pcszItemChildName, "OtherResourceType")) + strOtherResourceType = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if (!strcmp(pcszItemChildName, "ResourceSubType")) + strResourceSubType = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if (!strcmp(pcszItemChildName, "AutomaticAllocation")) + fAutomaticAllocation = (!strcmp(pelmItemChild->getValueN(RT_XML_CONTENT_SMALL), "true")) ? true : false; + else if (!strcmp(pcszItemChildName, "AutomaticDeallocation")) + fAutomaticDeallocation = (!strcmp(pelmItemChild->getValueN(RT_XML_CONTENT_SMALL), "true")) ? true : false; + else if (!strcmp(pcszItemChildName, "Parent")) + strParent = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if (!strcmp(pcszItemChildName, "Connection")) + strConnection = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if (!strcmp(pcszItemChildName, "Address")) + { + strAddress = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + pelmItemChild->copyValue(lAddress); + } + else if (!strcmp(pcszItemChildName, "AddressOnParent")) + strAddressOnParent = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if (!strcmp(pcszItemChildName, "AllocationUnits")) + strAllocationUnits = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if (!strcmp(pcszItemChildName, "VirtualQuantity")) + pelmItemChild->copyValue(ullVirtualQuantity); + else if (!strcmp(pcszItemChildName, "Reservation")) + pelmItemChild->copyValue(ullReservation); + else if (!strcmp(pcszItemChildName, "Limit")) + pelmItemChild->copyValue(ullLimit); + else if (!strcmp(pcszItemChildName, "Weight")) + pelmItemChild->copyValue(ullWeight); + else if (!strcmp(pcszItemChildName, "ConsumerVisibility")) + strConsumerVisibility = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if (!strcmp(pcszItemChildName, "MappingBehavior")) + strMappingBehavior = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if (!strcmp(pcszItemChildName, "PoolID")) + strPoolID = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if (!strcmp(pcszItemChildName, "BusNumber")) + pelmItemChild->copyValue(ulBusNumber); +// else if (pelmItemChild->getPrefix() == NULL +// || strcmp(pelmItemChild->getPrefix(), "vmw")) +// throw OVFLogicError(N_("Unknown element '%s' under Item element, line %d"), +// pcszItemChildName, +// m_iLineNumber); + } +} + +void VirtualHardwareItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError) +{ + RTCString name = getItemName(); + if (resourceType == 0) + throw OVFLogicError(N_("Empty element ResourceType under %s element, line %d. see DMTF Schema Documentation %s"), + name.c_str(), m_iLineNumber, DTMF_SPECS_URI); + + /* Don't be too uptight about the strInstanceID value. There are OVAs out + there which have InstanceID="%iid%" for memory for instance, which is + no good reason for not being able to process them. bugref:8997 */ + if (strInstanceID.isEmpty()) + { + if ( resourceType == ResourceType_IDEController + || resourceType == ResourceType_OtherStorageDevice + || resourceType == ResourceType_ParallelSCSIHBA + || resourceType == ResourceType_iSCSIHBA //?? + || resourceType == ResourceType_IBHCA ) //?? + throw OVFLogicError(N_("Element InstanceID is absent under %s element, line %d. see DMTF Schema Documentation %s"), + name.c_str(), m_iLineNumber, DTMF_SPECS_URI); + else + LogRel(("OVFREADER: Warning: Ignoring missing or invalid InstanceID under element %s, line %u\n", + name.c_str(), m_iLineNumber)); + } +} + +void StorageItem::fillItem(const xml::ElementNode *item) +{ + VirtualHardwareItem::fillItem(item); + + xml::NodesLoop loopItemChildren(*item);// all child elements + const xml::ElementNode *pelmItemChild; + while ((pelmItemChild = loopItemChildren.forAllNodes())) + { + const char *pcszItemChildName = pelmItemChild->getName(); + if (!strcmp(pcszItemChildName, "HostExtentName")) + strHostExtentName = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if (!strcmp(pcszItemChildName, "OtherHostExtentNameFormat")) + strOtherHostExtentNameFormat = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if (!strcmp(pcszItemChildName, "OtherHostExtentNameNamespace")) + strOtherHostExtentNameNamespace = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if (!strcmp(pcszItemChildName, "VirtualQuantityUnits")) + strVirtualQuantityUnits = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL); + else if (!strcmp(pcszItemChildName, "Access")) + { + uint32_t temp; + pelmItemChild->copyValue(temp); + accessType = (StorageAccessType_T)temp; + } + else if (!strcmp(pcszItemChildName, "HostExtentNameFormat")) + { + } + else if (!strcmp(pcszItemChildName, "HostExtentNameNamespace")) + { + } + else if (!strcmp(pcszItemChildName, "HostExtentStartingAddress")) + { + } + else if (!strcmp(pcszItemChildName, "HostResourceBlockSize")) + { + int64_t temp; + pelmItemChild->copyValue(temp); + hostResourceBlockSize = temp; + } + else if (!strcmp(pcszItemChildName, "Limit")) + { + int64_t temp; + pelmItemChild->copyValue(temp); + limit = temp; + } + else if (!strcmp(pcszItemChildName, "Reservation")) + { + int64_t temp; + pelmItemChild->copyValue(temp); + reservation = temp; + } + else if (!strcmp(pcszItemChildName, "VirtualQuantity")) + { + int64_t temp; + pelmItemChild->copyValue(temp); + virtualQuantity = temp; + } + else if (!strcmp(pcszItemChildName, "VirtualResourceBlockSize")) + { + int64_t temp; + pelmItemChild->copyValue(temp); + virtualResourceBlockSize = temp; + } + } +} + + +void StorageItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError) +{ + VirtualHardwareItem::_checkConsistencyAndCompliance(); + + RTCString name = getItemName(); + + if (accessType == StorageAccessType_Unknown) + { + //throw OVFLogicError(N_("Access type is unknown under %s element, line %d"), + // name.c_str(), m_iLineNumber); + } + + if (hostResourceBlockSize <= 0 && reservation > 0) + { + throw OVFLogicError(N_("Element HostResourceBlockSize is absent under %s element, line %d. " + "see DMTF Schema Documentation %s"), + name.c_str(), m_iLineNumber, DTMF_SPECS_URI); + } + + if (virtualResourceBlockSize <= 0 && virtualQuantity > 0) + { + throw OVFLogicError(N_("Element VirtualResourceBlockSize is absent under %s element, line %d. " + "see DMTF Schema Documentation %s"), + name.c_str(), m_iLineNumber, DTMF_SPECS_URI); + } + + if (virtualQuantity > 0 && strVirtualQuantityUnits.isEmpty()) + { + throw OVFLogicError(N_("Element VirtualQuantityUnits is absent under %s element, line %d. " + "see DMTF Schema Documentation %s"), + name.c_str(), m_iLineNumber, DTMF_SPECS_URI); + } + + if (virtualResourceBlockSize <= 1 && + strVirtualQuantityUnits.compare(RTCString("count"), RTCString::CaseInsensitive) == 0 + ) + { + throw OVFLogicError(N_("Element VirtualQuantityUnits is set to \"count\" " + "while VirtualResourceBlockSize is set to 1. " + "under %s element, line %d. " + "It's needed to change on \"byte\". " + "see DMTF Schema Documentation %s"), + name.c_str(), m_iLineNumber, DTMF_SPECS_URI); + } +} + +void EthernetPortItem::fillItem(const xml::ElementNode *item) +{ + VirtualHardwareItem::fillItem(item); + + xml::NodesLoop loopItemChildren(*item);// all child elements + const xml::ElementNode *pelmItemChild; + while ((pelmItemChild = loopItemChildren.forAllNodes())) + { + } +} + +void EthernetPortItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError) +{ + VirtualHardwareItem::_checkConsistencyAndCompliance(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Errors +// +//////////////////////////////////////////////////////////////////////////////// + +OVFLogicError::OVFLogicError(const char *aFormat, ...) +{ + char *pszNewMsg; + va_list args; + va_start(args, aFormat); + RTStrAPrintfV(&pszNewMsg, aFormat, args); + setWhat(pszNewMsg); + RTStrFree(pszNewMsg); + va_end(args); +} diff --git a/src/VBox/Main/xml/samples/VirtualBox-global.xml b/src/VBox/Main/xml/samples/VirtualBox-global.xml new file mode 100644 index 00000000..139a47ce --- /dev/null +++ b/src/VBox/Main/xml/samples/VirtualBox-global.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no" ?> +<!-- Oracle VM VirtualBox Sample Global Configuration --> +<!-- + * :tabSize=2:indentSize=2:noTabs=true: + * :folding=explicit:collapseFolds=1: +--> + +<VirtualBox + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.innotek.de/VirtualBox-settings ../VirtualBox-settings-windows.xsd" + xmlns="http://www.innotek.de/VirtualBox-settings" + version="1.2-windows" +> + + <Global> + <ExtraData> + <ExtraDataItem name="GUI/Input/HostKey" value="165"/> + <ExtraDataItem name="GUI/Input/AutoCapture" value="true"/> + <ExtraDataItem name="GUI/SuppressMessages" value="remindAboutAutoCapture,remindAboutMouseIntegrationOn"/> + </ExtraData> + <MachineRegistry> + <MachineEntry src="Machines\{1a4d3cff-e693-4ae5-afaf-a1ccc6882183}.xml" uuid="{1a4d3cff-e693-4ae5-afaf-a1ccc6882183}"/> + <MachineEntry src="Machines\{aa08aecf-e67d-46b9-a325-948691660689}.xml" uuid="{aa08aecf-e67d-46b9-a325-948691660689}"/> + <MachineEntry src="Machines/%7B81ae80b4-2507-4fe8-bed8-33f14920d301%7D.xml" uuid="{81ae80b4-2507-4fe8-bed8-33f14920d301}"/> + </MachineRegistry> + <DiskRegistry> + <HardDisks> + <HardDisk uuid="{23dbf22e-9f27-49da-bdae-f4c43f968057}" type="normal"> + <VirtualDiskImage filePath="E:/develop/innotek/images/nt4ger.vdi"/> + <DiffHardDisk uuid="{23dbf22e-9f27-49da-bdae-f4c43f968057}"> + <VirtualDiskImage filePath="E:/develop/innotek/images/diff1.vdi"/> + </DiffHardDisk> + <DiffHardDisk uuid="{23dbf22e-9f27-49da-bdae-f4c43f968057}"> + <VirtualDiskImage filePath="E:/develop/innotek/images/diff2.vdi"/> + <DiffHardDisk uuid="{23dbf22e-9f27-49da-bdae-f4c43f968057}"> + <VirtualDiskImage filePath="E:/develop/innotek/images/diff2.vdi"/> + </DiffHardDisk> + </DiffHardDisk> + </HardDisk> + <HardDisk uuid="{23dbf22e-9f27-49da-bdae-f4c43f968058}" type="immutable"> + <ISCSIHardDisk server="abc.com" target="drive"/> + </HardDisk> + <HardDisk uuid="{23dbf22e-9f27-49da-bdae-f4c43f968059}" type="writethrough"> + <PhysicalVolume path="D:"/> + </HardDisk> + </HardDisks> + <DVDImages> + <Image src="e:/Develop/innotek/images/fdbootcd.iso" uuid="{8c307b77-f331-45fd-b8b4-cbc5281bf0FF}"/> + </DVDImages> + <FloppyImages> + </FloppyImages> + </DiskRegistry> + <SystemProperties defaultVDIFolder="somefolder"/> + <USBDeviceFilters> + <DeviceFilter name="Test1" active="false" action="Ignore"/> + <DeviceFilter name="Test2" active="false" vendorid="MegaCorpLTD" action="Ignore"/> + </USBDeviceFilters> + </Global> + +</VirtualBox> + + diff --git a/src/VBox/Main/xml/samples/VirtualBox-machine-linux.xml b/src/VBox/Main/xml/samples/VirtualBox-machine-linux.xml new file mode 100644 index 00000000..8b67af38 --- /dev/null +++ b/src/VBox/Main/xml/samples/VirtualBox-machine-linux.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no" ?> +<!-- Oracle VM VirtualBox Sample Machine Configuration (for a linux host) --> +<!-- + * :tabSize=2:indentSize=2:noTabs=true: + * :folding=explicit:collapseFolds=1: +--> + +<VirtualBox + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.innotek.de/VirtualBox-settings ../VirtualBox-settings-linux.xsd" + xmlns="http://www.innotek.de/VirtualBox-settings" + version="1.2-linux" +> + + <Machine + name="dos" OSType="dos" uuid="{1a4d3cff-e693-4ae5-afaf-a1ccc6882183}" + stateFile="aaa.sav" + > + + <HardDiskAttachments> + <HardDiskAttachment hardDisk="{508b474f-62ce-4872-9a42-3af09037a277}" + bus="ide0" device="master"/> + </HardDiskAttachments> + + <Hardware> + <Memory RAMSize="128"/> + <Boot> + <Order device="None" position="4"/> + <Order device="None" position="2"/> + <Order device="HardDisk" position="1"/> + <Order device="Floppy" position="3"/> + </Boot> + <Display VRAMSize="4"/> + <RemoteDisplay enabled="false"/> + <BIOS> + <ACPI enabled="false"/> + <Logo displayTime="0" fadeIn="true" fadeOut="true"/> + </BIOS> + <DVDDrive> + <Image uuid="{00000000-0000-0000-0100-000000000000}"/> + </DVDDrive> + <FloppyDrive> + <HostDrive src="aaa"/> + </FloppyDrive> + <USBController enabled="false"/> + <Network> + <Adapter slot="0" enabled="true" cable="true" MACAddress="08002751DD5E"> + <NAT/> + </Adapter> + <Adapter slot="1" enabled="true" cable="false" MACAddress="08002751DD5E"> + <HostInterface/> + </Adapter> + <Adapter slot="2" enabled="true" cable="false" MACAddress="08002751DD5E"/> + <Adapter slot="3" enabled="true" cable="false" MACAddress="08002751DD5E"/> + </Network> + <AudioAdapter driver="alsa" enabled="true"/> + <SharedFolders> + <SharedFolder name="aaa" hostPath="/bbb/ccc"/> + <SharedFolder name="aaab" hostPath="/bbb/ccc"/> + </SharedFolders> + </Hardware> + + </Machine> + +</VirtualBox> diff --git a/src/VBox/Main/xml/samples/VirtualBox-machine-windows.xml b/src/VBox/Main/xml/samples/VirtualBox-machine-windows.xml new file mode 100644 index 00000000..729a9357 --- /dev/null +++ b/src/VBox/Main/xml/samples/VirtualBox-machine-windows.xml @@ -0,0 +1,203 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no" ?> +<!-- Oracle VM VirtualBox Sample Machine Configuration (for a win32 host) --> +<!-- + * :tabSize=2:indentSize=2:noTabs=true: + * :folding=explicit:collapseFolds=1: +--> + +<VirtualBox + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.innotek.de/VirtualBox-settings ../VirtualBox-settings-windows.xsd" + xmlns="http://www.innotek.de/VirtualBox-settings" + version="1.2-windows" +> + + <Machine + name="dos" OSType="dos" uuid="{00000000-0000-0000-0100-000000000000}" + currentSnapshot="{00000000-0000-0000-0111-000000000000}" + snapshotFolder="." + lastStateChange="2005-01-01T10:22:22Z" + aborted="true" + > + <Description> + Something is here. + </Description> + <Hardware> + <Memory RAMSize="128"/> + <Boot> + <Order device="None" position="1"/> + <Order device="HardDisk" position="2"/> + </Boot> + <Display VRAMSize="4"/> + <RemoteDisplay enabled="false"/> + <BIOS> + <ACPI enabled="false"/> + <Logo displayTime="0" fadeIn="true" fadeOut="true"/> + </BIOS> + <DVDDrive> + <Image uuid="{00000000-0000-0000-0100-000000000000}"/> + </DVDDrive> + <FloppyDrive> + <HostDrive src="aaa"/> + </FloppyDrive> + <USBController enabled="false"> + <DeviceFilter name="Test1" active="false"/> + </USBController> + <Network> + <Adapter slot="0" enabled="false" cable="false" MACAddress="08002751DD5E"/> + <Adapter slot="3" enabled="false" cable="false" MACAddress="08002751DD5E"> + <NAT/> + </Adapter> + <Adapter slot="2" enabled="false" cable="false" MACAddress="08002751DD5E"> + <HostInterface name="Home LAN"/> + </Adapter> + </Network> + <AudioAdapter driver="winmm" enabled="true"/> + </Hardware> + + <HardDiskAttachments> + <HardDiskAttachment hardDisk="{508b474f-62ce-4872-9a42-3af09037a277}" + bus="ide0" device="master"/> + </HardDiskAttachments> + + <ExtraData> + <ExtraDataItem name="GUI/LastCloseAction" value="powerOff"/> + </ExtraData> + + <Snapshot + name="Snapshot 1" uuid="{00000000-0000-0000-0111-000000000000}" + timeStamp="2006-01-01T00:00:00Z" + > + <Hardware> + <Memory RAMSize="128"/> + <Boot> + <Order device="None" position="1"/> + <Order device="HardDisk" position="2"/> + </Boot> + <Display VRAMSize="4"/> + <RemoteDisplay enabled="false"/> + <BIOS> + <ACPI enabled="false"/> + <Logo displayTime="0" fadeIn="true" fadeOut="true"/> + </BIOS> + <DVDDrive> + <Image uuid="{00000000-0000-0000-0100-000000000000}"/> + </DVDDrive> + <FloppyDrive> + <HostDrive src="aaa"/> + </FloppyDrive> + <USBController enabled="false"> + <DeviceFilter name="Test1" active="false"/> + </USBController> + <Network> + <Adapter slot="0" enabled="false" cable="false" MACAddress="08002751DD5E"/> + <Adapter slot="3" enabled="false" cable="false" MACAddress="08002751DD5E"> + <NAT/> + </Adapter> + <Adapter slot="2" enabled="false" cable="false" MACAddress="08002751DD5E"> + <HostInterface name="Home LAN"/> + </Adapter> + </Network> + <AudioAdapter driver="winmm" enabled="true"/> + <Clipboard mode="Disabled"/> + </Hardware> + <HardDiskAttachments> + <HardDiskAttachment hardDisk="{508b474f-62ce-4872-9a42-3af09037a277}" + bus="ide0" device="master"/> + </HardDiskAttachments> + + <Snapshots> + + <Snapshot + name="Snapshot 2" uuid="{00000000-0000-0000-0100-000000000000}" + timeStamp="2006-01-01T00:00:00Z" + stateFile="D:\aaa\bbb.sav" + > + <Description>ewfqwef</Description> + <Hardware> + <Memory RAMSize="128"/> + <Boot> + <Order device="None" position="1"/> + <Order device="HardDisk" position="2"/> + </Boot> + <Display VRAMSize="4"/> + <RemoteDisplay enabled="false"/> + <BIOS> + <ACPI enabled="false"/> + <Logo displayTime="0" fadeIn="true" fadeOut="true"/> + </BIOS> + <DVDDrive> + <Image uuid="{00000000-0000-0000-0100-000000000000}"/> + </DVDDrive> + <FloppyDrive> + <HostDrive src="aaa"/> + </FloppyDrive> + <USBController enabled="false"> + <DeviceFilter name="Test1" active="false"/> + </USBController> + <Network> + <Adapter slot="0" enabled="false" cable="false" MACAddress="08002751DD5E"/> + <Adapter slot="3" enabled="false" cable="false" MACAddress="08002751DD5E"> + <NAT/> + </Adapter> + <Adapter slot="2" enabled="false" cable="false" MACAddress="08002751DD5E"> + <HostInterface name="Home LAN"/> + </Adapter> + </Network> + <AudioAdapter driver="winmm" enabled="true"/> + </Hardware> + <HardDiskAttachments> + <HardDiskAttachment hardDisk="{508b474f-62ce-4872-9a42-3af09037a277}" + bus="ide0" device="master"/> + </HardDiskAttachments> + <Snapshots/> + </Snapshot> + + <Snapshot + name="Snapshot 3" uuid="{00000000-0000-0000-0112-000000000000}" + timeStamp="2006-01-01T00:00:00Z" + > + <Hardware> + <Memory RAMSize="128"/> + <Boot> + <Order device="None" position="1"/> + <Order device="HardDisk" position="2"/> + </Boot> + <Display VRAMSize="4"/> + <RemoteDisplay enabled="false"/> + <BIOS> + <ACPI enabled="false"/> + <Logo displayTime="0" fadeIn="true" fadeOut="true"/> + </BIOS> + <DVDDrive> + <Image uuid="{00000000-0000-0000-0100-000000000000}"/> + </DVDDrive> + <FloppyDrive> + <HostDrive src="aaa"/> + </FloppyDrive> + <USBController enabled="false"> + <DeviceFilter name="Test1" active="false"/> + </USBController> + <Network> + <Adapter slot="0" enabled="false" cable="false" MACAddress="08002751DD5E"/> + <Adapter slot="3" enabled="false" cable="false" MACAddress="08002751DD5E"> + <NAT/> + </Adapter> + <Adapter slot="2" enabled="false" cable="false" MACAddress="08002751DD5E"> + <HostInterface name="Home LAN"/> + </Adapter> + </Network> + <AudioAdapter driver="winmm" enabled="true"/> + </Hardware> + <HardDiskAttachments> + <HardDiskAttachment hardDisk="{508b474f-62ce-4872-9a42-3af09037a277}" + bus="ide0" device="master"/> + </HardDiskAttachments> + <Snapshots/> + </Snapshot> + + </Snapshots> + </Snapshot> + </Machine> + +</VirtualBox> |