/* $Id: vboximgMedia.cpp $ */ /** @file * vboximgMedia.cpp - Disk Image Flattening FUSE Program. */ /* * Copyright (C) 2009-2023 Oracle and/or its affiliates. * * This file is part of VirtualBox base platform packages, as * available from https://www.virtualbox.org. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, in version 3 of the * License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * SPDX-License-Identifier: GPL-3.0-only */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vboximgOpts.h" using namespace com; extern VBOXIMGOPTS g_vboximgOpts; #define SAFENULL(strPtr) (strPtr ? strPtr : "") /** Makes null harmless to print */ #define CSTR(arg) Utf8Str(arg).c_str() /** Converts XPCOM string type to C string type */ #define MAX_UUID_LEN 256 /** Max length of a UUID */ #define VM_MAX_NAME 32 /** Max length of VM name we handle */ typedef struct MEDIUMINFO { char *pszName; char *pszUuid; char *pszBaseUuid; char *pszPath; char *pszDescription; char *pszState; char *pszType; char *pszFormat; bool fSnapshot; PRInt64 cbSize; MediumType_T type; MediumState_T state; ~MEDIUMINFO() { RTMemFree(pszName); RTMemFree(pszUuid); RTMemFree(pszPath); RTMemFree(pszDescription); RTMemFree(pszFormat); } } MEDIUMINFO; char *vboximgScaledSize(size_t size) { uint64_t exp = 0; if (size > 0) exp = log2((double)size); char scaledMagnitude = ((char []){ ' ', 'K', 'M', 'G', 'T', 'P' })[exp / 10]; /* This workaround is because IPRT RT*Printf* funcs don't handle floating point format specifiers */ double cbScaled = (double)size / pow(2, (double)(((uint64_t)(exp / 10)) * 10)); uint64_t intPart = cbScaled; uint64_t fracPart = (cbScaled - (double)intPart) * 10; char tmp[256]; RTStrPrintf(tmp, sizeof (tmp), "%d.%d%c", intPart, fracPart, scaledMagnitude); return RTStrDup(tmp); } static int getMediumInfo(IMachine *pMachine, IMedium *pMedium, MEDIUMINFO **ppMediumInfo) { RT_NOREF(pMachine); MEDIUMINFO *info = new MEDIUMINFO(); *ppMediumInfo = info; Bstr name; Bstr uuid; Bstr baseUuid; Bstr path; Bstr description; Bstr format; PRInt64 *pSize = &info->cbSize; ComPtr pBase; MediumType_T *pType = &info->type; MediumState_T *pState = &info->state; *pState = MediumState_NotCreated; HRESULT hrc; CHECK_ERROR(pMedium, RefreshState(pState)); CHECK_ERROR(pMedium, COMGETTER(Id)(uuid.asOutParam())); CHECK_ERROR(pMedium, COMGETTER(Base)(pBase.asOutParam())); CHECK_ERROR(pBase, COMGETTER(Id)(baseUuid.asOutParam())); CHECK_ERROR(pMedium, COMGETTER(State)(pState)); CHECK_ERROR(pMedium, COMGETTER(Location)(path.asOutParam())); CHECK_ERROR(pMedium, COMGETTER(Format)(format.asOutParam())); CHECK_ERROR(pMedium, COMGETTER(Type)(pType)); CHECK_ERROR(pMedium, COMGETTER(Size)(pSize)); info->pszUuid = RTStrDup((char *)CSTR(uuid)); info->pszBaseUuid = RTStrDup((char *)CSTR(baseUuid)); info->pszPath = RTStrDup((char *)CSTR(path)); info->pszFormat = RTStrDup((char *)CSTR(format)); info->fSnapshot = RTStrCmp(CSTR(uuid), CSTR(baseUuid)) != 0; if (info->fSnapshot) { /** @todo Determine the VM snapshot this and set name and description * to the snapshot name/description */ CHECK_ERROR(pMedium, COMGETTER(Name)(name.asOutParam())); CHECK_ERROR(pMedium, COMGETTER(Description)(description.asOutParam())); } else { CHECK_ERROR(pMedium, COMGETTER(Name)(name.asOutParam())); CHECK_ERROR(pMedium, COMGETTER(Description)(description.asOutParam())); } info->pszName = RTStrDup((char *)CSTR(name)); info->pszDescription = RTStrDup((char *)CSTR(description)); switch(*pType) { case MediumType_Normal: info->pszType = (char *)"normal"; break; case MediumType_Immutable: info->pszType = (char *)"immutable"; break; case MediumType_Writethrough: info->pszType = (char *)"writethrough"; break; case MediumType_Shareable: info->pszType = (char *)"shareable"; break; case MediumType_Readonly: info->pszType = (char *)"readonly"; break; case MediumType_MultiAttach: info->pszType = (char *)"multiattach"; break; default: info->pszType = (char *)"?"; } switch(*pState) { case MediumState_NotCreated: info->pszState = (char *)"uncreated"; break; case MediumState_Created: info->pszState = (char *)"created"; break; case MediumState_LockedRead: info->pszState = (char *)"rlock"; break; case MediumState_LockedWrite: info->pszState = (char *)"wlock"; break; case MediumState_Inaccessible: info->pszState = (char *)"no access"; break; case MediumState_Creating: info->pszState = (char *)"creating"; break; case MediumState_Deleting: info->pszState = (char *)"deleting"; break; default: info->pszState = (char *)"?"; } return VINF_SUCCESS; } static void displayMediumInfo(MEDIUMINFO *pInfo, int nestLevel, bool fLast) { char *pszSzScaled = vboximgScaledSize(pInfo->cbSize); int cPad = nestLevel * 2; if (g_vboximgOpts.fWide && !g_vboximgOpts.fVerbose) { RTPrintf("%3s %-*s %7s %-9s %9s %-*s %s\n", !fLast ? (pInfo->fSnapshot ? " | " : " +-") : (pInfo->fSnapshot ? " " : " +-"), VM_MAX_NAME, pInfo->fSnapshot ? "+- " : pInfo->pszName, pszSzScaled, pInfo->pszFormat, pInfo->pszState, cPad, "", pInfo->pszUuid); } else { if (!pInfo->fSnapshot) { RTPrintf(" Image: %s\n", pInfo->pszName); if (pInfo->pszDescription && RTStrNLen(pInfo->pszDescription, 256) > 0) RTPrintf("Desc: %s\n", pInfo->pszDescription); RTPrintf(" UUID: %s\n", pInfo->pszUuid); if (g_vboximgOpts.fVerbose) { RTPrintf(" Path: %s\n", pInfo->pszPath); RTPrintf(" Format: %s\n", pInfo->pszFormat); RTPrintf(" Size: %s\n", pszSzScaled); RTPrintf(" State: %s\n", pInfo->pszState); RTPrintf(" Type: %s\n", pInfo->pszType); } RTPrintf("\n"); } else { RTPrintf(" Snapshot: %s\n", pInfo->pszUuid); if (g_vboximgOpts.fVerbose) { RTPrintf(" Name: %s\n", pInfo->pszName); RTPrintf(" Desc: %s\n", pInfo->pszDescription); } RTPrintf(" Size: %s\n", pszSzScaled); if (g_vboximgOpts.fVerbose) RTPrintf(" Path: %s\n", pInfo->pszPath); RTPrintf("\n"); } } RTMemFree(pszSzScaled); } static int vboximgListBranch(IMachine *pMachine, IMedium *pMedium, uint8_t nestLevel, bool fLast) { MEDIUMINFO *pMediumInfo; int vrc = getMediumInfo(pMachine, pMedium, &pMediumInfo); if (RT_FAILURE(vrc)) return vrc; displayMediumInfo(pMediumInfo, nestLevel, fLast); HRESULT hrc; com::SafeIfaceArray pChildren; CHECK_ERROR_RET(pMedium, COMGETTER(Children)(ComSafeArrayAsOutParam(pChildren)), VERR_NOT_FOUND); /** @todo r=andy Find a better rc. */ for (size_t i = 0; i < pChildren.size(); i++) vboximgListBranch(pMachine, pChildren[i], nestLevel + 1, fLast); delete pMediumInfo; return VINF_SUCCESS; } static int listMedia(IVirtualBox *pVirtualBox, IMachine *pMachine, char *vmName, char *vmUuid) { RT_NOREF(pVirtualBox); RT_NOREF(vmName); RT_NOREF(vmUuid); int vrc = VINF_SUCCESS; com::SafeIfaceArray pMediumAttachments; HRESULT hrc; CHECK_ERROR(pMachine, COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(pMediumAttachments))); for (size_t i = 0; i < pMediumAttachments.size(); i++) { bool fLast = (i == pMediumAttachments.size() - 1); DeviceType_T deviceType; CHECK_ERROR(pMediumAttachments[i], COMGETTER(Type)(&deviceType)); if (deviceType != DeviceType_HardDisk) continue; ComPtr pMedium; CHECK_ERROR(pMediumAttachments[i], COMGETTER(Medium)(pMedium.asOutParam())); ComPtr pBase; CHECK_ERROR(pMedium, COMGETTER(Base)(pBase.asOutParam())); if (g_vboximgOpts.fWide && !g_vboximgOpts.fVerbose) RTPrintf(" |\n"); else RTPrintf("\n"); vrc = vboximgListBranch(pMachine, pBase, 0, fLast); if (RT_FAILURE(vrc)) { RTPrintf("vboximgListBranch failed with %Rrc\n", vrc); break; } } return vrc; } /** * Display all registered VMs on the screen with some information about each * * @param virtualBox VirtualBox instance object. */ int vboximgListVMs(IVirtualBox *pVirtualBox) { HRESULT hrc; com::SafeIfaceArray pMachines; CHECK_ERROR(pVirtualBox, COMGETTER(Machines)(ComSafeArrayAsOutParam(pMachines))); if (g_vboximgOpts.fWide) { RTPrintf("\n"); RTPrintf("VM Image Size Type State UUID (hierarchy)\n"); } int vrc = VINF_SUCCESS; for (size_t i = 0; i < pMachines.size(); ++i) { ComPtr pMachine = pMachines[i]; if (pMachine) { BOOL fAccessible; CHECK_ERROR(pMachines[i], COMGETTER(Accessible)(&fAccessible)); if (fAccessible) { Bstr machineName; Bstr machineUuid; Bstr description; Bstr machineLocation; CHECK_ERROR(pMachine, COMGETTER(Name)(machineName.asOutParam())); CHECK_ERROR(pMachine, COMGETTER(Id)(machineUuid.asOutParam())); CHECK_ERROR(pMachine, COMGETTER(Description)(description.asOutParam())); CHECK_ERROR(pMachine, COMGETTER(SettingsFilePath)(machineLocation.asOutParam())); if ( g_vboximgOpts.pszVm == NULL || RTStrNCmp(CSTR(machineUuid), g_vboximgOpts.pszVm, MAX_UUID_LEN) == 0 || RTStrNCmp((const char *)machineName.raw(), g_vboximgOpts.pszVm, MAX_UUID_LEN) == 0) { if (g_vboximgOpts.fVerbose) { RTPrintf("-----------------------------------------------------------------\n"); RTPrintf("VM Name: \"%s\"\n", CSTR(machineName)); RTPrintf("UUID: %s\n", CSTR(machineUuid)); if (*description.raw() != '\0') RTPrintf("Desc: %s\n", CSTR(description)); RTPrintf("Path: %s\n", CSTR(machineLocation)); } else { if (g_vboximgOpts.fWide & !g_vboximgOpts.fVerbose) { RTPrintf("----------------------------------------------------------------- " "------------------------------------\n"); RTPrintf("%-*s %*s %s\n", VM_MAX_NAME, CSTR(machineName), 33, "", CSTR(machineUuid)); } else { RTPrintf("-----------------------------------------------------------------\n"); RTPrintf("VM: %s\n", CSTR(machineName)); RTPrintf("UUID: %s\n", CSTR(machineUuid)); } } int vrc2 = listMedia(pVirtualBox, pMachine, RTStrDup(CSTR(machineName)), RTStrDup(CSTR(machineUuid))); if (RT_SUCCESS(vrc)) vrc = vrc2; RTPrintf("\n"); } } } } return vrc; }