/* $Id: VBoxManageDisk.cpp 53354 2014-11-19 18:32:03Z vboxsync $ */ /** @file * VBoxManage - The disk related commands. */ /* * Copyright (C) 2006-2013 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. */ #ifndef VBOX_ONLY_DOCS /******************************************************************************* * Header Files * *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "VBoxManage.h" using namespace com; // funcs /////////////////////////////////////////////////////////////////////////////// static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va) { RTMsgErrorV(pszFormat, va); RTMsgError("Error code %Rrc at %s(%u) in function %s", rc, RT_SRC_POS_ARGS); } static int parseDiskVariant(const char *psz, MediumVariant_T *pDiskVariant) { int rc = VINF_SUCCESS; unsigned DiskVariant = (unsigned)(*pDiskVariant); while (psz && *psz && RT_SUCCESS(rc)) { size_t len; const char *pszComma = strchr(psz, ','); if (pszComma) len = pszComma - psz; else len = strlen(psz); if (len > 0) { // Parsing is intentionally inconsistent: "standard" resets the // variant, whereas the other flags are cumulative. if (!RTStrNICmp(psz, "standard", len)) DiskVariant = MediumVariant_Standard; else if ( !RTStrNICmp(psz, "fixed", len) || !RTStrNICmp(psz, "static", len)) DiskVariant |= MediumVariant_Fixed; else if (!RTStrNICmp(psz, "Diff", len)) DiskVariant |= MediumVariant_Diff; else if (!RTStrNICmp(psz, "split2g", len)) DiskVariant |= MediumVariant_VmdkSplit2G; else if ( !RTStrNICmp(psz, "stream", len) || !RTStrNICmp(psz, "streamoptimized", len)) DiskVariant |= MediumVariant_VmdkStreamOptimized; else if (!RTStrNICmp(psz, "esx", len)) DiskVariant |= MediumVariant_VmdkESX; else rc = VERR_PARSE_ERROR; } if (pszComma) psz += len + 1; else psz += len; } if (RT_SUCCESS(rc)) *pDiskVariant = (MediumVariant_T)DiskVariant; return rc; } int parseDiskType(const char *psz, MediumType_T *pDiskType) { int rc = VINF_SUCCESS; MediumType_T DiskType = MediumType_Normal; if (!RTStrICmp(psz, "normal")) DiskType = MediumType_Normal; else if (!RTStrICmp(psz, "immutable")) DiskType = MediumType_Immutable; else if (!RTStrICmp(psz, "writethrough")) DiskType = MediumType_Writethrough; else if (!RTStrICmp(psz, "shareable")) DiskType = MediumType_Shareable; else if (!RTStrICmp(psz, "readonly")) DiskType = MediumType_Readonly; else if (!RTStrICmp(psz, "multiattach")) DiskType = MediumType_MultiAttach; else rc = VERR_PARSE_ERROR; if (RT_SUCCESS(rc)) *pDiskType = DiskType; return rc; } /** @todo move this into getopt, as getting bool values is generic */ int parseBool(const char *psz, bool *pb) { int rc = VINF_SUCCESS; if ( !RTStrICmp(psz, "on") || !RTStrICmp(psz, "yes") || !RTStrICmp(psz, "true") || !RTStrICmp(psz, "1") || !RTStrICmp(psz, "enable") || !RTStrICmp(psz, "enabled")) { *pb = true; } else if ( !RTStrICmp(psz, "off") || !RTStrICmp(psz, "no") || !RTStrICmp(psz, "false") || !RTStrICmp(psz, "0") || !RTStrICmp(psz, "disable") || !RTStrICmp(psz, "disabled")) { *pb = false; } else rc = VERR_PARSE_ERROR; return rc; } HRESULT openMedium(HandlerArg *a, const char *pszFilenameOrUuid, DeviceType_T enmDevType, AccessMode_T enmAccessMode, ComPtr &pMedium, bool fForceNewUuidOnOpen, bool fSilent) { HRESULT rc; Guid id(pszFilenameOrUuid); char szFilenameAbs[RTPATH_MAX] = ""; /* If it is no UUID, convert the filename to an absolute one. */ if (!id.isValid()) { int irc = RTPathAbs(pszFilenameOrUuid, szFilenameAbs, sizeof(szFilenameAbs)); if (RT_FAILURE(irc)) { if (!fSilent) RTMsgError("Cannot convert filename \"%s\" to absolute path", pszFilenameOrUuid); return E_FAIL; } pszFilenameOrUuid = szFilenameAbs; } if (!fSilent) CHECK_ERROR(a->virtualBox, OpenMedium(Bstr(pszFilenameOrUuid).raw(), enmDevType, enmAccessMode, fForceNewUuidOnOpen, pMedium.asOutParam())); else rc = a->virtualBox->OpenMedium(Bstr(pszFilenameOrUuid).raw(), enmDevType, enmAccessMode, fForceNewUuidOnOpen, pMedium.asOutParam()); return rc; } static HRESULT createFloppy(HandlerArg *a, const char *pszFormat, const char *pszFilename, ComPtr &pMedium) { HRESULT rc; char szFilenameAbs[RTPATH_MAX] = ""; /** @todo laziness shortcut. should really check the MediumFormatCapabilities */ if (RTStrICmp(pszFormat, "iSCSI")) { int irc = RTPathAbs(pszFilename, szFilenameAbs, sizeof(szFilenameAbs)); if (RT_FAILURE(irc)) { RTMsgError("Cannot convert filename \"%s\" to absolute path", pszFilename); return E_FAIL; } pszFilename = szFilenameAbs; } CHECK_ERROR(a->virtualBox, CreateMedium(Bstr(pszFormat).raw(), Bstr(pszFilename).raw(), AccessMode_ReadWrite, TRUE, DeviceType_Floppy, pMedium.asOutParam())); return rc; } static HRESULT createDVD(HandlerArg *a, const char *pszFormat, const char *pszFilename, ComPtr &pMedium) { HRESULT rc; char szFilenameAbs[RTPATH_MAX] = ""; /** @todo laziness shortcut. should really check the MediumFormatCapabilities */ if (RTStrICmp(pszFormat, "iSCSI")) { int irc = RTPathAbs(pszFilename, szFilenameAbs, sizeof(szFilenameAbs)); if (RT_FAILURE(irc)) { RTMsgError("Cannot convert filename \"%s\" to absolute path", pszFilename); return E_FAIL; } pszFilename = szFilenameAbs; } CHECK_ERROR(a->virtualBox, CreateMedium(Bstr(pszFormat).raw(), Bstr(pszFilename).raw(), AccessMode_ReadWrite, TRUE, DeviceType_DVD, pMedium.asOutParam())); return rc; } static HRESULT createHardDisk(HandlerArg *a, const char *pszFormat, const char *pszFilename, ComPtr &pMedium) { HRESULT rc; char szFilenameAbs[RTPATH_MAX] = ""; /** @todo laziness shortcut. should really check the MediumFormatCapabilities */ if (RTStrICmp(pszFormat, "iSCSI")) { int irc = RTPathAbs(pszFilename, szFilenameAbs, sizeof(szFilenameAbs)); if (RT_FAILURE(irc)) { RTMsgError("Cannot convert filename \"%s\" to absolute path", pszFilename); return E_FAIL; } pszFilename = szFilenameAbs; } CHECK_ERROR(a->virtualBox, CreateHardDisk(Bstr(pszFormat).raw(), Bstr(pszFilename).raw(), pMedium.asOutParam())); return rc; } static const RTGETOPTDEF g_aCreateHardDiskOptions[] = { { "--filename", 'f', RTGETOPT_REQ_STRING }, { "-filename", 'f', RTGETOPT_REQ_STRING }, // deprecated { "--diffparent", 'd', RTGETOPT_REQ_STRING }, { "--size", 's', RTGETOPT_REQ_UINT64 }, { "-size", 's', RTGETOPT_REQ_UINT64 }, // deprecated { "--sizebyte", 'S', RTGETOPT_REQ_UINT64 }, { "--format", 'o', RTGETOPT_REQ_STRING }, { "-format", 'o', RTGETOPT_REQ_STRING }, // deprecated { "--static", 'F', RTGETOPT_REQ_NOTHING }, { "-static", 'F', RTGETOPT_REQ_NOTHING }, // deprecated { "--variant", 'm', RTGETOPT_REQ_STRING }, { "-variant", 'm', RTGETOPT_REQ_STRING }, // deprecated }; int handleCreateHardDisk(HandlerArg *a) { HRESULT rc; int vrc; const char *filename = NULL; const char *diffparent = NULL; uint64_t size = 0; const char *format = NULL; bool fBase = true; MediumVariant_T DiskVariant = MediumVariant_Standard; int c; RTGETOPTUNION ValueUnion; RTGETOPTSTATE GetState; // start at 0 because main() has hacked both the argc and argv given to us RTGetOptInit(&GetState, a->argc, a->argv, g_aCreateHardDiskOptions, RT_ELEMENTS(g_aCreateHardDiskOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS); while ((c = RTGetOpt(&GetState, &ValueUnion))) { switch (c) { case 'f': // --filename filename = ValueUnion.psz; break; case 'd': // --diffparent diffparent = ValueUnion.psz; fBase = false; break; case 's': // --size size = ValueUnion.u64 * _1M; break; case 'S': // --sizebyte size = ValueUnion.u64; break; case 'o': // --format format = ValueUnion.psz; break; case 'F': // --static ("fixed"/"flat") { unsigned uDiskVariant = (unsigned)DiskVariant; uDiskVariant |= MediumVariant_Fixed; DiskVariant = (MediumVariant_T)uDiskVariant; break; } case 'm': // --variant vrc = parseDiskVariant(ValueUnion.psz, &DiskVariant); if (RT_FAILURE(vrc)) return errorArgument("Invalid hard disk variant '%s'", ValueUnion.psz); break; case VINF_GETOPT_NOT_OPTION: return errorSyntax(USAGE_CREATEHD, "Invalid parameter '%s'", ValueUnion.psz); default: if (c > 0) { if (RT_C_IS_PRINT(c)) return errorSyntax(USAGE_CREATEHD, "Invalid option -%c", c); else return errorSyntax(USAGE_CREATEHD, "Invalid option case %i", c); } else if (c == VERR_GETOPT_UNKNOWN_OPTION) return errorSyntax(USAGE_CREATEHD, "unknown option: %s\n", ValueUnion.psz); else if (ValueUnion.pDef) return errorSyntax(USAGE_CREATEHD, "%s: %Rrs", ValueUnion.pDef->pszLong, c); else return errorSyntax(USAGE_CREATEHD, "error: %Rrs", c); } } /* check the outcome */ ComPtr parentHardDisk; if (fBase) { if ( !filename || !*filename || size == 0) return errorSyntax(USAGE_CREATEHD, "Parameters --filename and --size are required"); if (!format || !*format) format = "VDI"; } else { if ( !filename || !*filename) return errorSyntax(USAGE_CREATEHD, "Parameters --filename is required"); size = 0; DiskVariant = MediumVariant_Diff; if (!format || !*format) { const char *pszExt = RTPathSuffix(filename); /* Skip over . if there is an extension. */ if (pszExt) pszExt++; if (!pszExt || !*pszExt) format = "VDI"; else format = pszExt; } rc = openMedium(a, diffparent, DeviceType_HardDisk, AccessMode_ReadWrite, parentHardDisk, false /* fForceNewUuidOnOpen */, false /* fSilent */); if (FAILED(rc)) return 1; if (parentHardDisk.isNull()) { RTMsgError("Invalid parent hard disk reference, avoiding crash"); return 1; } MediumState_T state; CHECK_ERROR(parentHardDisk, COMGETTER(State)(&state)); if (FAILED(rc)) return 1; if (state == MediumState_Inaccessible) { CHECK_ERROR(parentHardDisk, RefreshState(&state)); if (FAILED(rc)) return 1; } } /* check for filename extension */ /** @todo use IMediumFormat to cover all extensions generically */ Utf8Str strName(filename); if (!RTPathHasSuffix(strName.c_str())) { Utf8Str strFormat(format); if (strFormat.compare("vmdk", RTCString::CaseInsensitive) == 0) strName.append(".vmdk"); else if (strFormat.compare("vhd", RTCString::CaseInsensitive) == 0) strName.append(".vhd"); else strName.append(".vdi"); filename = strName.c_str(); } ComPtr hardDisk; rc = createHardDisk(a, format, filename, hardDisk); if (SUCCEEDED(rc) && hardDisk) { ComPtr progress; com::SafeArray l_variants(sizeof(MediumVariant_T)*8); for (ULONG i = 0; i < l_variants.size(); ++i) { ULONG temp = DiskVariant; temp &= 1<argc, a->argv, g_aCreateHardDiskOptions, RT_ELEMENTS(g_aCreateHardDiskOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS); while ((c = RTGetOpt(&GetState, &ValueUnion))) { switch (c) { case 'f': // --filename filename = ValueUnion.psz; break; case 'd': // --diffparent diffparent = ValueUnion.psz; fBase = false; break; case 's': // --size size = ValueUnion.u64 * _1M; break; case 'S': // --sizebyte size = ValueUnion.u64; break; case 'o': // --format format = ValueUnion.psz; break; case 'F': // --static ("fixed"/"flat") { unsigned uDiskVariant = (unsigned)DiskVariant; uDiskVariant |= MediumVariant_Fixed; DiskVariant = (MediumVariant_T)uDiskVariant; break; } case 'm': // --variant vrc = parseDiskVariant(ValueUnion.psz, &DiskVariant); if (RT_FAILURE(vrc)) return errorArgument("Invalid medium variant '%s'", ValueUnion.psz); break; case VINF_GETOPT_NOT_OPTION: return errorSyntax(USAGE_CREATEFLOPPY, "Invalid parameter '%s'", ValueUnion.psz); default: if (c > 0) { if (RT_C_IS_PRINT(c)) return errorSyntax(USAGE_CREATEFLOPPY, "Invalid option -%c", c); else return errorSyntax(USAGE_CREATEFLOPPY, "Invalid option case %i", c); } else if (c == VERR_GETOPT_UNKNOWN_OPTION) return errorSyntax(USAGE_CREATEFLOPPY, "unknown option: %s\n", ValueUnion.psz); else if (ValueUnion.pDef) return errorSyntax(USAGE_CREATEFLOPPY, "%s: %Rrs", ValueUnion.pDef->pszLong, c); else return errorSyntax(USAGE_CREATEFLOPPY, "error: %Rrs", c); } } /* check the outcome */ ComPtr parentMedium; if (fBase) { if ( !filename || !*filename || size == 0) return errorSyntax(USAGE_CREATEHD, "Parameters --filename and --size are required"); if (!format || !*format) format = "RAW"; } else { if ( !filename || !*filename) return errorSyntax(USAGE_CREATEHD, "Parameters --filename is required"); size = 0; DiskVariant = MediumVariant_Diff; if (!format || !*format) { const char *pszExt = RTPathSuffix(filename); /* Skip over . if there is an extension. */ if (pszExt) pszExt++; if (!pszExt || !*pszExt) format = "RAW"; else format = pszExt; } rc = openMedium(a, diffparent, DeviceType_Floppy, AccessMode_ReadWrite, parentMedium, false /* fForceNewUuidOnOpen */, false /* fSilent */); if (FAILED(rc)) return 1; if (parentMedium.isNull()) { RTMsgError("Invalid parent medium reference, avoiding crash"); return 1; } MediumState_T state; CHECK_ERROR(parentMedium, COMGETTER(State)(&state)); if (FAILED(rc)) return 1; if (state == MediumState_Inaccessible) { CHECK_ERROR(parentMedium, RefreshState(&state)); if (FAILED(rc)) return 1; } } /* check for filename extension */ /** @todo use IMediumFormat to cover all extensions generically */ Utf8Str strName(filename); if (!RTPathHasSuffix(strName.c_str())) { Utf8Str strFormat(format); strName.append(".raw"); filename = strName.c_str(); } ComPtr medium; rc = createFloppy(a, format, filename, medium); if (SUCCEEDED(rc) && medium) { ComPtr progress; com::SafeArray l_variants(sizeof(MediumVariant_T)*8); for (ULONG i = 0; i < l_variants.size(); ++i) { ULONG temp = DiskVariant; temp &= 1<argc, a->argv, g_aCreateHardDiskOptions, RT_ELEMENTS(g_aCreateHardDiskOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS); while ((c = RTGetOpt(&GetState, &ValueUnion))) { switch (c) { case 'f': // --filename filename = ValueUnion.psz; break; case 'd': // --diffparent diffparent = ValueUnion.psz; fBase = false; break; case 's': // --size size = ValueUnion.u64 * _1M; break; case 'S': // --sizebyte size = ValueUnion.u64; break; case 'o': // --format format = ValueUnion.psz; break; case 'F': // --static ("fixed"/"flat") { unsigned uDiskVariant = (unsigned)DiskVariant; uDiskVariant |= MediumVariant_Fixed; DiskVariant = (MediumVariant_T)uDiskVariant; break; } case 'm': // --variant vrc = parseDiskVariant(ValueUnion.psz, &DiskVariant); if (RT_FAILURE(vrc)) return errorArgument("Invalid medium variant '%s'", ValueUnion.psz); break; case VINF_GETOPT_NOT_OPTION: return errorSyntax(USAGE_CREATEDVD, "Invalid parameter '%s'", ValueUnion.psz); default: if (c > 0) { if (RT_C_IS_PRINT(c)) return errorSyntax(USAGE_CREATEDVD, "Invalid option -%c", c); else return errorSyntax(USAGE_CREATEDVD, "Invalid option case %i", c); } else if (c == VERR_GETOPT_UNKNOWN_OPTION) return errorSyntax(USAGE_CREATEDVD, "unknown option: %s\n", ValueUnion.psz); else if (ValueUnion.pDef) return errorSyntax(USAGE_CREATEDVD, "%s: %Rrs", ValueUnion.pDef->pszLong, c); else return errorSyntax(USAGE_CREATEDVD, "error: %Rrs", c); } } /* check the outcome */ ComPtr parentMedium; if (fBase) { if ( !filename || !*filename || size == 0) return errorSyntax(USAGE_CREATEHD, "Parameters --filename and --size are required"); if (!format || !*format) format = "ISO"; } else { if ( !filename || !*filename) return errorSyntax(USAGE_CREATEHD, "Parameters --filename is required"); size = 0; DiskVariant = MediumVariant_Diff; if (!format || !*format) { const char *pszExt = RTPathSuffix(filename); /* Skip over . if there is an extension. */ if (pszExt) pszExt++; if (!pszExt || !*pszExt) format = "ISO"; else format = pszExt; } rc = openMedium(a, diffparent, DeviceType_DVD, AccessMode_ReadWrite, parentMedium, false /* fForceNewUuidOnOpen */, false /* fSilent */); if (FAILED(rc)) return 1; if (parentMedium.isNull()) { RTMsgError("Invalid parent hard disk reference, avoiding crash"); return 1; } MediumState_T state; CHECK_ERROR(parentMedium, COMGETTER(State)(&state)); if (FAILED(rc)) return 1; if (state == MediumState_Inaccessible) { CHECK_ERROR(parentMedium, RefreshState(&state)); if (FAILED(rc)) return 1; } } /* check for filename extension */ /** @todo use IMediumFormat to cover all extensions generically */ Utf8Str strName(filename); if (!RTPathHasSuffix(strName.c_str())) { Utf8Str strFormat(format); strName.append(".iso"); filename = strName.c_str(); } ComPtr medium; rc = createDVD(a, format, filename, medium); if (SUCCEEDED(rc) && medium) { ComPtr progress; com::SafeArray l_variants(sizeof(MediumVariant_T)*8); for (ULONG i = 0; i < l_variants.size(); ++i) { ULONG temp = DiskVariant; temp &= 1< hardDisk; MediumType_T DiskType; bool AutoReset = false; SafeArray mediumPropNames; SafeArray mediumPropValues; bool fModifyDiskType = false; bool fModifyAutoReset = false; bool fModifyProperties = false; bool fModifyCompact = false; bool fModifyResize = false; uint64_t cbResize = 0; const char *FilenameOrUuid = NULL; int c; RTGETOPTUNION ValueUnion; RTGETOPTSTATE GetState; // start at 0 because main() has hacked both the argc and argv given to us RTGetOptInit(&GetState, a->argc, a->argv, g_aModifyHardDiskOptions, RT_ELEMENTS(g_aModifyHardDiskOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS); while ((c = RTGetOpt(&GetState, &ValueUnion))) { switch (c) { case 't': // --type vrc = parseDiskType(ValueUnion.psz, &DiskType); if (RT_FAILURE(vrc)) return errorArgument("Invalid hard disk type '%s'", ValueUnion.psz); fModifyDiskType = true; break; case 'z': // --autoreset vrc = parseBool(ValueUnion.psz, &AutoReset); if (RT_FAILURE(vrc)) return errorArgument("Invalid autoreset parameter '%s'", ValueUnion.psz); fModifyAutoReset = true; break; case 'p': // --property { /* Parse 'name=value' */ char *pszProperty = RTStrDup(ValueUnion.psz); if (pszProperty) { char *pDelimiter = strchr(pszProperty, '='); if (pDelimiter) { *pDelimiter = '\0'; Bstr bstrName(pszProperty); Bstr bstrValue(&pDelimiter[1]); bstrName.detachTo(mediumPropNames.appendedRaw()); bstrValue.detachTo(mediumPropValues.appendedRaw()); fModifyProperties = true; } else { errorArgument("Invalid --property argument '%s'", ValueUnion.psz); rc = E_FAIL; } RTStrFree(pszProperty); } else { RTStrmPrintf(g_pStdErr, "Error: Failed to allocate memory for medium property '%s'\n", ValueUnion.psz); rc = E_FAIL; } break; } case 'c': // --compact fModifyCompact = true; break; case 'r': // --resize cbResize = ValueUnion.u64 * _1M; fModifyResize = true; break; case 'R': // --resizebyte cbResize = ValueUnion.u64; fModifyResize = true; break; case VINF_GETOPT_NOT_OPTION: if (!FilenameOrUuid) FilenameOrUuid = ValueUnion.psz; else return errorSyntax(USAGE_MODIFYHD, "Invalid parameter '%s'", ValueUnion.psz); break; default: if (c > 0) { if (RT_C_IS_PRINT(c)) return errorSyntax(USAGE_MODIFYHD, "Invalid option -%c", c); else return errorSyntax(USAGE_MODIFYHD, "Invalid option case %i", c); } else if (c == VERR_GETOPT_UNKNOWN_OPTION) return errorSyntax(USAGE_MODIFYHD, "unknown option: %s\n", ValueUnion.psz); else if (ValueUnion.pDef) return errorSyntax(USAGE_MODIFYHD, "%s: %Rrs", ValueUnion.pDef->pszLong, c); else return errorSyntax(USAGE_MODIFYHD, "error: %Rrs", c); } } if (!FilenameOrUuid) return errorSyntax(USAGE_MODIFYHD, "Disk name or UUID required"); if (!fModifyDiskType && !fModifyAutoReset && !fModifyProperties && !fModifyCompact && !fModifyResize) return errorSyntax(USAGE_MODIFYHD, "No operation specified"); /* Always open the medium if necessary, there is no other way. */ rc = openMedium(a, FilenameOrUuid, DeviceType_HardDisk, AccessMode_ReadWrite, hardDisk, false /* fForceNewUuidOnOpen */, false /* fSilent */); if (FAILED(rc)) return 1; if (hardDisk.isNull()) { RTMsgError("Invalid hard disk reference, avoiding crash"); return 1; } if (fModifyDiskType) { MediumType_T hddType; CHECK_ERROR(hardDisk, COMGETTER(Type)(&hddType)); if (hddType != DiskType) CHECK_ERROR(hardDisk, COMSETTER(Type)(DiskType)); } if (fModifyAutoReset) { CHECK_ERROR(hardDisk, COMSETTER(AutoReset)(AutoReset)); } if (fModifyProperties) { CHECK_ERROR(hardDisk, SetProperties(ComSafeArrayAsInParam(mediumPropNames), ComSafeArrayAsInParam(mediumPropValues))); } if (fModifyCompact) { ComPtr progress; CHECK_ERROR(hardDisk, Compact(progress.asOutParam())); if (SUCCEEDED(rc)) rc = showProgress(progress); if (FAILED(rc)) { if (rc == E_NOTIMPL) RTMsgError("Compact hard disk operation is not implemented!"); else if (rc == VBOX_E_NOT_SUPPORTED) RTMsgError("Compact hard disk operation for this format is not implemented yet!"); else if (!progress.isNull()) CHECK_PROGRESS_ERROR(progress, ("Failed to compact hard disk")); else RTMsgError("Failed to compact hard disk!"); } } if (fModifyResize) { ComPtr progress; CHECK_ERROR(hardDisk, Resize(cbResize, progress.asOutParam())); if (SUCCEEDED(rc)) rc = showProgress(progress); if (FAILED(rc)) { if (rc == E_NOTIMPL) RTMsgError("Resize hard disk operation is not implemented!"); else if (rc == VBOX_E_NOT_SUPPORTED) RTMsgError("Resize hard disk operation for this format is not implemented yet!"); else CHECK_PROGRESS_ERROR(progress, ("Failed to resize hard disk")); } } return SUCCEEDED(rc) ? 0 : 1; } static const RTGETOPTDEF g_aCloneHardDiskOptions[] = { { "--format", 'o', RTGETOPT_REQ_STRING }, { "-format", 'o', RTGETOPT_REQ_STRING }, { "--static", 'F', RTGETOPT_REQ_NOTHING }, { "-static", 'F', RTGETOPT_REQ_NOTHING }, { "--existing", 'E', RTGETOPT_REQ_NOTHING }, { "--variant", 'm', RTGETOPT_REQ_STRING }, { "-variant", 'm', RTGETOPT_REQ_STRING }, }; int handleCloneHardDisk(HandlerArg *a) { HRESULT rc; int vrc; const char *pszSrc = NULL; const char *pszDst = NULL; Bstr format; MediumVariant_T DiskVariant = MediumVariant_Standard; bool fExisting = false; int c; RTGETOPTUNION ValueUnion; RTGETOPTSTATE GetState; // start at 0 because main() has hacked both the argc and argv given to us RTGetOptInit(&GetState, a->argc, a->argv, g_aCloneHardDiskOptions, RT_ELEMENTS(g_aCloneHardDiskOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS); while ((c = RTGetOpt(&GetState, &ValueUnion))) { switch (c) { case 'o': // --format format = ValueUnion.psz; break; case 'F': // --static { unsigned uDiskVariant = (unsigned)DiskVariant; uDiskVariant |= MediumVariant_Fixed; DiskVariant = (MediumVariant_T)uDiskVariant; break; } case 'E': // --existing fExisting = true; break; case 'm': // --variant vrc = parseDiskVariant(ValueUnion.psz, &DiskVariant); if (RT_FAILURE(vrc)) return errorArgument("Invalid hard disk variant '%s'", ValueUnion.psz); break; case VINF_GETOPT_NOT_OPTION: if (!pszSrc) pszSrc = ValueUnion.psz; else if (!pszDst) pszDst = ValueUnion.psz; else return errorSyntax(USAGE_CLONEHD, "Invalid parameter '%s'", ValueUnion.psz); break; default: if (c > 0) { if (RT_C_IS_GRAPH(c)) return errorSyntax(USAGE_CLONEHD, "unhandled option: -%c", c); else return errorSyntax(USAGE_CLONEHD, "unhandled option: %i", c); } else if (c == VERR_GETOPT_UNKNOWN_OPTION) return errorSyntax(USAGE_CLONEHD, "unknown option: %s", ValueUnion.psz); else if (ValueUnion.pDef) return errorSyntax(USAGE_CLONEHD, "%s: %Rrs", ValueUnion.pDef->pszLong, c); else return errorSyntax(USAGE_CLONEHD, "error: %Rrs", c); } } if (!pszSrc) return errorSyntax(USAGE_CLONEHD, "Mandatory UUID or input file parameter missing"); if (!pszDst) return errorSyntax(USAGE_CLONEHD, "Mandatory output file parameter missing"); if (fExisting && (!format.isEmpty() || DiskVariant != MediumType_Normal)) return errorSyntax(USAGE_CLONEHD, "Specified options which cannot be used with --existing"); ComPtr srcDisk; ComPtr dstDisk; rc = openMedium(a, pszSrc, DeviceType_HardDisk, AccessMode_ReadOnly, srcDisk, false /* fForceNewUuidOnOpen */, false /* fSilent */); if (FAILED(rc)) return 1; do { /* open/create destination hard disk */ if (fExisting) { rc = openMedium(a, pszDst, DeviceType_HardDisk, AccessMode_ReadWrite, dstDisk, false /* fForceNewUuidOnOpen */, false /* fSilent */); if (FAILED(rc)) break; /* Perform accessibility check now. */ MediumState_T state; CHECK_ERROR_BREAK(dstDisk, RefreshState(&state)); CHECK_ERROR_BREAK(dstDisk, COMGETTER(Format)(format.asOutParam())); } else { /* use the format of the source hard disk if unspecified */ if (format.isEmpty()) CHECK_ERROR_BREAK(srcDisk, COMGETTER(Format)(format.asOutParam())); rc = createHardDisk(a, Utf8Str(format).c_str(), pszDst, dstDisk); if (FAILED(rc)) break; } ComPtr progress; com::SafeArray l_variants(sizeof(MediumVariant_T)*8); for (ULONG i = 0; i < l_variants.size(); ++i) { ULONG temp = DiskVariant; temp &= 1<= (uint64_t)cbBuffer ? cbBuffer : (size_t)(cbFile - offFile); rc = RTFileRead(File, pvBuf, cbToRead, &cbRead); if (RT_FAILURE(rc) || !cbRead) break; rc = VDWrite(pDisk, offFile, pvBuf, cbRead); if (RT_FAILURE(rc)) { RTMsgError("Failed to write to disk image \"%s\": %Rrc", dstfilename, rc); goto out; } offFile += cbRead; } out: if (pvBuf) RTMemFree(pvBuf); if (pDisk) VDClose(pDisk, RT_FAILURE(rc)); if (File != NIL_RTFILE) RTFileClose(File); return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; } HRESULT showMediumInfo(const ComPtr &pVirtualBox, const ComPtr &pMedium, const char *pszParentUUID, bool fOptLong) { HRESULT rc = S_OK; do { Bstr uuid; pMedium->COMGETTER(Id)(uuid.asOutParam()); RTPrintf("UUID: %ls\n", uuid.raw()); if (pszParentUUID) RTPrintf("Parent UUID: %s\n", pszParentUUID); /* check for accessibility */ MediumState_T enmState; CHECK_ERROR_BREAK(pMedium, RefreshState(&enmState)); pMedium->RefreshState(&enmState); const char *pszState = "unknown"; switch (enmState) { case MediumState_NotCreated: pszState = "not created"; break; case MediumState_Created: pszState = "created"; break; case MediumState_LockedRead: pszState = "locked read"; break; case MediumState_LockedWrite: pszState = "locked write"; break; case MediumState_Inaccessible: pszState = "inaccessible"; break; case MediumState_Creating: pszState = "creating"; break; case MediumState_Deleting: pszState = "deleting"; break; } RTPrintf("State: %s\n", pszState); if (fOptLong && enmState == MediumState_Inaccessible) { Bstr err; CHECK_ERROR_BREAK(pMedium, COMGETTER(LastAccessError)(err.asOutParam())); RTPrintf("Access Error: %ls\n", err.raw()); } if (fOptLong) { Bstr description; pMedium->COMGETTER(Description)(description.asOutParam()); if (!description.isEmpty()) RTPrintf("Description: %ls\n", description.raw()); } MediumType_T type; pMedium->COMGETTER(Type)(&type); const char *typeStr = "unknown"; switch (type) { case MediumType_Normal: if (pszParentUUID && Guid(pszParentUUID).isValid()) typeStr = "normal (differencing)"; else typeStr = "normal (base)"; break; case MediumType_Immutable: typeStr = "immutable"; break; case MediumType_Writethrough: typeStr = "writethrough"; break; case MediumType_Shareable: typeStr = "shareable"; break; case MediumType_Readonly: typeStr = "readonly"; break; case MediumType_MultiAttach: typeStr = "multiattach"; break; } RTPrintf("Type: %s\n", typeStr); /* print out information specific for differencing hard disks */ if (fOptLong && pszParentUUID && Guid(pszParentUUID).isValid()) { BOOL autoReset = FALSE; pMedium->COMGETTER(AutoReset)(&autoReset); RTPrintf("Auto-Reset: %s\n", autoReset ? "on" : "off"); } Bstr loc; pMedium->COMGETTER(Location)(loc.asOutParam()); RTPrintf("Location: %ls\n", loc.raw()); Bstr format; pMedium->COMGETTER(Format)(format.asOutParam()); RTPrintf("Storage format: %ls\n", format.raw()); if (fOptLong) { com::SafeArray safeArray_variant; pMedium->COMGETTER(Variant)(ComSafeArrayAsOutParam(safeArray_variant)); ULONG variant=0; for (size_t i = 0; i < safeArray_variant.size(); i++) variant |= safeArray_variant[i]; const char *variantStr = "unknown"; switch (variant & ~(MediumVariant_Fixed | MediumVariant_Diff)) { case MediumVariant_VmdkSplit2G: variantStr = "split2G"; break; case MediumVariant_VmdkStreamOptimized: variantStr = "streamOptimized"; break; case MediumVariant_VmdkESX: variantStr = "ESX"; break; case MediumVariant_Standard: variantStr = "default"; break; } const char *variantTypeStr = "dynamic"; if (variant & MediumVariant_Fixed) variantTypeStr = "fixed"; else if (variant & MediumVariant_Diff) variantTypeStr = "differencing"; RTPrintf("Format variant: %s %s\n", variantTypeStr, variantStr); } LONG64 logicalSize; pMedium->COMGETTER(LogicalSize)(&logicalSize); RTPrintf("Capacity: %lld MBytes\n", logicalSize >> 20); if (fOptLong) { LONG64 actualSize; pMedium->COMGETTER(Size)(&actualSize); RTPrintf("Size on disk: %lld MBytes\n", actualSize >> 20); } if (fOptLong) { com::SafeArray names; com::SafeArray values; pMedium->GetProperties(Bstr().raw(), ComSafeArrayAsOutParam(names), ComSafeArrayAsOutParam(values)); size_t cNames = names.size(); size_t cValues = values.size(); bool fFirst = true; for (size_t i = 0; i < cNames; i++) { Bstr value; if (i < cValues) value = values[i]; RTPrintf("%s%ls=%ls\n", fFirst ? "Property: " : " ", names[i], value.raw()); } } if (fOptLong) { bool fFirst = true; com::SafeArray machineIds; pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(machineIds)); for (size_t i = 0; i < machineIds.size(); i++) { ComPtr machine; CHECK_ERROR(pVirtualBox, FindMachine(machineIds[i], machine.asOutParam())); if (machine) { Bstr name; machine->COMGETTER(Name)(name.asOutParam()); machine->COMGETTER(Id)(uuid.asOutParam()); RTPrintf("%s%ls (UUID: %ls)", fFirst ? "In use by VMs: " : " ", name.raw(), machineIds[i]); fFirst = false; com::SafeArray snapshotIds; pMedium->GetSnapshotIds(machineIds[i], ComSafeArrayAsOutParam(snapshotIds)); for (size_t j = 0; j < snapshotIds.size(); j++) { ComPtr snapshot; machine->FindSnapshot(snapshotIds[j], snapshot.asOutParam()); if (snapshot) { Bstr snapshotName; snapshot->COMGETTER(Name)(snapshotName.asOutParam()); RTPrintf(" [%ls (UUID: %ls)]", snapshotName.raw(), snapshotIds[j]); } } RTPrintf("\n"); } } } if (fOptLong) { com::SafeIfaceArray children; pMedium->COMGETTER(Children)(ComSafeArrayAsOutParam(children)); bool fFirst = true; for (size_t i = 0; i < children.size(); i++) { ComPtr pChild(children[i]); if (pChild) { Bstr childUUID; pChild->COMGETTER(Id)(childUUID.asOutParam()); RTPrintf("%s%ls\n", fFirst ? "Child UUIDs: " : " ", childUUID.raw()); fFirst = false; } } } } while (0); return rc; } static const RTGETOPTDEF g_aShowHardDiskInfoOptions[] = { { "--dummy", 256, RTGETOPT_REQ_NOTHING }, // placeholder for C++ }; int handleShowHardDiskInfo(HandlerArg *a) { HRESULT rc; const char *FilenameOrUuid = NULL; int c; RTGETOPTUNION ValueUnion; RTGETOPTSTATE GetState; // start at 0 because main() has hacked both the argc and argv given to us RTGetOptInit(&GetState, a->argc, a->argv, g_aShowHardDiskInfoOptions, RT_ELEMENTS(g_aShowHardDiskInfoOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS); while ((c = RTGetOpt(&GetState, &ValueUnion))) { switch (c) { case VINF_GETOPT_NOT_OPTION: if (!FilenameOrUuid) FilenameOrUuid = ValueUnion.psz; else return errorSyntax(USAGE_SHOWHDINFO, "Invalid parameter '%s'", ValueUnion.psz); break; default: if (c > 0) { if (RT_C_IS_PRINT(c)) return errorSyntax(USAGE_SHOWHDINFO, "Invalid option -%c", c); else return errorSyntax(USAGE_SHOWHDINFO, "Invalid option case %i", c); } else if (c == VERR_GETOPT_UNKNOWN_OPTION) return errorSyntax(USAGE_SHOWHDINFO, "unknown option: %s\n", ValueUnion.psz); else if (ValueUnion.pDef) return errorSyntax(USAGE_SHOWHDINFO, "%s: %Rrs", ValueUnion.pDef->pszLong, c); else return errorSyntax(USAGE_SHOWHDINFO, "error: %Rrs", c); } } /* check for required options */ if (!FilenameOrUuid) return errorSyntax(USAGE_SHOWHDINFO, "Disk name or UUID required"); ComPtr hardDisk; rc = openMedium(a, FilenameOrUuid, DeviceType_HardDisk, AccessMode_ReadOnly, hardDisk, false /* fForceNewUuidOnOpen */, false /* fSilent */); if (FAILED(rc)) return 1; Utf8Str strParentUUID("base"); ComPtr parent; hardDisk->COMGETTER(Parent)(parent.asOutParam()); if (!parent.isNull()) { Bstr bstrParentUUID; parent->COMGETTER(Id)(bstrParentUUID.asOutParam()); strParentUUID = bstrParentUUID; } rc = showMediumInfo(a->virtualBox, hardDisk, strParentUUID.c_str(), true); return SUCCEEDED(rc) ? 0 : 1; } static const RTGETOPTDEF g_aCloseMediumOptions[] = { { "disk", 'd', RTGETOPT_REQ_NOTHING }, { "dvd", 'D', RTGETOPT_REQ_NOTHING }, { "floppy", 'f', RTGETOPT_REQ_NOTHING }, { "--delete", 'r', RTGETOPT_REQ_NOTHING }, }; int handleCloseMedium(HandlerArg *a) { HRESULT rc = S_OK; enum { CMD_NONE, CMD_DISK, CMD_DVD, CMD_FLOPPY } cmd = CMD_NONE; const char *FilenameOrUuid = NULL; bool fDelete = false; int c; RTGETOPTUNION ValueUnion; RTGETOPTSTATE GetState; // start at 0 because main() has hacked both the argc and argv given to us RTGetOptInit(&GetState, a->argc, a->argv, g_aCloseMediumOptions, RT_ELEMENTS(g_aCloseMediumOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS); while ((c = RTGetOpt(&GetState, &ValueUnion))) { switch (c) { case 'd': // disk if (cmd != CMD_NONE) return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz); cmd = CMD_DISK; break; case 'D': // DVD if (cmd != CMD_NONE) return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz); cmd = CMD_DVD; break; case 'f': // floppy if (cmd != CMD_NONE) return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz); cmd = CMD_FLOPPY; break; case 'r': // --delete fDelete = true; break; case VINF_GETOPT_NOT_OPTION: if (!FilenameOrUuid) FilenameOrUuid = ValueUnion.psz; else return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid parameter '%s'", ValueUnion.psz); break; default: if (c > 0) { if (RT_C_IS_PRINT(c)) return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option -%c", c); else return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option case %i", c); } else if (c == VERR_GETOPT_UNKNOWN_OPTION) return errorSyntax(USAGE_CLOSEMEDIUM, "unknown option: %s\n", ValueUnion.psz); else if (ValueUnion.pDef) return errorSyntax(USAGE_CLOSEMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c); else return errorSyntax(USAGE_CLOSEMEDIUM, "error: %Rrs", c); } } /* check for required options */ if (cmd == CMD_NONE) return errorSyntax(USAGE_CLOSEMEDIUM, "Command variant disk/dvd/floppy required"); if (!FilenameOrUuid) return errorSyntax(USAGE_CLOSEMEDIUM, "Disk name or UUID required"); ComPtr medium; if (cmd == CMD_DISK) rc = openMedium(a, FilenameOrUuid, DeviceType_HardDisk, AccessMode_ReadWrite, medium, false /* fForceNewUuidOnOpen */, false /* fSilent */); else if (cmd == CMD_DVD) rc = openMedium(a, FilenameOrUuid, DeviceType_DVD, AccessMode_ReadOnly, medium, false /* fForceNewUuidOnOpen */, false /* fSilent */); else if (cmd == CMD_FLOPPY) rc = openMedium(a, FilenameOrUuid, DeviceType_Floppy, AccessMode_ReadWrite, medium, false /* fForceNewUuidOnOpen */, false /* fSilent */); if (SUCCEEDED(rc) && medium) { if (fDelete) { ComPtr progress; CHECK_ERROR(medium, DeleteStorage(progress.asOutParam())); if (SUCCEEDED(rc)) { rc = showProgress(progress); CHECK_PROGRESS_ERROR(progress, ("Failed to delete medium")); } else RTMsgError("Failed to delete medium. Error code %Rrc", rc); } CHECK_ERROR(medium, Close()); } return SUCCEEDED(rc) ? 0 : 1; } int handleMediumProperty(HandlerArg *a) { HRESULT rc = S_OK; const char *pszAction = NULL; const char *pszFilenameOrUuid = NULL; const char *pszProperty = NULL; ComPtr pMedium; if (a->argc == 0) return errorSyntax(USAGE_HDPROPERTY, "Missing action"); pszAction = a->argv[0]; if ( RTStrICmp(pszAction, "set") && RTStrICmp(pszAction, "get") && RTStrICmp(pszAction, "delete")) return errorSyntax(USAGE_HDPROPERTY, "Invalid action given: %s", pszAction); if ( ( !RTStrICmp(pszAction, "set") && a->argc != 4) || ( RTStrICmp(pszAction, "set") && a->argc != 3)) return errorSyntax(USAGE_HDPROPERTY, "Invalid number of arguments given for action: %s", pszAction); pszFilenameOrUuid = a->argv[1]; pszProperty = a->argv[2]; rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk, AccessMode_ReadWrite, pMedium, false /* fForceNewUuidOnOpen */, false /* fSilent */); if (SUCCEEDED(rc) && !pMedium.isNull()) { if (!RTStrICmp(pszAction, "set")) { const char *pszValue = a->argv[3]; CHECK_ERROR(pMedium, SetProperty(Bstr(pszProperty).raw(), Bstr(pszValue).raw())); } else if (!RTStrICmp(pszAction, "get")) { Bstr strVal; CHECK_ERROR(pMedium, GetProperty(Bstr(pszProperty).raw(), strVal.asOutParam())); if (SUCCEEDED(rc)) RTPrintf("%s=%ls\n", pszProperty, strVal.raw()); } else if (!RTStrICmp(pszAction, "delete")) { /** @todo */ } } return SUCCEEDED(rc) ? 0 : 1; } #endif /* !VBOX_ONLY_DOCS */