summaryrefslogtreecommitdiffstats
path: root/src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp')
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp2597
1 files changed, 2597 insertions, 0 deletions
diff --git a/src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp b/src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp
new file mode 100644
index 00000000..9beaf9bc
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp
@@ -0,0 +1,2597 @@
+/* $Id: VBoxInternalManage.cpp $ */
+/** @file
+ * VBoxManage - The 'internalcommands' command.
+ *
+ * VBoxInternalManage used to be a second CLI for doing special tricks,
+ * not intended for general usage, only for assisting VBox developers.
+ * It is now integrated into VBoxManage.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/com/com.h>
+#include <VBox/com/string.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+
+#include <VBox/com/VirtualBox.h>
+
+#include <VBox/vd.h>
+#include <VBox/sup.h>
+#include <VBox/log.h>
+
+#include <iprt/ctype.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+#include <iprt/sha.h>
+
+#include "VBoxManage.h"
+
+/* Includes for the raw disk stuff. */
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+# include <winioctl.h>
+#elif defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) \
+ || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
+# include <errno.h>
+# include <sys/ioctl.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <fcntl.h>
+# include <unistd.h>
+#endif
+#ifdef RT_OS_LINUX
+# include <sys/utsname.h>
+# include <linux/hdreg.h>
+# include <linux/fs.h>
+# include <stdlib.h> /* atoi() */
+#endif /* RT_OS_LINUX */
+#ifdef RT_OS_DARWIN
+# include <sys/disk.h>
+#endif /* RT_OS_DARWIN */
+#ifdef RT_OS_SOLARIS
+# include <stropts.h>
+# include <sys/dkio.h>
+# include <sys/vtoc.h>
+#endif /* RT_OS_SOLARIS */
+#ifdef RT_OS_FREEBSD
+# include <sys/disk.h>
+#endif /* RT_OS_FREEBSD */
+
+using namespace com;
+
+
+/** Macro for checking whether a partition is of extended type or not. */
+#define PARTTYPE_IS_EXTENDED(x) ((x) == 0x05 || (x) == 0x0f || (x) == 0x85)
+
+/** Maximum number of partitions we can deal with.
+ * Ridiculously large number, but the memory consumption is rather low so who
+ * cares about never using most entries. */
+#define HOSTPARTITION_MAX 100
+
+
+typedef struct HOSTPARTITION
+{
+ /** partition number */
+ unsigned uIndex;
+ /** partition number (internal only, windows specific numbering) */
+ unsigned uIndexWin;
+ /** partition type */
+ unsigned uType;
+ /** CHS/cylinder of the first sector */
+ unsigned uStartCylinder;
+ /** CHS/head of the first sector */
+ unsigned uStartHead;
+ /** CHS/head of the first sector */
+ unsigned uStartSector;
+ /** CHS/cylinder of the last sector */
+ unsigned uEndCylinder;
+ /** CHS/head of the last sector */
+ unsigned uEndHead;
+ /** CHS/sector of the last sector */
+ unsigned uEndSector;
+ /** start sector of this partition relative to the beginning of the hard
+ * disk or relative to the beginning of the extended partition table */
+ uint64_t uStart;
+ /** numer of sectors of the partition */
+ uint64_t uSize;
+ /** start sector of this partition _table_ */
+ uint64_t uPartDataStart;
+ /** numer of sectors of this partition _table_ */
+ uint64_t cPartDataSectors;
+} HOSTPARTITION, *PHOSTPARTITION;
+
+typedef struct HOSTPARTITIONS
+{
+ /** partitioning type - MBR or GPT */
+ VDISKPARTTYPE uPartitioningType;
+ unsigned cPartitions;
+ HOSTPARTITION aPartitions[HOSTPARTITION_MAX];
+} HOSTPARTITIONS, *PHOSTPARTITIONS;
+
+/** flag whether we're in internal mode */
+bool g_fInternalMode;
+
+/**
+ * Print the usage info.
+ */
+void printUsageInternal(USAGECATEGORY u64Cmd, PRTSTREAM pStrm)
+{
+ RTStrmPrintf(pStrm,
+ "Usage: VBoxManage internalcommands <command> [command arguments]\n"
+ "\n"
+ "Commands:\n"
+ "\n"
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"
+ "WARNING: This is a development tool and shall only be used to analyse\n"
+ " problems. It is completely unsupported and will change in\n"
+ " incompatible ways without warning.\n",
+
+ (u64Cmd & USAGE_LOADMAP)
+ ? " loadmap <vmname|uuid> <symfile> <address> [module] [subtrahend] [segment]\n"
+ " This will instruct DBGF to load the given map file\n"
+ " during initialization. (See also loadmap in the debugger.)\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_LOADSYMS)
+ ? " loadsyms <vmname|uuid> <symfile> [delta] [module] [module address]\n"
+ " This will instruct DBGF to load the given symbol file\n"
+ " during initialization.\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_SETHDUUID)
+ ? " sethduuid <filepath> [<uuid>]\n"
+ " Assigns a new UUID to the given image file. This way, multiple copies\n"
+ " of a container can be registered.\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_SETHDPARENTUUID)
+ ? " sethdparentuuid <filepath> <uuid>\n"
+ " Assigns a new parent UUID to the given image file.\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_DUMPHDINFO)
+ ? " dumphdinfo <filepath>\n"
+ " Prints information about the image at the given location.\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_LISTPARTITIONS)
+ ? " listpartitions -rawdisk <diskname>\n"
+ " Lists all partitions on <diskname>.\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_CREATERAWVMDK)
+ ? " createrawvmdk -filename <filename> -rawdisk <diskname>\n"
+ " [-partitions <list of partition numbers> [-mbr <filename>] ]\n"
+ " [-relative]\n"
+ " Creates a new VMDK image which gives access to an entire host disk (if\n"
+ " the parameter -partitions is not specified) or some partitions of a\n"
+ " host disk. If access to individual partitions is granted, then the\n"
+ " parameter -mbr can be used to specify an alternative MBR to be used\n"
+ " (the partitioning information in the MBR file is ignored).\n"
+ " The diskname is on Linux e.g. /dev/sda, and on Windows e.g.\n"
+ " \\\\.\\PhysicalDrive0).\n"
+ " On Linux or FreeBSD host the parameter -relative causes a VMDK file to\n"
+ " be created which refers to individual partitions instead to the entire\n"
+ " disk.\n"
+ " The necessary partition numbers can be queried with\n"
+ " VBoxManage internalcommands listpartitions\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_RENAMEVMDK)
+ ? " renamevmdk -from <filename> -to <filename>\n"
+ " Renames an existing VMDK image, including the base file and all its extents.\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_CONVERTTORAW)
+ ? " converttoraw [-format <fileformat>] <filename> <outputfile>"
+#ifdef ENABLE_CONVERT_RAW_TO_STDOUT
+ "|stdout"
+#endif /* ENABLE_CONVERT_RAW_TO_STDOUT */
+ "\n"
+ " Convert image to raw, writing to file"
+#ifdef ENABLE_CONVERT_RAW_TO_STDOUT
+ " or stdout"
+#endif /* ENABLE_CONVERT_RAW_TO_STDOUT */
+ ".\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_CONVERTHD)
+ ? " converthd [-srcformat VDI|VMDK|VHD|RAW]\n"
+ " [-dstformat VDI|VMDK|VHD|RAW]\n"
+ " <inputfile> <outputfile>\n"
+ " converts hard disk images between formats\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_REPAIRHD)
+ ? " repairhd [-dry-run]\n"
+ " [-format VDI|VMDK|VHD|...]\n"
+ " <filename>\n"
+ " Tries to repair corrupted disk images\n"
+ "\n"
+ : "",
+#ifdef RT_OS_WINDOWS
+ (u64Cmd & USAGE_MODINSTALL)
+ ? " modinstall\n"
+ " Installs the necessary driver for the host OS\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_MODUNINSTALL)
+ ? " moduninstall\n"
+ " Deinstalls the driver\n"
+ "\n"
+ : "",
+#else
+ "",
+ "",
+#endif
+ (u64Cmd & USAGE_DEBUGLOG)
+ ? " debuglog <vmname|uuid> [--enable|--disable] [--flags todo]\n"
+ " [--groups todo] [--destinations todo]\n"
+ " Controls debug logging.\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_PASSWORDHASH)
+ ? " passwordhash <passsword>\n"
+ " Generates a password hash.\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_GUESTSTATS)
+ ? " gueststats <vmname|uuid> [--interval <seconds>]\n"
+ " Obtains and prints internal guest statistics.\n"
+ " Sets the update interval if specified.\n"
+ "\n"
+ : ""
+ );
+}
+
+/** @todo this is no longer necessary, we can enumerate extra data */
+/**
+ * Finds a new unique key name.
+ *
+ * I don't think this is 100% race condition proof, but we assumes
+ * the user is not trying to push this point.
+ *
+ * @returns Result from the insert.
+ * @param pMachine The Machine object.
+ * @param pszKeyBase The base key.
+ * @param rKey Reference to the string object in which we will return the key.
+ */
+static HRESULT NewUniqueKey(ComPtr<IMachine> pMachine, const char *pszKeyBase, Utf8Str &rKey)
+{
+ Bstr KeyBase(pszKeyBase);
+ Bstr Keys;
+ HRESULT hrc = pMachine->GetExtraData(KeyBase.raw(), Keys.asOutParam());
+ if (FAILED(hrc))
+ return hrc;
+
+ /* if there are no keys, it's simple. */
+ if (Keys.isEmpty())
+ {
+ rKey = "1";
+ return pMachine->SetExtraData(KeyBase.raw(), Bstr(rKey).raw());
+ }
+
+ /* find a unique number - brute force rulez. */
+ Utf8Str KeysUtf8(Keys);
+ const char *pszKeys = RTStrStripL(KeysUtf8.c_str());
+ for (unsigned i = 1; i < 1000000; i++)
+ {
+ char szKey[32];
+ size_t cchKey = RTStrPrintf(szKey, sizeof(szKey), "%#x", i);
+ const char *psz = strstr(pszKeys, szKey);
+ while (psz)
+ {
+ if ( ( psz == pszKeys
+ || psz[-1] == ' ')
+ && ( psz[cchKey] == ' '
+ || !psz[cchKey])
+ )
+ break;
+ psz = strstr(psz + cchKey, szKey);
+ }
+ if (!psz)
+ {
+ rKey = szKey;
+ Utf8StrFmt NewKeysUtf8("%s %s", pszKeys, szKey);
+ return pMachine->SetExtraData(KeyBase.raw(),
+ Bstr(NewKeysUtf8).raw());
+ }
+ }
+ RTMsgError("Cannot find unique key for '%s'!", pszKeyBase);
+ return E_FAIL;
+}
+
+
+#if 0
+/**
+ * Remove a key.
+ *
+ * I don't think this isn't 100% race condition proof, but we assumes
+ * the user is not trying to push this point.
+ *
+ * @returns Result from the insert.
+ * @param pMachine The machine object.
+ * @param pszKeyBase The base key.
+ * @param pszKey The key to remove.
+ */
+static HRESULT RemoveKey(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey)
+{
+ Bstr Keys;
+ HRESULT hrc = pMachine->GetExtraData(Bstr(pszKeyBase), Keys.asOutParam());
+ if (FAILED(hrc))
+ return hrc;
+
+ /* if there are no keys, it's simple. */
+ if (Keys.isEmpty())
+ return S_OK;
+
+ char *pszKeys;
+ int rc = RTUtf16ToUtf8(Keys.raw(), &pszKeys);
+ if (RT_SUCCESS(rc))
+ {
+ /* locate it */
+ size_t cchKey = strlen(pszKey);
+ char *psz = strstr(pszKeys, pszKey);
+ while (psz)
+ {
+ if ( ( psz == pszKeys
+ || psz[-1] == ' ')
+ && ( psz[cchKey] == ' '
+ || !psz[cchKey])
+ )
+ break;
+ psz = strstr(psz + cchKey, pszKey);
+ }
+ if (psz)
+ {
+ /* remove it */
+ char *pszNext = RTStrStripL(psz + cchKey);
+ if (*pszNext)
+ memmove(psz, pszNext, strlen(pszNext) + 1);
+ else
+ *psz = '\0';
+ psz = RTStrStrip(pszKeys);
+
+ /* update */
+ hrc = pMachine->SetExtraData(Bstr(pszKeyBase), Bstr(psz));
+ }
+
+ RTStrFree(pszKeys);
+ return hrc;
+ }
+ else
+ RTMsgError("Failed to delete key '%s' from '%s', string conversion error %Rrc!",
+ pszKey, pszKeyBase, rc);
+
+ return E_FAIL;
+}
+#endif
+
+
+/**
+ * Sets a key value, does necessary error bitching.
+ *
+ * @returns COM status code.
+ * @param pMachine The Machine object.
+ * @param pszKeyBase The key base.
+ * @param pszKey The key.
+ * @param pszAttribute The attribute name.
+ * @param pszValue The string value.
+ */
+static HRESULT SetString(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey, const char *pszAttribute, const char *pszValue)
+{
+ HRESULT hrc = pMachine->SetExtraData(BstrFmt("%s/%s/%s", pszKeyBase,
+ pszKey, pszAttribute).raw(),
+ Bstr(pszValue).raw());
+ if (FAILED(hrc))
+ RTMsgError("Failed to set '%s/%s/%s' to '%s'! hrc=%#x",
+ pszKeyBase, pszKey, pszAttribute, pszValue, hrc);
+ return hrc;
+}
+
+
+/**
+ * Sets a key value, does necessary error bitching.
+ *
+ * @returns COM status code.
+ * @param pMachine The Machine object.
+ * @param pszKeyBase The key base.
+ * @param pszKey The key.
+ * @param pszAttribute The attribute name.
+ * @param u64Value The value.
+ */
+static HRESULT SetUInt64(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey, const char *pszAttribute, uint64_t u64Value)
+{
+ char szValue[64];
+ RTStrPrintf(szValue, sizeof(szValue), "%#RX64", u64Value);
+ return SetString(pMachine, pszKeyBase, pszKey, pszAttribute, szValue);
+}
+
+
+/**
+ * Sets a key value, does necessary error bitching.
+ *
+ * @returns COM status code.
+ * @param pMachine The Machine object.
+ * @param pszKeyBase The key base.
+ * @param pszKey The key.
+ * @param pszAttribute The attribute name.
+ * @param i64Value The value.
+ */
+static HRESULT SetInt64(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey, const char *pszAttribute, int64_t i64Value)
+{
+ char szValue[64];
+ RTStrPrintf(szValue, sizeof(szValue), "%RI64", i64Value);
+ return SetString(pMachine, pszKeyBase, pszKey, pszAttribute, szValue);
+}
+
+
+/**
+ * Identical to the 'loadsyms' command.
+ */
+static RTEXITCODE CmdLoadSyms(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aSession);
+ HRESULT rc;
+
+ /*
+ * Get the VM
+ */
+ ComPtr<IMachine> machine;
+ CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
+ machine.asOutParam()), RTEXITCODE_FAILURE);
+
+ /*
+ * Parse the command.
+ */
+ const char *pszFilename;
+ int64_t offDelta = 0;
+ const char *pszModule = NULL;
+ uint64_t ModuleAddress = UINT64_MAX;
+ uint64_t ModuleSize = 0;
+
+ /* filename */
+ if (argc < 2)
+ return errorArgument("Missing the filename argument!\n");
+ pszFilename = argv[1];
+
+ /* offDelta */
+ if (argc >= 3)
+ {
+ int irc = RTStrToInt64Ex(argv[2], NULL, 0, &offDelta);
+ if (RT_FAILURE(irc))
+ return errorArgument(argv[0], "Failed to read delta '%s', rc=%Rrc\n", argv[2], rc);
+ }
+
+ /* pszModule */
+ if (argc >= 4)
+ pszModule = argv[3];
+
+ /* ModuleAddress */
+ if (argc >= 5)
+ {
+ int irc = RTStrToUInt64Ex(argv[4], NULL, 0, &ModuleAddress);
+ if (RT_FAILURE(irc))
+ return errorArgument(argv[0], "Failed to read module address '%s', rc=%Rrc\n", argv[4], rc);
+ }
+
+ /* ModuleSize */
+ if (argc >= 6)
+ {
+ int irc = RTStrToUInt64Ex(argv[5], NULL, 0, &ModuleSize);
+ if (RT_FAILURE(irc))
+ return errorArgument(argv[0], "Failed to read module size '%s', rc=%Rrc\n", argv[5], rc);
+ }
+
+ /*
+ * Add extra data.
+ */
+ Utf8Str KeyStr;
+ HRESULT hrc = NewUniqueKey(machine, "VBoxInternal/DBGF/loadsyms", KeyStr);
+ if (SUCCEEDED(hrc))
+ hrc = SetString(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "Filename", pszFilename);
+ if (SUCCEEDED(hrc) && argc >= 3)
+ hrc = SetInt64(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "Delta", offDelta);
+ if (SUCCEEDED(hrc) && argc >= 4)
+ hrc = SetString(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "Module", pszModule);
+ if (SUCCEEDED(hrc) && argc >= 5)
+ hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "ModuleAddress", ModuleAddress);
+ if (SUCCEEDED(hrc) && argc >= 6)
+ hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "ModuleSize", ModuleSize);
+
+ return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Identical to the 'loadmap' command.
+ */
+static RTEXITCODE CmdLoadMap(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aSession);
+ HRESULT rc;
+
+ /*
+ * Get the VM
+ */
+ ComPtr<IMachine> machine;
+ CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
+ machine.asOutParam()), RTEXITCODE_FAILURE);
+
+ /*
+ * Parse the command.
+ */
+ const char *pszFilename;
+ uint64_t ModuleAddress = UINT64_MAX;
+ const char *pszModule = NULL;
+ uint64_t offSubtrahend = 0;
+ uint32_t iSeg = UINT32_MAX;
+
+ /* filename */
+ if (argc < 2)
+ return errorArgument("Missing the filename argument!\n");
+ pszFilename = argv[1];
+
+ /* address */
+ if (argc < 3)
+ return errorArgument("Missing the module address argument!\n");
+ int irc = RTStrToUInt64Ex(argv[2], NULL, 0, &ModuleAddress);
+ if (RT_FAILURE(irc))
+ return errorArgument(argv[0], "Failed to read module address '%s', rc=%Rrc\n", argv[2], rc);
+
+ /* name (optional) */
+ if (argc > 3)
+ pszModule = argv[3];
+
+ /* subtrahend (optional) */
+ if (argc > 4)
+ {
+ irc = RTStrToUInt64Ex(argv[4], NULL, 0, &offSubtrahend);
+ if (RT_FAILURE(irc))
+ return errorArgument(argv[0], "Failed to read subtrahend '%s', rc=%Rrc\n", argv[4], rc);
+ }
+
+ /* segment (optional) */
+ if (argc > 5)
+ {
+ irc = RTStrToUInt32Ex(argv[5], NULL, 0, &iSeg);
+ if (RT_FAILURE(irc))
+ return errorArgument(argv[0], "Failed to read segment number '%s', rc=%Rrc\n", argv[5], rc);
+ }
+
+ /*
+ * Add extra data.
+ */
+ Utf8Str KeyStr;
+ HRESULT hrc = NewUniqueKey(machine, "VBoxInternal/DBGF/loadmap", KeyStr);
+ if (SUCCEEDED(hrc))
+ hrc = SetString(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Filename", pszFilename);
+ if (SUCCEEDED(hrc))
+ hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Address", ModuleAddress);
+ if (SUCCEEDED(hrc) && pszModule != NULL)
+ hrc = SetString(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Name", pszModule);
+ if (SUCCEEDED(hrc) && offSubtrahend != 0)
+ hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Subtrahend", offSubtrahend);
+ if (SUCCEEDED(hrc) && iSeg != UINT32_MAX)
+ hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Segment", iSeg);
+
+ return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va)
+{
+ RT_NOREF(pvUser);
+ RTMsgErrorV(pszFormat, va);
+ RTMsgError("Error code %Rrc at %s(%u) in function %s", rc, RT_SRC_POS_ARGS);
+}
+
+static DECLCALLBACK(int) handleVDMessage(void *pvUser, const char *pszFormat, va_list va)
+{
+ NOREF(pvUser);
+ return RTPrintfV(pszFormat, va);
+}
+
+static RTEXITCODE CmdSetHDUUID(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+ Guid uuid;
+ RTUUID rtuuid;
+ enum eUuidType {
+ HDUUID,
+ HDPARENTUUID
+ } uuidType;
+
+ if (!strcmp(argv[0], "sethduuid"))
+ {
+ uuidType = HDUUID;
+ if (argc != 3 && argc != 2)
+ return errorSyntax(USAGE_SETHDUUID, "Not enough parameters");
+ /* if specified, take UUID, otherwise generate a new one */
+ if (argc == 3)
+ {
+ if (RT_FAILURE(RTUuidFromStr(&rtuuid, argv[2])))
+ return errorSyntax(USAGE_SETHDUUID, "Invalid UUID parameter");
+ uuid = argv[2];
+ } else
+ uuid.create();
+ }
+ else if (!strcmp(argv[0], "sethdparentuuid"))
+ {
+ uuidType = HDPARENTUUID;
+ if (argc != 3)
+ return errorSyntax(USAGE_SETHDPARENTUUID, "Not enough parameters");
+ if (RT_FAILURE(RTUuidFromStr(&rtuuid, argv[2])))
+ return errorSyntax(USAGE_SETHDPARENTUUID, "Invalid UUID parameter");
+ uuid = argv[2];
+ }
+ else
+ return errorSyntax(USAGE_SETHDUUID, "Invalid invocation");
+
+ /* just try it */
+ char *pszFormat = NULL;
+ VDTYPE enmType = VDTYPE_INVALID;
+ int rc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
+ argv[1], &pszFormat, &enmType);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Format autodetect failed: %Rrc", rc);
+
+ PVDISK pDisk = NULL;
+
+ PVDINTERFACE pVDIfs = NULL;
+ VDINTERFACEERROR vdInterfaceError;
+ vdInterfaceError.pfnError = handleVDError;
+ vdInterfaceError.pfnMessage = handleVDMessage;
+
+ rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
+ NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
+ AssertRC(rc);
+
+ rc = VDCreate(pVDIfs, enmType, &pDisk);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot create the virtual disk container: %Rrc", rc);
+
+ /* Open the image */
+ rc = VDOpen(pDisk, pszFormat, argv[1], VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_INFO, NULL);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot open the image: %Rrc", rc);
+
+ if (uuidType == HDUUID)
+ rc = VDSetUuid(pDisk, VD_LAST_IMAGE, uuid.raw());
+ else
+ rc = VDSetParentUuid(pDisk, VD_LAST_IMAGE, uuid.raw());
+ if (RT_FAILURE(rc))
+ RTMsgError("Cannot set a new UUID: %Rrc", rc);
+ else
+ RTPrintf("UUID changed to: %s\n", uuid.toString().c_str());
+
+ VDCloseAll(pDisk);
+
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+static RTEXITCODE CmdDumpHDInfo(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+
+ /* we need exactly one parameter: the image file */
+ if (argc != 1)
+ {
+ return errorSyntax(USAGE_DUMPHDINFO, "Not enough parameters");
+ }
+
+ /* just try it */
+ char *pszFormat = NULL;
+ VDTYPE enmType = VDTYPE_INVALID;
+ int rc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
+ argv[0], &pszFormat, &enmType);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Format autodetect failed: %Rrc", rc);
+
+ PVDISK pDisk = NULL;
+
+ PVDINTERFACE pVDIfs = NULL;
+ VDINTERFACEERROR vdInterfaceError;
+ vdInterfaceError.pfnError = handleVDError;
+ vdInterfaceError.pfnMessage = handleVDMessage;
+
+ rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
+ NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
+ AssertRC(rc);
+
+ rc = VDCreate(pVDIfs, enmType, &pDisk);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot create the virtual disk container: %Rrc", rc);
+
+ /* Open the image */
+ rc = VDOpen(pDisk, pszFormat, argv[0], VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO, NULL);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot open the image: %Rrc", rc);
+
+ VDDumpImages(pDisk);
+
+ VDCloseAll(pDisk);
+
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static int partRead(RTFILE File, PHOSTPARTITIONS pPart)
+{
+ uint8_t aBuffer[512];
+ uint8_t partitionTableHeader[512];
+ uint32_t sector_size = 512;
+ uint64_t lastUsableLBA = 0;
+ int rc;
+
+ VDISKPARTTYPE partitioningType;
+
+ pPart->cPartitions = 0;
+ memset(pPart->aPartitions, '\0', sizeof(pPart->aPartitions));
+
+ rc = RTFileReadAt(File, 0, &aBuffer, sizeof(aBuffer), NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (aBuffer[450] == 0xEE)/* check the sign of the GPT disk*/
+ {
+ partitioningType = GPT;
+ pPart->uPartitioningType = GPT;//partitioningType;
+
+ if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa)
+ return VERR_INVALID_PARAMETER;
+
+ rc = RTFileReadAt(File, sector_size, &partitionTableHeader, sector_size, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo r=bird: This is a 64-bit magic value, right... */
+ const char *l_ppth = (char *)partitionTableHeader;
+ if (strncmp(l_ppth, "EFI PART", 8))
+ return VERR_INVALID_PARAMETER;
+
+ /** @todo check GPT Version */
+
+ /** @todo r=bird: C have this handy concept called structures which
+ * greatly simplify data access... (Someone is really lazy here!) */
+#if 0 /* unused */
+ uint64_t firstUsableLBA = RT_MAKE_U64_FROM_U8(partitionTableHeader[40],
+ partitionTableHeader[41],
+ partitionTableHeader[42],
+ partitionTableHeader[43],
+ partitionTableHeader[44],
+ partitionTableHeader[45],
+ partitionTableHeader[46],
+ partitionTableHeader[47]
+ );
+#endif
+ lastUsableLBA = RT_MAKE_U64_FROM_U8(partitionTableHeader[48],
+ partitionTableHeader[49],
+ partitionTableHeader[50],
+ partitionTableHeader[51],
+ partitionTableHeader[52],
+ partitionTableHeader[53],
+ partitionTableHeader[54],
+ partitionTableHeader[55]
+ );
+ uint32_t partitionsNumber = RT_MAKE_U32_FROM_U8(partitionTableHeader[80],
+ partitionTableHeader[81],
+ partitionTableHeader[82],
+ partitionTableHeader[83]
+ );
+ uint32_t partitionEntrySize = RT_MAKE_U32_FROM_U8(partitionTableHeader[84],
+ partitionTableHeader[85],
+ partitionTableHeader[86],
+ partitionTableHeader[87]
+ );
+
+ uint32_t currentEntry = 0;
+
+ if (partitionEntrySize * partitionsNumber > 4 * _1M)
+ {
+ RTMsgError("The GPT header seems corrupt because it contains too many entries");
+ return VERR_INVALID_PARAMETER;
+ }
+
+ uint8_t *pbPartTable = (uint8_t *)RTMemAllocZ(RT_ALIGN_Z(partitionEntrySize * partitionsNumber, 512));
+ if (!pbPartTable)
+ {
+ RTMsgError("Allocating memory for the GPT partitions entries failed");
+ return VERR_NO_MEMORY;
+ }
+
+ /* partition entries begin from LBA2 */
+ /** @todo r=aeichner: Reading from LBA 2 is not always correct, the header will contain the starting LBA. */
+ rc = RTFileReadAt(File, 1024, pbPartTable, RT_ALIGN_Z(partitionEntrySize * partitionsNumber, 512), NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("Reading the partition table failed");
+ RTMemFree(pbPartTable);
+ return rc;
+ }
+
+ while (currentEntry < partitionsNumber)
+ {
+ uint8_t *partitionEntry = pbPartTable + currentEntry * partitionEntrySize;
+
+ uint64_t start = RT_MAKE_U64_FROM_U8(partitionEntry[32], partitionEntry[33], partitionEntry[34], partitionEntry[35],
+ partitionEntry[36], partitionEntry[37], partitionEntry[38], partitionEntry[39]);
+ uint64_t end = RT_MAKE_U64_FROM_U8(partitionEntry[40], partitionEntry[41], partitionEntry[42], partitionEntry[43],
+ partitionEntry[44], partitionEntry[45], partitionEntry[46], partitionEntry[47]);
+
+ PHOSTPARTITION pCP = &pPart->aPartitions[pPart->cPartitions++];
+ pCP->uIndex = currentEntry + 1;
+ pCP->uIndexWin = currentEntry + 1;
+ pCP->uType = 0;
+ pCP->uStartCylinder = 0;
+ pCP->uStartHead = 0;
+ pCP->uStartSector = 0;
+ pCP->uEndCylinder = 0;
+ pCP->uEndHead = 0;
+ pCP->uEndSector = 0;
+ pCP->uPartDataStart = 0; /* will be filled out later properly. */
+ pCP->cPartDataSectors = 0;
+ if (start==0 || end==0)
+ {
+ pCP->uIndex = 0;
+ pCP->uIndexWin = 0;
+ --pPart->cPartitions;
+ break;
+ }
+ else
+ {
+ pCP->uStart = start;
+ pCP->uSize = (end +1) - start;/*+1 LBA because the last address is included*/
+ }
+
+ ++currentEntry;
+ }
+
+ RTMemFree(pbPartTable);
+ }
+ }
+ else
+ {
+ partitioningType = MBR;
+ pPart->uPartitioningType = MBR;//partitioningType;
+
+ if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa)
+ return VERR_INVALID_PARAMETER;
+
+ unsigned uExtended = (unsigned)-1;
+ unsigned uIndexWin = 1;
+
+ for (unsigned i = 0; i < 4; i++)
+ {
+ uint8_t *p = &aBuffer[0x1be + i * 16];
+ if (p[4] == 0)
+ continue;
+ PHOSTPARTITION pCP = &pPart->aPartitions[pPart->cPartitions++];
+ pCP->uIndex = i + 1;
+ pCP->uType = p[4];
+ pCP->uStartCylinder = (uint32_t)p[3] + ((uint32_t)(p[2] & 0xc0) << 2);
+ pCP->uStartHead = p[1];
+ pCP->uStartSector = p[2] & 0x3f;
+ pCP->uEndCylinder = (uint32_t)p[7] + ((uint32_t)(p[6] & 0xc0) << 2);
+ pCP->uEndHead = p[5];
+ pCP->uEndSector = p[6] & 0x3f;
+ pCP->uStart = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
+ pCP->uSize = RT_MAKE_U32_FROM_U8(p[12], p[13], p[14], p[15]);
+ pCP->uPartDataStart = 0; /* will be filled out later properly. */
+ pCP->cPartDataSectors = 0;
+
+ if (PARTTYPE_IS_EXTENDED(p[4]))
+ {
+ if (uExtended == (unsigned)-1)
+ {
+ uExtended = (unsigned)(pCP - pPart->aPartitions);
+ pCP->uIndexWin = 0;
+ }
+ else
+ {
+ RTMsgError("More than one extended partition");
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ pCP->uIndexWin = uIndexWin;
+ uIndexWin++;
+ }
+ }
+
+ if (uExtended != (unsigned)-1)
+ {
+ unsigned uIndex = 5;
+ uint64_t uStart = pPart->aPartitions[uExtended].uStart;
+ uint64_t uOffset = 0;
+ if (!uStart)
+ {
+ RTMsgError("Inconsistency for logical partition start");
+ return VERR_INVALID_PARAMETER;
+ }
+
+ do
+ {
+ rc = RTFileReadAt(File, (uStart + uOffset) * 512, &aBuffer, sizeof(aBuffer), NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa)
+ {
+ RTMsgError("Logical partition without magic");
+ return VERR_INVALID_PARAMETER;
+ }
+ uint8_t *p = &aBuffer[0x1be];
+
+ if (p[4] == 0)
+ {
+ RTMsgError("Logical partition with type 0 encountered");
+ return VERR_INVALID_PARAMETER;
+ }
+
+ PHOSTPARTITION pCP = &pPart->aPartitions[pPart->cPartitions++];
+ pCP->uIndex = uIndex;
+ pCP->uIndexWin = uIndexWin;
+ pCP->uType = p[4];
+ pCP->uStartCylinder = (uint32_t)p[3] + ((uint32_t)(p[2] & 0xc0) << 2);
+ pCP->uStartHead = p[1];
+ pCP->uStartSector = p[2] & 0x3f;
+ pCP->uEndCylinder = (uint32_t)p[7] + ((uint32_t)(p[6] & 0xc0) << 2);
+ pCP->uEndHead = p[5];
+ pCP->uEndSector = p[6] & 0x3f;
+ uint32_t uStartOffset = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
+ if (!uStartOffset)
+ {
+ RTMsgError("Invalid partition start offset");
+ return VERR_INVALID_PARAMETER;
+ }
+ pCP->uStart = uStart + uOffset + uStartOffset;
+ pCP->uSize = RT_MAKE_U32_FROM_U8(p[12], p[13], p[14], p[15]);
+ /* Fill out partitioning location info for EBR. */
+ pCP->uPartDataStart = uStart + uOffset;
+ pCP->cPartDataSectors = uStartOffset;
+ p += 16;
+ if (p[4] == 0)
+ uExtended = (unsigned)-1;
+ else if (PARTTYPE_IS_EXTENDED(p[4]))
+ {
+ uExtended = uIndex;
+ uIndex++;
+ uIndexWin++;
+ uOffset = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
+ }
+ else
+ {
+ RTMsgError("Logical partition chain broken");
+ return VERR_INVALID_PARAMETER;
+ }
+ } while (uExtended != (unsigned)-1);
+ }
+ }
+
+
+ /* Sort partitions in ascending order of start sector, plus a trivial
+ * bit of consistency checking. */
+ for (unsigned i = 0; i < pPart->cPartitions-1; i++)
+ {
+ unsigned uMinIdx = i;
+ uint64_t uMinVal = pPart->aPartitions[i].uStart;
+ for (unsigned j = i + 1; j < pPart->cPartitions; j++)
+ {
+ if (pPart->aPartitions[j].uStart < uMinVal)
+ {
+ uMinIdx = j;
+ uMinVal = pPart->aPartitions[j].uStart;
+ }
+ else if (pPart->aPartitions[j].uStart == uMinVal)
+ {
+ RTMsgError("Two partitions start at the same place");
+ return VERR_INVALID_PARAMETER;
+ }
+ else if (pPart->aPartitions[j].uStart == 0)
+ {
+ RTMsgError("Partition starts at sector 0");
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+ if (uMinIdx != i)
+ {
+ /* Swap entries at index i and uMinIdx. */
+ memcpy(&pPart->aPartitions[pPart->cPartitions],
+ &pPart->aPartitions[i], sizeof(HOSTPARTITION));
+ memcpy(&pPart->aPartitions[i],
+ &pPart->aPartitions[uMinIdx], sizeof(HOSTPARTITION));
+ memcpy(&pPart->aPartitions[uMinIdx],
+ &pPart->aPartitions[pPart->cPartitions], sizeof(HOSTPARTITION));
+ }
+ }
+
+ /* Fill out partitioning location info for MBR or GPT. */
+ pPart->aPartitions[0].uPartDataStart = 0;
+ pPart->aPartitions[0].cPartDataSectors = pPart->aPartitions[0].uStart;
+
+ /* Fill out partitioning location info for backup GPT. */
+ if (partitioningType == GPT)
+ {
+ pPart->aPartitions[pPart->cPartitions-1].uPartDataStart = lastUsableLBA+1;
+ pPart->aPartitions[pPart->cPartitions-1].cPartDataSectors = 33;
+
+ /* Now do a some partition table consistency checking, to reject the most
+ * obvious garbage which can lead to trouble later. */
+ uint64_t uPrevEnd = 0;
+ for (unsigned i = 0; i < pPart->cPartitions; i++)
+ {
+ if (pPart->aPartitions[i].cPartDataSectors)
+ uPrevEnd = pPart->aPartitions[i].uPartDataStart + pPart->aPartitions[i].cPartDataSectors;
+ if (pPart->aPartitions[i].uStart < uPrevEnd &&
+ pPart->cPartitions-1 != i)
+ {
+ RTMsgError("Overlapping GPT partitions");
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+ }
+ else
+ {
+ /* Now do a some partition table consistency checking, to reject the most
+ * obvious garbage which can lead to trouble later. */
+ uint64_t uPrevEnd = 0;
+ for (unsigned i = 0; i < pPart->cPartitions; i++)
+ {
+ if (pPart->aPartitions[i].cPartDataSectors)
+ uPrevEnd = pPart->aPartitions[i].uPartDataStart + pPart->aPartitions[i].cPartDataSectors;
+ if (pPart->aPartitions[i].uStart < uPrevEnd)
+ {
+ RTMsgError("Overlapping MBR partitions");
+ return VERR_INVALID_PARAMETER;
+ }
+ if (!PARTTYPE_IS_EXTENDED(pPart->aPartitions[i].uType))
+ uPrevEnd = pPart->aPartitions[i].uStart + pPart->aPartitions[i].uSize;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+static RTEXITCODE CmdListPartitions(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+ Utf8Str rawdisk;
+
+ /* let's have a closer look at the arguments */
+ for (int i = 0; i < argc; i++)
+ {
+ if (strcmp(argv[i], "-rawdisk") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ rawdisk = argv[i];
+ }
+ else
+ {
+ return errorSyntax(USAGE_LISTPARTITIONS, "Invalid parameter '%s'", argv[i]);
+ }
+ }
+
+ if (rawdisk.isEmpty())
+ return errorSyntax(USAGE_LISTPARTITIONS, "Mandatory parameter -rawdisk missing");
+
+ RTFILE hRawFile;
+ int vrc = RTFileOpen(&hRawFile, rawdisk.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+ if (RT_FAILURE(vrc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot open the raw disk: %Rrc", vrc);
+
+ HOSTPARTITIONS partitions;
+ vrc = partRead(hRawFile, &partitions);
+ /* Don't bail out on errors, print the table and return the result code. */
+
+ RTPrintf("Number Type StartCHS EndCHS Size (MiB) Start (Sect)\n");
+ for (unsigned i = 0; i < partitions.cPartitions; i++)
+ {
+ /* Don't show the extended partition, otherwise users might think they
+ * can add it to the list of partitions for raw partition access. */
+ if (PARTTYPE_IS_EXTENDED(partitions.aPartitions[i].uType))
+ continue;
+
+ RTPrintf("%-7u %#04x %-4u/%-3u/%-2u %-4u/%-3u/%-2u %10llu %10llu\n",
+ partitions.aPartitions[i].uIndex,
+ partitions.aPartitions[i].uType,
+ partitions.aPartitions[i].uStartCylinder,
+ partitions.aPartitions[i].uStartHead,
+ partitions.aPartitions[i].uStartSector,
+ partitions.aPartitions[i].uEndCylinder,
+ partitions.aPartitions[i].uEndHead,
+ partitions.aPartitions[i].uEndSector,
+ partitions.aPartitions[i].uSize / 2048,
+ partitions.aPartitions[i].uStart);
+ }
+
+ return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static PVDISKRAWPARTDESC appendPartDesc(uint32_t *pcPartDescs, PVDISKRAWPARTDESC *ppPartDescs)
+{
+ (*pcPartDescs)++;
+ PVDISKRAWPARTDESC p;
+ p = (PVDISKRAWPARTDESC)RTMemRealloc(*ppPartDescs,
+ *pcPartDescs * sizeof(VDISKRAWPARTDESC));
+ *ppPartDescs = p;
+ if (p)
+ {
+ p = p + *pcPartDescs - 1;
+ memset(p, '\0', sizeof(VDISKRAWPARTDESC));
+ }
+
+ return p;
+}
+
+static RTEXITCODE CmdCreateRawVMDK(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+ HRESULT rc = S_OK;
+ Utf8Str filename;
+ const char *pszMBRFilename = NULL;
+ Utf8Str rawdisk;
+ const char *pszPartitions = NULL;
+ bool fRelative = false;
+
+ uint64_t cbSize = 0;
+ PVDISK pDisk = NULL;
+ VDISKRAW RawDescriptor;
+ PVDINTERFACE pVDIfs = NULL;
+
+ /* let's have a closer look at the arguments */
+ for (int i = 0; i < argc; i++)
+ {
+ if (strcmp(argv[i], "-filename") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ filename = argv[i];
+ }
+ else if (strcmp(argv[i], "-mbr") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ pszMBRFilename = argv[i];
+ }
+ else if (strcmp(argv[i], "-rawdisk") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ rawdisk = argv[i];
+ }
+ else if (strcmp(argv[i], "-partitions") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ pszPartitions = argv[i];
+ }
+#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_WINDOWS)
+ else if (strcmp(argv[i], "-relative") == 0)
+ {
+ fRelative = true;
+ }
+#endif /* RT_OS_LINUX || RT_OS_FREEBSD */
+ else
+ return errorSyntax(USAGE_CREATERAWVMDK, "Invalid parameter '%s'", argv[i]);
+ }
+
+ if (filename.isEmpty())
+ return errorSyntax(USAGE_CREATERAWVMDK, "Mandatory parameter -filename missing");
+ if (rawdisk.isEmpty())
+ return errorSyntax(USAGE_CREATERAWVMDK, "Mandatory parameter -rawdisk missing");
+ if (!pszPartitions && pszMBRFilename)
+ return errorSyntax(USAGE_CREATERAWVMDK, "The parameter -mbr is only valid when the parameter -partitions is also present");
+
+#ifdef RT_OS_DARWIN
+ fRelative = true;
+#endif /* RT_OS_DARWIN */
+ RTFILE hRawFile;
+ int vrc = RTFileOpen(&hRawFile, rawdisk.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot open the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+
+#ifdef RT_OS_WINDOWS
+ /* Windows NT has no IOCTL_DISK_GET_LENGTH_INFORMATION ioctl. This was
+ * added to Windows XP, so we have to use the available info from DriveGeo.
+ * Note that we cannot simply use IOCTL_DISK_GET_DRIVE_GEOMETRY as it
+ * yields a slightly different result than IOCTL_DISK_GET_LENGTH_INFO.
+ * We call IOCTL_DISK_GET_DRIVE_GEOMETRY first as we need to check the media
+ * type anyway, and if IOCTL_DISK_GET_LENGTH_INFORMATION is supported
+ * we will later override cbSize.
+ */
+ DISK_GEOMETRY DriveGeo;
+ DWORD cbDriveGeo;
+ if (DeviceIoControl((HANDLE)RTFileToNative(hRawFile),
+ IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
+ &DriveGeo, sizeof(DriveGeo), &cbDriveGeo, NULL))
+ {
+ if ( DriveGeo.MediaType == FixedMedia
+ || DriveGeo.MediaType == RemovableMedia)
+ {
+ cbSize = DriveGeo.Cylinders.QuadPart
+ * DriveGeo.TracksPerCylinder
+ * DriveGeo.SectorsPerTrack
+ * DriveGeo.BytesPerSector;
+ }
+ else
+ {
+ RTMsgError("File '%s' is no fixed/removable medium device", rawdisk.c_str());
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+
+ GET_LENGTH_INFORMATION DiskLenInfo;
+ DWORD junk;
+ if (DeviceIoControl((HANDLE)RTFileToNative(hRawFile),
+ IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
+ &DiskLenInfo, sizeof(DiskLenInfo), &junk, (LPOVERLAPPED)NULL))
+ {
+ /* IOCTL_DISK_GET_LENGTH_INFO is supported -- override cbSize. */
+ cbSize = DiskLenInfo.Length.QuadPart;
+ }
+ if ( fRelative
+ && !rawdisk.startsWith("\\\\.\\PhysicalDrive", Utf8Str::CaseInsensitive))
+ {
+ RTMsgError("The -relative parameter is invalid for raw disk %s", rawdisk.c_str());
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+ else
+ {
+ /*
+ * Could be raw image, remember error code and try to get the size first
+ * before failing.
+ */
+ vrc = RTErrConvertFromWin32(GetLastError());
+ if (RT_FAILURE(RTFileGetSize(hRawFile, &cbSize)))
+ {
+ RTMsgError("Cannot get the geometry of the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ else
+ {
+ if (fRelative)
+ {
+ RTMsgError("The -relative parameter is invalid for raw images");
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ vrc = VINF_SUCCESS;
+ }
+ }
+#elif defined(RT_OS_LINUX)
+ struct stat DevStat;
+ if (!fstat(RTFileToNative(hRawFile), &DevStat))
+ {
+ if (S_ISBLK(DevStat.st_mode))
+ {
+#ifdef BLKGETSIZE64
+ /* BLKGETSIZE64 is broken up to 2.4.17 and in many 2.5.x. In 2.6.0
+ * it works without problems. */
+ struct utsname utsname;
+ if ( uname(&utsname) == 0
+ && ( (strncmp(utsname.release, "2.5.", 4) == 0 && atoi(&utsname.release[4]) >= 18)
+ || (strncmp(utsname.release, "2.", 2) == 0 && atoi(&utsname.release[2]) >= 6)))
+ {
+ uint64_t cbBlk;
+ if (!ioctl(RTFileToNative(hRawFile), BLKGETSIZE64, &cbBlk))
+ cbSize = cbBlk;
+ }
+#endif /* BLKGETSIZE64 */
+ if (!cbSize)
+ {
+ long cBlocks;
+ if (!ioctl(RTFileToNative(hRawFile), BLKGETSIZE, &cBlocks))
+ cbSize = (uint64_t)cBlocks << 9;
+ else
+ {
+ vrc = RTErrConvertFromErrno(errno);
+ RTMsgError("Cannot get the size of the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ }
+ }
+ else if (S_ISREG(DevStat.st_mode))
+ {
+ vrc = RTFileGetSize(hRawFile, &cbSize);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Failed to get size of file '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ else if (fRelative)
+ {
+ RTMsgError("The -relative parameter is invalid for raw images");
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+ else
+ {
+ RTMsgError("File '%s' is no block device", rawdisk.c_str());
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+ else
+ {
+ vrc = RTErrConvertFromErrno(errno);
+ RTMsgError("Failed to get file informtation for raw disk '%s': %Rrc",
+ rawdisk.c_str(), vrc);
+ }
+#elif defined(RT_OS_DARWIN)
+ struct stat DevStat;
+ if (!fstat(RTFileToNative(hRawFile), &DevStat))
+ {
+ if (S_ISBLK(DevStat.st_mode))
+ {
+ uint64_t cBlocks;
+ uint32_t cbBlock;
+ if (!ioctl(RTFileToNative(hRawFile), DKIOCGETBLOCKCOUNT, &cBlocks))
+ {
+ if (!ioctl(RTFileToNative(hRawFile), DKIOCGETBLOCKSIZE, &cbBlock))
+ cbSize = cBlocks * cbBlock;
+ else
+ {
+ RTMsgError("Cannot get the block size for file '%s': %Rrc", rawdisk.c_str(), vrc);
+ vrc = RTErrConvertFromErrno(errno);
+ goto out;
+ }
+ }
+ else
+ {
+ vrc = RTErrConvertFromErrno(errno);
+ RTMsgError("Cannot get the block count for file '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ }
+ else if (S_ISREG(DevStat.st_mode))
+ {
+ fRelative = false; /* Must be false for raw image files. */
+ vrc = RTFileGetSize(hRawFile, &cbSize);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Failed to get size of file '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ }
+ else
+ {
+ RTMsgError("File '%s' is neither block device nor regular file", rawdisk.c_str());
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+ else
+ {
+ vrc = RTErrConvertFromErrno(errno);
+ RTMsgError("Failed to get file informtation for raw disk '%s': %Rrc",
+ rawdisk.c_str(), vrc);
+ }
+#elif defined(RT_OS_SOLARIS)
+ struct stat DevStat;
+ if (!fstat(RTFileToNative(hRawFile), &DevStat))
+ {
+ if (S_ISBLK(DevStat.st_mode) || S_ISCHR(DevStat.st_mode))
+ {
+ struct dk_minfo mediainfo;
+ if (!ioctl(RTFileToNative(hRawFile), DKIOCGMEDIAINFO, &mediainfo))
+ cbSize = mediainfo.dki_capacity * mediainfo.dki_lbsize;
+ else
+ {
+ vrc = RTErrConvertFromErrno(errno);
+ RTMsgError("Cannot get the size of the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ }
+ else if (S_ISREG(DevStat.st_mode))
+ {
+ vrc = RTFileGetSize(hRawFile, &cbSize);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Failed to get size of file '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ }
+ else
+ {
+ RTMsgError("File '%s' is no block or char device", rawdisk.c_str());
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+ else
+ {
+ vrc = RTErrConvertFromErrno(errno);
+ RTMsgError("Failed to get file informtation for raw disk '%s': %Rrc",
+ rawdisk.c_str(), vrc);
+ }
+#elif defined(RT_OS_FREEBSD)
+ struct stat DevStat;
+ if (!fstat(RTFileToNative(hRawFile), &DevStat))
+ {
+ if (S_ISCHR(DevStat.st_mode))
+ {
+ off_t cbMedia = 0;
+ if (!ioctl(RTFileToNative(hRawFile), DIOCGMEDIASIZE, &cbMedia))
+ cbSize = cbMedia;
+ else
+ {
+ vrc = RTErrConvertFromErrno(errno);
+ RTMsgError("Cannot get the block count for file '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ }
+ else if (S_ISREG(DevStat.st_mode))
+ {
+ if (fRelative)
+ {
+ RTMsgError("The -relative parameter is invalid for raw images");
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ cbSize = DevStat.st_size;
+ }
+ else
+ {
+ RTMsgError("File '%s' is neither character device nor regular file", rawdisk.c_str());
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+ else
+ {
+ vrc = RTErrConvertFromErrno(errno);
+ RTMsgError("Failed to get file informtation for raw disk '%s': %Rrc",
+ rawdisk.c_str(), vrc);
+ }
+#else /* all unrecognized OSes */
+ /* Hopefully this works on all other hosts. If it doesn't, it'll just fail
+ * creating the VMDK, so no real harm done. */
+ vrc = RTFileGetSize(hRawFile, &cbSize);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot get the size of the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+#endif
+
+ /* Check whether cbSize is actually sensible. */
+ if (!cbSize || cbSize % 512)
+ {
+ RTMsgError("Detected size of raw disk '%s' is %RU64, an invalid value", rawdisk.c_str(), cbSize);
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+
+ RawDescriptor.szSignature[0] = 'R';
+ RawDescriptor.szSignature[1] = 'A';
+ RawDescriptor.szSignature[2] = 'W';
+ RawDescriptor.szSignature[3] = '\0';
+ if (!pszPartitions)
+ {
+ RawDescriptor.uFlags = VDISKRAW_DISK;
+ RawDescriptor.pszRawDisk = rawdisk.c_str();
+ }
+ else
+ {
+ RawDescriptor.uFlags = VDISKRAW_NORMAL;
+ RawDescriptor.pszRawDisk = NULL;
+ RawDescriptor.cPartDescs = 0;
+ RawDescriptor.pPartDescs = NULL;
+
+ uint32_t uPartitions = 0;
+ uint32_t uPartitionsRO = 0;
+
+ const char *p = pszPartitions;
+ char *pszNext;
+ uint32_t u32;
+ while (*p != '\0')
+ {
+ vrc = RTStrToUInt32Ex(p, &pszNext, 0, &u32);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Incorrect value in partitions parameter");
+ goto out;
+ }
+ uPartitions |= RT_BIT(u32);
+ p = pszNext;
+ if (*p == 'r')
+ {
+ uPartitionsRO |= RT_BIT(u32);
+ p++;
+ }
+ if (*p == ',')
+ p++;
+ else if (*p != '\0')
+ {
+ RTMsgError("Incorrect separator in partitions parameter");
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+
+ HOSTPARTITIONS partitions;
+ vrc = partRead(hRawFile, &partitions);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot read the partition information from '%s'", rawdisk.c_str());
+ goto out;
+ }
+
+ RawDescriptor.uPartitioningType = partitions.uPartitioningType;
+
+ for (unsigned i = 0; i < partitions.cPartitions; i++)
+ {
+ if ( uPartitions & RT_BIT(partitions.aPartitions[i].uIndex)
+ && PARTTYPE_IS_EXTENDED(partitions.aPartitions[i].uType))
+ {
+ /* Some ignorant user specified an extended partition.
+ * Bad idea, as this would trigger an overlapping
+ * partitions error later during VMDK creation. So warn
+ * here and ignore what the user requested. */
+ RTMsgWarning("It is not possible (and necessary) to explicitly give access to the "
+ "extended partition %u. If required, enable access to all logical "
+ "partitions inside this extended partition.",
+ partitions.aPartitions[i].uIndex);
+ uPartitions &= ~RT_BIT(partitions.aPartitions[i].uIndex);
+ }
+ }
+
+ for (unsigned i = 0; i < partitions.cPartitions; i++)
+ {
+ PVDISKRAWPARTDESC pPartDesc = NULL;
+
+ /* first dump the MBR/EPT data area */
+ if (partitions.aPartitions[i].cPartDataSectors)
+ {
+ pPartDesc = appendPartDesc(&RawDescriptor.cPartDescs,
+ &RawDescriptor.pPartDescs);
+ if (!pPartDesc)
+ {
+ RTMsgError("Out of memory allocating the partition list for '%s'", rawdisk.c_str());
+ vrc = VERR_NO_MEMORY;
+ goto out;
+ }
+
+ /** @todo the clipping below isn't 100% accurate, as it should
+ * actually clip to the track size. However, that's easier said
+ * than done as figuring out the track size is heuristics. In
+ * any case the clipping is adjusted later after sorting, to
+ * prevent overlapping data areas on the resulting image. */
+ pPartDesc->cbData = RT_MIN(partitions.aPartitions[i].cPartDataSectors, 63) * 512;
+ pPartDesc->uStart = partitions.aPartitions[i].uPartDataStart * 512;
+ Assert(pPartDesc->cbData - (size_t)pPartDesc->cbData == 0);
+ void *pPartData = RTMemAlloc((size_t)pPartDesc->cbData);
+ if (!pPartData)
+ {
+ RTMsgError("Out of memory allocating the partition descriptor for '%s'", rawdisk.c_str());
+ vrc = VERR_NO_MEMORY;
+ goto out;
+ }
+ vrc = RTFileReadAt(hRawFile, partitions.aPartitions[i].uPartDataStart * 512,
+ pPartData, (size_t)pPartDesc->cbData, NULL);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot read partition data from raw device '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ /* Splice in the replacement MBR code if specified. */
+ if ( partitions.aPartitions[i].uPartDataStart == 0
+ && pszMBRFilename)
+ {
+ RTFILE MBRFile;
+ vrc = RTFileOpen(&MBRFile, pszMBRFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot open replacement MBR file '%s' specified with -mbr: %Rrc", pszMBRFilename, vrc);
+ goto out;
+ }
+ vrc = RTFileReadAt(MBRFile, 0, pPartData, 0x1be, NULL);
+ RTFileClose(MBRFile);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot read replacement MBR file '%s': %Rrc", pszMBRFilename, vrc);
+ goto out;
+ }
+ }
+ pPartDesc->pvPartitionData = pPartData;
+ }
+
+ if (PARTTYPE_IS_EXTENDED(partitions.aPartitions[i].uType))
+ {
+ /* Suppress exporting the actual extended partition. Only
+ * logical partitions should be processed. However completely
+ * ignoring it leads to leaving out the EBR data. */
+ continue;
+ }
+
+ /* set up values for non-relative device names */
+ const char *pszRawName = rawdisk.c_str();
+ uint64_t uStartOffset = partitions.aPartitions[i].uStart * 512;
+
+ pPartDesc = appendPartDesc(&RawDescriptor.cPartDescs,
+ &RawDescriptor.pPartDescs);
+ if (!pPartDesc)
+ {
+ RTMsgError("Out of memory allocating the partition list for '%s'", rawdisk.c_str());
+ vrc = VERR_NO_MEMORY;
+ goto out;
+ }
+
+ if (uPartitions & RT_BIT(partitions.aPartitions[i].uIndex))
+ {
+ if (uPartitionsRO & RT_BIT(partitions.aPartitions[i].uIndex))
+ pPartDesc->uFlags |= VDISKRAW_READONLY;
+
+ if (fRelative)
+ {
+#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
+ /* Refer to the correct partition and use offset 0. */
+ char *psz;
+#if defined(RT_OS_LINUX)
+ /*
+ * Check whether raw disk ends with a digit. In that case
+ * insert a p before adding the partition number.
+ * This is used for nvme devices only currently which look like
+ * /dev/nvme0n1p1 but might be extended to other devices in the
+ * future.
+ */
+ size_t cchRawDisk = rawdisk.length();
+ if (RT_C_IS_DIGIT(pszRawName[cchRawDisk - 1]))
+ RTStrAPrintf(&psz,
+ "%sp%u",
+ rawdisk.c_str(),
+ partitions.aPartitions[i].uIndex);
+ else
+ RTStrAPrintf(&psz,
+ "%s%u",
+ rawdisk.c_str(),
+ partitions.aPartitions[i].uIndex);
+#elif defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
+ RTStrAPrintf(&psz,
+ "%ss%u",
+ rawdisk.c_str(),
+ partitions.aPartitions[i].uIndex);
+#endif
+ if (!psz)
+ {
+ vrc = VERR_NO_STR_MEMORY;
+ RTMsgError("Cannot create reference to individual partition %u, rc=%Rrc",
+ partitions.aPartitions[i].uIndex, vrc);
+ goto out;
+ }
+ pszRawName = psz;
+ uStartOffset = 0;
+#elif defined(RT_OS_WINDOWS)
+ /* Refer to the correct partition and use offset 0. */
+ char *psz;
+ RTStrAPrintf(&psz, "\\\\.\\Harddisk%sPartition%u",
+ rawdisk.c_str() + 17,
+ partitions.aPartitions[i].uIndexWin);
+ if (!psz)
+ {
+ vrc = VERR_NO_STR_MEMORY;
+ RTMsgError("Cannot create reference to individual partition %u (numbered %u), rc=%Rrc",
+ partitions.aPartitions[i].uIndex, partitions.aPartitions[i].uIndexWin, vrc);
+ goto out;
+ }
+ pszRawName = psz;
+ uStartOffset = 0;
+#else
+ /** @todo not implemented for other hosts. Treat just like
+ * not specified (this code is actually never reached). */
+#endif
+ }
+
+ pPartDesc->pszRawDevice = pszRawName;
+ pPartDesc->uStartOffset = uStartOffset;
+ }
+ else
+ {
+ pPartDesc->pszRawDevice = NULL;
+ pPartDesc->uStartOffset = 0;
+ }
+
+ pPartDesc->uStart = partitions.aPartitions[i].uStart * 512;
+ pPartDesc->cbData = partitions.aPartitions[i].uSize * 512;
+ }
+
+ /* Sort data areas in ascending order of start. */
+ for (unsigned i = 0; i < RawDescriptor.cPartDescs-1; i++)
+ {
+ unsigned uMinIdx = i;
+ uint64_t uMinVal = RawDescriptor.pPartDescs[i].uStart;
+ for (unsigned j = i + 1; j < RawDescriptor.cPartDescs; j++)
+ {
+ if (RawDescriptor.pPartDescs[j].uStart < uMinVal)
+ {
+ uMinIdx = j;
+ uMinVal = RawDescriptor.pPartDescs[j].uStart;
+ }
+ }
+ if (uMinIdx != i)
+ {
+ /* Swap entries at index i and uMinIdx. */
+ VDISKRAWPARTDESC tmp;
+ memcpy(&tmp, &RawDescriptor.pPartDescs[i], sizeof(tmp));
+ memcpy(&RawDescriptor.pPartDescs[i], &RawDescriptor.pPartDescs[uMinIdx], sizeof(tmp));
+ memcpy(&RawDescriptor.pPartDescs[uMinIdx], &tmp, sizeof(tmp));
+ }
+ }
+
+ /* Have a second go at MBR/EPT, GPT area clipping. Now that the data areas
+ * are sorted this is much easier to get 100% right. */
+ //for (unsigned i = 0; i < RawDescriptor.cPartDescs-1; i++)
+ for (unsigned i = 0; i < RawDescriptor.cPartDescs; i++)
+ {
+ if (RawDescriptor.pPartDescs[i].pvPartitionData)
+ {
+ RawDescriptor.pPartDescs[i].cbData = RT_MIN(RawDescriptor.pPartDescs[i+1].uStart - RawDescriptor.pPartDescs[i].uStart, RawDescriptor.pPartDescs[i].cbData);
+ if (!RawDescriptor.pPartDescs[i].cbData)
+ {
+ if (RawDescriptor.uPartitioningType == MBR)
+ {
+ RTMsgError("MBR/EPT overlaps with data area");
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ else
+ {
+ if (RawDescriptor.cPartDescs != i+1)
+ {
+ RTMsgError("GPT overlaps with data area");
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ RTFileClose(hRawFile);
+
+#ifdef DEBUG_klaus
+ if (!(RawDescriptor.uFlags & VDISKRAW_DISK))
+ {
+ RTPrintf("# start length startoffset partdataptr device\n");
+ for (unsigned i = 0; i < RawDescriptor.cPartDescs; i++)
+ {
+ RTPrintf("%2u %14RU64 %14RU64 %14RU64 %#18p %s\n", i,
+ RawDescriptor.pPartDescs[i].uStart,
+ RawDescriptor.pPartDescs[i].cbData,
+ RawDescriptor.pPartDescs[i].uStartOffset,
+ RawDescriptor.pPartDescs[i].pvPartitionData,
+ RawDescriptor.pPartDescs[i].pszRawDevice);
+ }
+ }
+#endif
+
+ VDINTERFACEERROR vdInterfaceError;
+ vdInterfaceError.pfnError = handleVDError;
+ vdInterfaceError.pfnMessage = handleVDMessage;
+
+ rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
+ NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
+ AssertRC(vrc);
+
+ vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk); /* Raw VMDK's are harddisk only. */
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot create the virtual disk container: %Rrc", vrc);
+ goto out;
+ }
+
+ Assert(RT_MIN(cbSize / 512 / 16 / 63, 16383) -
+ (unsigned int)RT_MIN(cbSize / 512 / 16 / 63, 16383) == 0);
+ VDGEOMETRY PCHS, LCHS;
+ PCHS.cCylinders = (unsigned int)RT_MIN(cbSize / 512 / 16 / 63, 16383);
+ PCHS.cHeads = 16;
+ PCHS.cSectors = 63;
+ LCHS.cCylinders = 0;
+ LCHS.cHeads = 0;
+ LCHS.cSectors = 0;
+ vrc = VDCreateBase(pDisk, "VMDK", filename.c_str(), cbSize,
+ VD_IMAGE_FLAGS_FIXED | VD_VMDK_IMAGE_FLAGS_RAWDISK,
+ (char *)&RawDescriptor, &PCHS, &LCHS, NULL,
+ VD_OPEN_FLAGS_NORMAL, NULL, NULL);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot create the raw disk VMDK: %Rrc", vrc);
+ goto out;
+ }
+ RTPrintf("RAW host disk access VMDK file %s created successfully.\n", filename.c_str());
+
+ VDCloseAll(pDisk);
+
+ /* Clean up allocated memory etc. */
+ if (pszPartitions)
+ {
+ for (unsigned i = 0; i < RawDescriptor.cPartDescs; i++)
+ {
+ /* Free memory allocated for relative device name. */
+ if (fRelative && RawDescriptor.pPartDescs[i].pszRawDevice)
+ RTStrFree((char *)(void *)RawDescriptor.pPartDescs[i].pszRawDevice);
+ if (RawDescriptor.pPartDescs[i].pvPartitionData)
+ RTMemFree((void *)RawDescriptor.pPartDescs[i].pvPartitionData);
+ }
+ if (RawDescriptor.pPartDescs)
+ RTMemFree(RawDescriptor.pPartDescs);
+ }
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+
+out:
+ RTMsgError("The raw disk vmdk file was not created");
+ return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static RTEXITCODE CmdRenameVMDK(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+ Utf8Str src;
+ Utf8Str dst;
+ /* Parse the arguments. */
+ for (int i = 0; i < argc; i++)
+ {
+ if (strcmp(argv[i], "-from") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ src = argv[i];
+ }
+ else if (strcmp(argv[i], "-to") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ dst = argv[i];
+ }
+ else
+ {
+ return errorSyntax(USAGE_RENAMEVMDK, "Invalid parameter '%s'", argv[i]);
+ }
+ }
+
+ if (src.isEmpty())
+ return errorSyntax(USAGE_RENAMEVMDK, "Mandatory parameter -from missing");
+ if (dst.isEmpty())
+ return errorSyntax(USAGE_RENAMEVMDK, "Mandatory parameter -to missing");
+
+ PVDISK pDisk = NULL;
+
+ PVDINTERFACE pVDIfs = NULL;
+ VDINTERFACEERROR vdInterfaceError;
+ vdInterfaceError.pfnError = handleVDError;
+ vdInterfaceError.pfnMessage = handleVDMessage;
+
+ int vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
+ NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
+ AssertRC(vrc);
+
+ vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
+ if (RT_FAILURE(vrc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot create the virtual disk container: %Rrc", vrc);
+
+ vrc = VDOpen(pDisk, "VMDK", src.c_str(), VD_OPEN_FLAGS_NORMAL, NULL);
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = VDCopy(pDisk, 0, pDisk, "VMDK", dst.c_str(), true, 0,
+ VD_IMAGE_FLAGS_NONE, NULL, VD_OPEN_FLAGS_NORMAL,
+ NULL, NULL, NULL);
+ if (RT_FAILURE(vrc))
+ RTMsgError("Cannot rename the image: %Rrc", vrc);
+ }
+ else
+ RTMsgError("Cannot create the source image: %Rrc", vrc);
+ VDCloseAll(pDisk);
+ return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static RTEXITCODE CmdConvertToRaw(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+ Utf8Str srcformat;
+ Utf8Str src;
+ Utf8Str dst;
+ bool fWriteToStdOut = false;
+
+ /* Parse the arguments. */
+ for (int i = 0; i < argc; i++)
+ {
+ if (strcmp(argv[i], "-format") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ srcformat = argv[i];
+ }
+ else if (src.isEmpty())
+ {
+ src = argv[i];
+ }
+ else if (dst.isEmpty())
+ {
+ dst = argv[i];
+#ifdef ENABLE_CONVERT_RAW_TO_STDOUT
+ if (!strcmp(argv[i], "stdout"))
+ fWriteToStdOut = true;
+#endif /* ENABLE_CONVERT_RAW_TO_STDOUT */
+ }
+ else
+ {
+ return errorSyntax(USAGE_CONVERTTORAW, "Invalid parameter '%s'", argv[i]);
+ }
+ }
+
+ if (src.isEmpty())
+ return errorSyntax(USAGE_CONVERTTORAW, "Mandatory filename parameter missing");
+ if (dst.isEmpty())
+ return errorSyntax(USAGE_CONVERTTORAW, "Mandatory outputfile parameter missing");
+
+ PVDISK pDisk = NULL;
+
+ PVDINTERFACE pVDIfs = NULL;
+ VDINTERFACEERROR vdInterfaceError;
+ vdInterfaceError.pfnError = handleVDError;
+ vdInterfaceError.pfnMessage = handleVDMessage;
+
+ int vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
+ NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
+ AssertRC(vrc);
+
+ /** @todo Support convert to raw for floppy and DVD images too. */
+ vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
+ if (RT_FAILURE(vrc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot create the virtual disk container: %Rrc", vrc);
+
+ /* Open raw output file. */
+ RTFILE outFile;
+ vrc = VINF_SUCCESS;
+ if (fWriteToStdOut)
+ vrc = RTFileFromNative(&outFile, 1);
+ else
+ vrc = RTFileOpen(&outFile, dst.c_str(), RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
+ if (RT_FAILURE(vrc))
+ {
+ VDCloseAll(pDisk);
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot create destination file \"%s\": %Rrc", dst.c_str(), vrc);
+ }
+
+ if (srcformat.isEmpty())
+ {
+ char *pszFormat = NULL;
+ VDTYPE enmType = VDTYPE_INVALID;
+ vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
+ src.c_str(), &pszFormat, &enmType);
+ if (RT_FAILURE(vrc) || enmType != VDTYPE_HDD)
+ {
+ VDCloseAll(pDisk);
+ if (!fWriteToStdOut)
+ {
+ RTFileClose(outFile);
+ RTFileDelete(dst.c_str());
+ }
+ if (RT_FAILURE(vrc))
+ RTMsgError("No file format specified and autodetect failed - please specify format: %Rrc", vrc);
+ else
+ RTMsgError("Only converting harddisk images is supported");
+ return RTEXITCODE_FAILURE;
+ }
+ srcformat = pszFormat;
+ RTStrFree(pszFormat);
+ }
+ vrc = VDOpen(pDisk, srcformat.c_str(), src.c_str(), VD_OPEN_FLAGS_READONLY, NULL);
+ if (RT_FAILURE(vrc))
+ {
+ VDCloseAll(pDisk);
+ if (!fWriteToStdOut)
+ {
+ RTFileClose(outFile);
+ RTFileDelete(dst.c_str());
+ }
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot open the source image: %Rrc", vrc);
+ }
+
+ uint64_t cbSize = VDGetSize(pDisk, VD_LAST_IMAGE);
+ uint64_t offFile = 0;
+#define RAW_BUFFER_SIZE _128K
+ size_t cbBuf = RAW_BUFFER_SIZE;
+ void *pvBuf = RTMemAlloc(cbBuf);
+ if (pvBuf)
+ {
+ RTStrmPrintf(g_pStdErr, "Converting image \"%s\" with size %RU64 bytes (%RU64MB) to raw...\n", src.c_str(), cbSize, (cbSize + _1M - 1) / _1M);
+ while (offFile < cbSize)
+ {
+ size_t cb = (size_t)RT_MIN(cbSize - offFile, cbBuf);
+ vrc = VDRead(pDisk, offFile, pvBuf, cb);
+ if (RT_FAILURE(vrc))
+ break;
+ vrc = RTFileWrite(outFile, pvBuf, cb, NULL);
+ if (RT_FAILURE(vrc))
+ break;
+ offFile += cb;
+ }
+ RTMemFree(pvBuf);
+ if (RT_FAILURE(vrc))
+ {
+ VDCloseAll(pDisk);
+ if (!fWriteToStdOut)
+ {
+ RTFileClose(outFile);
+ RTFileDelete(dst.c_str());
+ }
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot copy image data: %Rrc", vrc);
+ }
+ }
+ else
+ {
+ vrc = VERR_NO_MEMORY;
+ VDCloseAll(pDisk);
+ if (!fWriteToStdOut)
+ {
+ RTFileClose(outFile);
+ RTFileDelete(dst.c_str());
+ }
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory allocating read buffer");
+ }
+
+ if (!fWriteToStdOut)
+ RTFileClose(outFile);
+ VDCloseAll(pDisk);
+ return RTEXITCODE_SUCCESS;
+}
+
+static RTEXITCODE CmdConvertHardDisk(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+ Utf8Str srcformat;
+ Utf8Str dstformat;
+ Utf8Str src;
+ Utf8Str dst;
+ int vrc;
+ PVDISK pSrcDisk = NULL;
+ PVDISK pDstDisk = NULL;
+ VDTYPE enmSrcType = VDTYPE_INVALID;
+
+ /* Parse the arguments. */
+ for (int i = 0; i < argc; i++)
+ {
+ if (strcmp(argv[i], "-srcformat") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ srcformat = argv[i];
+ }
+ else if (strcmp(argv[i], "-dstformat") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ dstformat = argv[i];
+ }
+ else if (src.isEmpty())
+ {
+ src = argv[i];
+ }
+ else if (dst.isEmpty())
+ {
+ dst = argv[i];
+ }
+ else
+ {
+ return errorSyntax(USAGE_CONVERTHD, "Invalid parameter '%s'", argv[i]);
+ }
+ }
+
+ if (src.isEmpty())
+ return errorSyntax(USAGE_CONVERTHD, "Mandatory input image parameter missing");
+ if (dst.isEmpty())
+ return errorSyntax(USAGE_CONVERTHD, "Mandatory output image parameter missing");
+
+
+ PVDINTERFACE pVDIfs = NULL;
+ VDINTERFACEERROR vdInterfaceError;
+ vdInterfaceError.pfnError = handleVDError;
+ vdInterfaceError.pfnMessage = handleVDMessage;
+
+ vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
+ NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
+ AssertRC(vrc);
+
+ do
+ {
+ /* Try to determine input image format */
+ if (srcformat.isEmpty())
+ {
+ char *pszFormat = NULL;
+ vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
+ src.c_str(), &pszFormat, &enmSrcType);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("No file format specified and autodetect failed - please specify format: %Rrc", vrc);
+ break;
+ }
+ srcformat = pszFormat;
+ RTStrFree(pszFormat);
+ }
+
+ vrc = VDCreate(pVDIfs, enmSrcType, &pSrcDisk);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot create the source virtual disk container: %Rrc", vrc);
+ break;
+ }
+
+ /* Open the input image */
+ vrc = VDOpen(pSrcDisk, srcformat.c_str(), src.c_str(), VD_OPEN_FLAGS_READONLY, NULL);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot open the source image: %Rrc", vrc);
+ break;
+ }
+
+ /* Output format defaults to VDI */
+ if (dstformat.isEmpty())
+ dstformat = "VDI";
+
+ vrc = VDCreate(pVDIfs, enmSrcType, &pDstDisk);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot create the destination virtual disk container: %Rrc", vrc);
+ break;
+ }
+
+ uint64_t cbSize = VDGetSize(pSrcDisk, VD_LAST_IMAGE);
+ RTStrmPrintf(g_pStdErr, "Converting image \"%s\" with size %RU64 bytes (%RU64MB)...\n", src.c_str(), cbSize, (cbSize + _1M - 1) / _1M);
+
+ /* Create the output image */
+ vrc = VDCopy(pSrcDisk, VD_LAST_IMAGE, pDstDisk, dstformat.c_str(),
+ dst.c_str(), false, 0, VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED,
+ NULL, VD_OPEN_FLAGS_NORMAL, NULL, NULL, NULL);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot copy the image: %Rrc", vrc);
+ break;
+ }
+ }
+ while (0);
+ if (pDstDisk)
+ VDCloseAll(pDstDisk);
+ if (pSrcDisk)
+ VDCloseAll(pSrcDisk);
+
+ return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+/**
+ * Tries to repair a corrupted hard disk image.
+ *
+ * @returns VBox status code
+ */
+static RTEXITCODE CmdRepairHardDisk(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+ Utf8Str image;
+ Utf8Str format;
+ int vrc;
+ bool fDryRun = false;
+
+ /* Parse the arguments. */
+ for (int i = 0; i < argc; i++)
+ {
+ if (strcmp(argv[i], "-dry-run") == 0)
+ {
+ fDryRun = true;
+ }
+ else if (strcmp(argv[i], "-format") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ format = argv[i];
+ }
+ else if (image.isEmpty())
+ {
+ image = argv[i];
+ }
+ else
+ {
+ return errorSyntax(USAGE_REPAIRHD, "Invalid parameter '%s'", argv[i]);
+ }
+ }
+
+ if (image.isEmpty())
+ return errorSyntax(USAGE_REPAIRHD, "Mandatory input image parameter missing");
+
+ PVDINTERFACE pVDIfs = NULL;
+ VDINTERFACEERROR vdInterfaceError;
+ vdInterfaceError.pfnError = handleVDError;
+ vdInterfaceError.pfnMessage = handleVDMessage;
+
+ vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
+ NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
+ AssertRC(vrc);
+
+ do
+ {
+ /* Try to determine input image format */
+ if (format.isEmpty())
+ {
+ char *pszFormat = NULL;
+ VDTYPE enmSrcType = VDTYPE_INVALID;
+
+ vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
+ image.c_str(), &pszFormat, &enmSrcType);
+ if (RT_FAILURE(vrc) && (vrc != VERR_VD_IMAGE_CORRUPTED))
+ {
+ RTMsgError("No file format specified and autodetect failed - please specify format: %Rrc", vrc);
+ break;
+ }
+ format = pszFormat;
+ RTStrFree(pszFormat);
+ }
+
+ uint32_t fFlags = 0;
+ if (fDryRun)
+ fFlags |= VD_REPAIR_DRY_RUN;
+
+ vrc = VDRepair(pVDIfs, NULL, image.c_str(), format.c_str(), fFlags);
+ }
+ while (0);
+
+ return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+/**
+ * Unloads the necessary driver.
+ *
+ * @returns VBox status code
+ */
+static RTEXITCODE CmdModUninstall(void)
+{
+ int rc = SUPR3Uninstall();
+ if (RT_SUCCESS(rc) || rc == VERR_NOT_IMPLEMENTED)
+ return RTEXITCODE_SUCCESS;
+ return RTEXITCODE_FAILURE;
+}
+
+/**
+ * Loads the necessary driver.
+ *
+ * @returns VBox status code
+ */
+static RTEXITCODE CmdModInstall(void)
+{
+ int rc = SUPR3Install();
+ if (RT_SUCCESS(rc) || rc == VERR_NOT_IMPLEMENTED)
+ return RTEXITCODE_SUCCESS;
+ return RTEXITCODE_FAILURE;
+}
+
+static RTEXITCODE CmdDebugLog(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ /*
+ * The first parameter is the name or UUID of a VM with a direct session
+ * that we wish to open.
+ */
+ if (argc < 1)
+ return errorSyntax(USAGE_DEBUGLOG, "Missing VM name/UUID");
+
+ ComPtr<IMachine> ptrMachine;
+ HRESULT rc;
+ CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
+ ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
+
+ CHECK_ERROR_RET(ptrMachine, LockMachine(aSession, LockType_Shared), RTEXITCODE_FAILURE);
+
+ /*
+ * Get the debugger interface.
+ */
+ ComPtr<IConsole> ptrConsole;
+ CHECK_ERROR_RET(aSession, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
+
+ ComPtr<IMachineDebugger> ptrDebugger;
+ CHECK_ERROR_RET(ptrConsole, COMGETTER(Debugger)(ptrDebugger.asOutParam()), RTEXITCODE_FAILURE);
+
+ /*
+ * Parse the command.
+ */
+ bool fEnablePresent = false;
+ bool fEnable = false;
+ bool fFlagsPresent = false;
+ RTCString strFlags;
+ bool fGroupsPresent = false;
+ RTCString strGroups;
+ bool fDestsPresent = false;
+ RTCString strDests;
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--disable", 'E', RTGETOPT_REQ_NOTHING },
+ { "--enable", 'e', RTGETOPT_REQ_NOTHING },
+ { "--flags", 'f', RTGETOPT_REQ_STRING },
+ { "--groups", 'g', RTGETOPT_REQ_STRING },
+ { "--destinations", 'd', RTGETOPT_REQ_STRING }
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'e':
+ fEnablePresent = true;
+ fEnable = true;
+ break;
+
+ case 'E':
+ fEnablePresent = true;
+ fEnable = false;
+ break;
+
+ case 'f':
+ fFlagsPresent = true;
+ if (*ValueUnion.psz)
+ {
+ if (strFlags.isNotEmpty())
+ strFlags.append(' ');
+ strFlags.append(ValueUnion.psz);
+ }
+ break;
+
+ case 'g':
+ fGroupsPresent = true;
+ if (*ValueUnion.psz)
+ {
+ if (strGroups.isNotEmpty())
+ strGroups.append(' ');
+ strGroups.append(ValueUnion.psz);
+ }
+ break;
+
+ case 'd':
+ fDestsPresent = true;
+ if (*ValueUnion.psz)
+ {
+ if (strDests.isNotEmpty())
+ strDests.append(' ');
+ strDests.append(ValueUnion.psz);
+ }
+ break;
+
+ default:
+ return errorGetOpt(USAGE_DEBUGLOG, ch, &ValueUnion);
+ }
+ }
+
+ /*
+ * Do the job.
+ */
+ if (fEnablePresent && !fEnable)
+ CHECK_ERROR_RET(ptrDebugger, COMSETTER(LogEnabled)(FALSE), RTEXITCODE_FAILURE);
+
+ /** @todo flags, groups destination. */
+ if (fFlagsPresent || fGroupsPresent || fDestsPresent)
+ RTMsgWarning("One or more of the requested features are not implemented! Feel free to do this.");
+
+ if (fEnablePresent && fEnable)
+ CHECK_ERROR_RET(ptrDebugger, COMSETTER(LogEnabled)(TRUE), RTEXITCODE_FAILURE);
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Generate a SHA-256 password hash
+ */
+static RTEXITCODE CmdGeneratePasswordHash(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+
+ /* one parameter, the password to hash */
+ if (argc != 1)
+ return errorSyntax(USAGE_PASSWORDHASH, "password to hash required");
+
+ uint8_t abDigest[RTSHA256_HASH_SIZE];
+ RTSha256(argv[0], strlen(argv[0]), abDigest);
+ char pszDigest[RTSHA256_DIGEST_LEN + 1];
+ RTSha256ToString(abDigest, pszDigest, sizeof(pszDigest));
+ RTPrintf("Password hash: %s\n", pszDigest);
+
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Print internal guest statistics or
+ * set internal guest statistics update interval if specified
+ */
+static RTEXITCODE CmdGuestStats(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ /* one parameter, guest name */
+ if (argc < 1)
+ return errorSyntax(USAGE_GUESTSTATS, "Missing VM name/UUID");
+
+ /*
+ * Parse the command.
+ */
+ ULONG aUpdateInterval = 0;
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--interval", 'i', RTGETOPT_REQ_UINT32 }
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'i':
+ aUpdateInterval = ValueUnion.u32;
+ break;
+
+ default:
+ return errorGetOpt(USAGE_GUESTSTATS, ch, &ValueUnion);
+ }
+ }
+
+ if (argc > 1 && aUpdateInterval == 0)
+ return errorSyntax(USAGE_GUESTSTATS, "Invalid update interval specified");
+
+ RTPrintf("argc=%d interval=%u\n", argc, aUpdateInterval);
+
+ ComPtr<IMachine> ptrMachine;
+ HRESULT rc;
+ CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
+ ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
+
+ CHECK_ERROR_RET(ptrMachine, LockMachine(aSession, LockType_Shared), RTEXITCODE_FAILURE);
+
+ /*
+ * Get the guest interface.
+ */
+ ComPtr<IConsole> ptrConsole;
+ CHECK_ERROR_RET(aSession, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
+
+ ComPtr<IGuest> ptrGuest;
+ CHECK_ERROR_RET(ptrConsole, COMGETTER(Guest)(ptrGuest.asOutParam()), RTEXITCODE_FAILURE);
+
+ if (aUpdateInterval)
+ CHECK_ERROR_RET(ptrGuest, COMSETTER(StatisticsUpdateInterval)(aUpdateInterval), RTEXITCODE_FAILURE);
+ else
+ {
+ ULONG mCpuUser, mCpuKernel, mCpuIdle;
+ ULONG mMemTotal, mMemFree, mMemBalloon, mMemShared, mMemCache, mPageTotal;
+ ULONG ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal, ulMemSharedTotal;
+
+ CHECK_ERROR_RET(ptrGuest, InternalGetStatistics(&mCpuUser, &mCpuKernel, &mCpuIdle,
+ &mMemTotal, &mMemFree, &mMemBalloon, &mMemShared, &mMemCache,
+ &mPageTotal, &ulMemAllocTotal, &ulMemFreeTotal,
+ &ulMemBalloonTotal, &ulMemSharedTotal),
+ RTEXITCODE_FAILURE);
+ RTPrintf("mCpuUser=%u mCpuKernel=%u mCpuIdle=%u\n"
+ "mMemTotal=%u mMemFree=%u mMemBalloon=%u mMemShared=%u mMemCache=%u\n"
+ "mPageTotal=%u ulMemAllocTotal=%u ulMemFreeTotal=%u ulMemBalloonTotal=%u ulMemSharedTotal=%u\n",
+ mCpuUser, mCpuKernel, mCpuIdle,
+ mMemTotal, mMemFree, mMemBalloon, mMemShared, mMemCache,
+ mPageTotal, ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal, ulMemSharedTotal);
+
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Wrapper for handling internal commands
+ */
+RTEXITCODE handleInternalCommands(HandlerArg *a)
+{
+ g_fInternalMode = true;
+
+ /* at least a command is required */
+ if (a->argc < 1)
+ return errorSyntax(USAGE_ALL, "Command missing");
+
+ /*
+ * The 'string switch' on command name.
+ */
+ const char *pszCmd = a->argv[0];
+ if (!strcmp(pszCmd, "loadmap"))
+ return CmdLoadMap(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "loadsyms"))
+ return CmdLoadSyms(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ //if (!strcmp(pszCmd, "unloadsyms"))
+ // return CmdUnloadSyms(argc - 1, &a->argv[1]);
+ if (!strcmp(pszCmd, "sethduuid") || !strcmp(pszCmd, "sethdparentuuid"))
+ return CmdSetHDUUID(a->argc, &a->argv[0], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "dumphdinfo"))
+ return CmdDumpHDInfo(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "listpartitions"))
+ return CmdListPartitions(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "createrawvmdk"))
+ return CmdCreateRawVMDK(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "renamevmdk"))
+ return CmdRenameVMDK(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "converttoraw"))
+ return CmdConvertToRaw(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "converthd"))
+ return CmdConvertHardDisk(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "modinstall"))
+ return CmdModInstall();
+ if (!strcmp(pszCmd, "moduninstall"))
+ return CmdModUninstall();
+ if (!strcmp(pszCmd, "debuglog"))
+ return CmdDebugLog(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "passwordhash"))
+ return CmdGeneratePasswordHash(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "gueststats"))
+ return CmdGuestStats(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "repairhd"))
+ return CmdRepairHardDisk(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+
+ /* default: */
+ return errorSyntax(USAGE_ALL, "Invalid command '%s'", a->argv[0]);
+}
+