/* $Id: MediumIOImpl.cpp 98292 2023-01-25 01:14:53Z vboxsync $ */ /** @file * VirtualBox COM class implementation: MediumIO */ /* * Copyright (C) 2018-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 */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #define LOG_GROUP LOG_GROUP_MAIN_MEDIUMIO #include "MediumIOImpl.h" #include "MediumImpl.h" #include "MediumLock.h" #include "DataStreamImpl.h" #include "Global.h" #include "ProgressImpl.h" #include "VirtualBoxImpl.h" #include "AutoCaller.h" #include "LoggingNew.h" #include "ThreadTask.h" #include #include #include #include /********************************************************************************************************************************* * Structures and Typedefs * *********************************************************************************************************************************/ /** * Private member data. */ struct MediumIO::Data { Data(Medium * const a_pMedium, VirtualBox * const a_pVirtualBox, bool a_fWritable, uint32_t a_cbSector = 512) : ptrMedium(a_pMedium) , ptrVirtualBox(a_pVirtualBox) , fWritable(a_fWritable) , cbSector(a_cbSector) , PasswordStore(false /*fKeyBufNonPageable*/) , pHdd(NULL) , hVfsFile(NIL_RTVFSFILE) { } /** Reference to the medium we're accessing. */ ComPtr ptrMedium; /** Reference to the VirtualBox object the medium is part of. */ ComPtr ptrVirtualBox; /** Set if writable, clear if readonly. */ bool fWritable; /** The sector size. */ uint32_t cbSector; /** Secret key store used to hold the passwords for encrypted medium. */ SecretKeyStore PasswordStore; /** Crypto filter settings. */ MediumCryptoFilterSettings CryptoSettings; /** Medium lock list. */ MediumLockList LockList; /** The HDD instance. */ PVDISK pHdd; /** VFS file for the HDD instance. */ RTVFSFILE hVfsFile; private: Data() : PasswordStore(false) { } }; /** * MediumIO::StreamTask class for asynchronous convert to stream operation. * * @note Instances of this class must be created using new() because the * task thread function will delete them when the task is complete. * * @note The constructor of this class adds a caller on the managed Medium * object which is automatically released upon destruction. */ class MediumIO::StreamTask : public ThreadTask { public: StreamTask(MediumIO *pMediumIO, DataStream *pDataStream, Progress *pProgress, const char *pszFormat, MediumVariant_T fMediumVariant) : ThreadTask("StreamTask"), mMediumIO(pMediumIO), mMediumCaller(pMediumIO->m->ptrMedium), m_pDataStream(pDataStream), m_fMediumVariant(fMediumVariant), m_strFormat(pszFormat), mProgress(pProgress), mVirtualBoxCaller(NULL) { AssertReturnVoidStmt(pMediumIO, mHrc = E_FAIL); AssertReturnVoidStmt(pDataStream, mHrc = E_FAIL); mHrc = mMediumCaller.hrc(); if (FAILED(mHrc)) return; /* Get strong VirtualBox reference, see below. */ VirtualBox *pVirtualBox = pMediumIO->m->ptrVirtualBox; mVirtualBox = pVirtualBox; mVirtualBoxCaller.attach(pVirtualBox); mHrc = mVirtualBoxCaller.hrc(); if (FAILED(mHrc)) return; } // Make all destructors virtual. Just in case. virtual ~StreamTask() { /* send the notification of completion.*/ if ( isAsync() && !mProgress.isNull()) mProgress->i_notifyComplete(mHrc); } HRESULT hrc() const { return mHrc; } bool isOk() const { return SUCCEEDED(hrc()); } const ComPtr& GetProgressObject() const {return mProgress;} /** * Implementation code for the "create base" task. * Used as function for execution from a standalone thread. */ void handler() { LogFlowFuncEnter(); try { mHrc = executeTask(); /* (destructor picks up mHrc, see above) */ LogFlowFunc(("hrc=%Rhrc\n", mHrc)); } catch (...) { LogRel(("Some exception in the function MediumIO::StreamTask:handler()\n")); } LogFlowFuncLeave(); } const ComObjPtr mMediumIO; AutoCaller mMediumCaller; protected: HRESULT mHrc; ComObjPtr m_pDataStream; MediumVariant_T m_fMediumVariant; Utf8Str m_strFormat; private: HRESULT executeTask(); const ComObjPtr mProgress; /* Must have a strong VirtualBox reference during a task otherwise the * reference count might drop to 0 while a task is still running. This * would result in weird behavior, including deadlocks due to uninit and * locking order issues. The deadlock often is not detectable because the * uninit uses event semaphores which sabotages deadlock detection. */ ComObjPtr mVirtualBox; AutoCaller mVirtualBoxCaller; static DECLCALLBACK(int) i_vdStreamOpen(void *pvUser, const char *pszLocation, uint32_t fOpen, PFNVDCOMPLETED pfnCompleted, void **ppStorage); static DECLCALLBACK(int) i_vdStreamClose(void *pvUser, void *pStorage); static DECLCALLBACK(int) i_vdStreamDelete(void *pvUser, const char *pcszFilename); static DECLCALLBACK(int) i_vdStreamMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove); static DECLCALLBACK(int) i_vdStreamGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace); static DECLCALLBACK(int) i_vdStreamGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime); static DECLCALLBACK(int) i_vdStreamGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize); static DECLCALLBACK(int) i_vdStreamSetSize(void *pvUser, void *pStorage, uint64_t cbSize); static DECLCALLBACK(int) i_vdStreamRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuffer, size_t cbBuffer, size_t *pcbRead); static DECLCALLBACK(int) i_vdStreamWrite(void *pvUser, void *pStorage, uint64_t uOffset, const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten); static DECLCALLBACK(int) i_vdStreamFlush(void *pvUser, void *pStorage); }; /** * State of a streamed file. */ typedef struct STREAMFILE { /** The data stream for this file state. */ DataStream *pDataStream; /** The last seen offset used to stream zeroes for non consecutive writes. */ uint64_t uOffsetLast; /** Set file size. */ uint64_t cbFile; } STREAMFILE; /** Pointer to the stream file state. */ typedef STREAMFILE *PSTREAMFILE; DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamOpen(void *pvUser, const char *pszLocation, uint32_t fOpen, PFNVDCOMPLETED pfnCompleted, void **ppStorage) { RT_NOREF2(pvUser, pszLocation); /* Validate input. */ AssertPtrReturn(ppStorage, VERR_INVALID_POINTER); AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER); AssertReturn((fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_WRITE, VERR_INVALID_PARAMETER); int vrc = VINF_SUCCESS; PSTREAMFILE pStreamFile = (PSTREAMFILE)RTMemAllocZ(sizeof(*pStreamFile)); if (RT_LIKELY(pStreamFile)) { pStreamFile->pDataStream = (DataStream *)pvUser; pStreamFile->uOffsetLast = 0; pStreamFile->cbFile = 0; *ppStorage = pStreamFile; } else vrc = VERR_NO_MEMORY; return vrc; } DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamClose(void *pvUser, void *pStorage) { RT_NOREF(pvUser); PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage; int vrc = VINF_SUCCESS; /* Fill up to the configured file size. */ if (pStreamFile->uOffsetLast < pStreamFile->cbFile) { do { size_t cbThisWrite = sizeof(g_abRTZero64K); size_t cbWritten = 0; if (pStreamFile->cbFile - pStreamFile->uOffsetLast < sizeof(g_abRTZero64K)) cbThisWrite = (size_t)(pStreamFile->cbFile - pStreamFile->uOffsetLast); vrc = pStreamFile->pDataStream->i_write(&g_abRTZero64K[0], cbThisWrite, &cbWritten); if (RT_SUCCESS(vrc)) pStreamFile->uOffsetLast += cbWritten; } while ( RT_SUCCESS(vrc) && pStreamFile->uOffsetLast < pStreamFile->cbFile); } int vrc2 = pStreamFile->pDataStream->i_close(); if (RT_SUCCESS(vrc)) vrc = vrc2; RTMemFree(pStreamFile); return vrc; } DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamDelete(void *pvUser, const char *pcszFilename) { NOREF(pvUser); NOREF(pcszFilename); AssertFailedReturn(VERR_NOT_SUPPORTED); } DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove) { NOREF(pvUser); NOREF(pcszSrc); NOREF(pcszDst); NOREF(fMove); AssertFailedReturn(VERR_NOT_SUPPORTED); } DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace) { NOREF(pvUser); NOREF(pcszFilename); AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER); *pcbFreeSpace = INT64_MAX; return VINF_SUCCESS; } DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime) { NOREF(pvUser); NOREF(pcszFilename); AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER); AssertFailedReturn(VERR_NOT_SUPPORTED); } DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize) { NOREF(pvUser); PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage; AssertPtrReturn(pcbSize, VERR_INVALID_POINTER); *pcbSize = pStreamFile->cbFile; return VINF_SUCCESS; } DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamSetSize(void *pvUser, void *pStorage, uint64_t cbSize) { RT_NOREF(pvUser); PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage; /* Reducing the size is not supported. */ int vrc = VINF_SUCCESS; if (pStreamFile->cbFile < cbSize) pStreamFile->cbFile = cbSize; else vrc = VERR_NOT_SUPPORTED; return vrc; } DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuffer, size_t cbBuffer, size_t *pcbRead) { NOREF(pvUser); NOREF(pStorage); NOREF(uOffset); NOREF(cbBuffer); NOREF(pcbRead); AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER); AssertFailedReturn(VERR_NOT_SUPPORTED); } DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamWrite(void *pvUser, void *pStorage, uint64_t uOffset, const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten) { RT_NOREF(pvUser); PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage; int vrc = VINF_SUCCESS; /* Fill up to the new offset if there is non consecutive access. */ if (pStreamFile->uOffsetLast < uOffset) { do { size_t cbThisWrite = sizeof(g_abRTZero64K); size_t cbWritten = 0; if (uOffset - pStreamFile->uOffsetLast < sizeof(g_abRTZero64K)) cbThisWrite = (size_t)(uOffset - pStreamFile->uOffsetLast); vrc = pStreamFile->pDataStream->i_write(&g_abRTZero64K[0], cbThisWrite, &cbWritten); if (RT_SUCCESS(vrc)) pStreamFile->uOffsetLast += cbWritten; } while ( RT_SUCCESS(vrc) && pStreamFile->uOffsetLast < uOffset); } if (RT_SUCCESS(vrc)) { if (pcbWritten) vrc = pStreamFile->pDataStream->i_write(pvBuffer, cbBuffer, pcbWritten); else { const uint8_t *pbBuf = (const uint8_t *)pvBuffer; size_t cbLeft = cbBuffer; size_t cbWritten = 0; while ( cbLeft > 0 && RT_SUCCESS(vrc)) { vrc = pStreamFile->pDataStream->i_write(pbBuf, cbLeft, &cbWritten); if (RT_SUCCESS(vrc)) { pbBuf += cbWritten; cbLeft -= cbWritten; } } } if (RT_SUCCESS(vrc)) { size_t cbWritten = pcbWritten ? *pcbWritten : cbBuffer; /* Adjust file size. */ if (uOffset + cbWritten > pStreamFile->cbFile) pStreamFile->cbFile = uOffset + cbWritten; pStreamFile->uOffsetLast = uOffset + cbWritten; } } return vrc; } DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamFlush(void *pvUser, void *pStorage) { NOREF(pvUser); NOREF(pStorage); return VINF_SUCCESS; } /** * Implementation code for the "stream" task. */ HRESULT MediumIO::StreamTask::executeTask() { HRESULT hrc = S_OK; VDINTERFACEIO IfsOutputIO; VDINTERFACEPROGRESS IfsProgress; PVDINTERFACE pIfsOp = NULL; PVDINTERFACE pIfsImg = NULL; PVDISK pDstDisk; if (mProgress) { IfsProgress.pfnProgress = mProgress->i_vdProgressCallback; VDInterfaceAdd(&IfsProgress.Core, "Medium::StreamTask::vdInterfaceProgress", VDINTERFACETYPE_PROGRESS, mProgress, sizeof(IfsProgress), &pIfsOp); } IfsOutputIO.pfnOpen = i_vdStreamOpen; IfsOutputIO.pfnClose = i_vdStreamClose; IfsOutputIO.pfnDelete = i_vdStreamDelete; IfsOutputIO.pfnMove = i_vdStreamMove; IfsOutputIO.pfnGetFreeSpace = i_vdStreamGetFreeSpace; IfsOutputIO.pfnGetModificationTime = i_vdStreamGetModificationTime; IfsOutputIO.pfnGetSize = i_vdStreamGetSize; IfsOutputIO.pfnSetSize = i_vdStreamSetSize; IfsOutputIO.pfnReadSync = i_vdStreamRead; IfsOutputIO.pfnWriteSync = i_vdStreamWrite; IfsOutputIO.pfnFlushSync = i_vdStreamFlush; VDInterfaceAdd(&IfsOutputIO.Core, "stream", VDINTERFACETYPE_IO, m_pDataStream, sizeof(VDINTERFACEIO), &pIfsImg); int vrc = VDCreate(NULL, VDTYPE_HDD, &pDstDisk); if (RT_SUCCESS(vrc)) { /* Create the output image */ vrc = VDCopy(mMediumIO->m->pHdd, VD_LAST_IMAGE, pDstDisk, m_strFormat.c_str(), "stream", false, 0, m_fMediumVariant, NULL, VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL, pIfsOp, pIfsImg, NULL); if (RT_FAILURE(vrc)) hrc = mMediumIO->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to convert and stream disk image")); VDDestroy(pDstDisk); } else hrc = mMediumIO->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to create destination disk container")); return hrc; } /********************************************************************************************************************************* * Boilerplate constructor & destructor * *********************************************************************************************************************************/ DEFINE_EMPTY_CTOR_DTOR(MediumIO) HRESULT MediumIO::FinalConstruct() { LogFlowThisFunc(("\n")); return BaseFinalConstruct(); } void MediumIO::FinalRelease() { LogFlowThisFuncEnter(); uninit(); BaseFinalRelease(); LogFlowThisFuncLeave(); } /********************************************************************************************************************************* * Initializer & uninitializer * *********************************************************************************************************************************/ /** * Initializes the medium I/O object. * * @param pMedium Pointer to the medium to access. * @param pVirtualBox Pointer to the VirtualBox object the medium is part of. * @param fWritable Read-write (true) or readonly (false) access. * @param rStrKeyId The key ID for an encrypted medium. Empty if not * encrypted. * @param rStrPassword The password for an encrypted medium. Empty if not * encrypted. * */ HRESULT MediumIO::initForMedium(Medium *pMedium, VirtualBox *pVirtualBox, bool fWritable, com::Utf8Str const &rStrKeyId, com::Utf8Str const &rStrPassword) { LogFlowThisFunc(("pMedium=%p pVirtualBox=%p fWritable=%RTbool\n", pMedium, pVirtualBox, fWritable)); CheckComArgExpr(rStrPassword, rStrPassword.isEmpty() == rStrKeyId.isEmpty()); /* checked by caller */ /* * Enclose the state transition NotReady->InInit->Ready */ AutoInitSpan autoInitSpan(this); AssertReturn(autoInitSpan.isOk(), E_FAIL); /* * Allocate data instance. */ HRESULT hrc = S_OK; m = new(std::nothrow) Data(pMedium, pVirtualBox, fWritable); if (m) { /* * Add the password to the keystore if specified. */ if (rStrKeyId.isNotEmpty()) { int vrc = m->PasswordStore.addSecretKey(rStrKeyId, (const uint8_t *)rStrPassword.c_str(), rStrPassword.length() + 1 /*including the Schwarzenegger character*/); if (vrc == VERR_NO_MEMORY) hrc = setError(E_OUTOFMEMORY, tr("Failed to allocate enough secure memory for the key/password")); else if (RT_FAILURE(vrc)) hrc = setErrorBoth(E_FAIL, vrc, tr("Unknown error happened while adding a password (%Rrc)"), vrc); } /* * Try open the medium and then get a VFS file handle for it. */ if (SUCCEEDED(hrc)) { hrc = pMedium->i_openForIO(fWritable, &m->PasswordStore, &m->pHdd, &m->LockList, &m->CryptoSettings); if (SUCCEEDED(hrc)) { int vrc = VDCreateVfsFileFromDisk(m->pHdd, 0 /*fFlags*/, &m->hVfsFile); if (RT_FAILURE(vrc)) { hrc = setErrorBoth(E_FAIL, vrc, tr("VDCreateVfsFileFromDisk failed: %Rrc"), vrc); m->hVfsFile = NIL_RTVFSFILE; } } } } else hrc = E_OUTOFMEMORY; /* * Done. Just update object readiness state. */ if (SUCCEEDED(hrc)) autoInitSpan.setSucceeded(); else { if (m) i_close(); /* Free password and whatever i_openHddForIO() may accidentally leave around on failure. */ autoInitSpan.setFailed(hrc); } LogFlowThisFunc(("returns %Rhrc\n", hrc)); return hrc; } /** * Uninitializes the instance (called from FinalRelease()). */ void MediumIO::uninit() { LogFlowThisFuncEnter(); /* Enclose the state transition Ready->InUninit->NotReady */ AutoUninitSpan autoUninitSpan(this); if (!autoUninitSpan.uninitDone()) { if (m) { i_close(); delete m; m = NULL; } } LogFlowThisFuncLeave(); } /********************************************************************************************************************************* * IMediumIO attributes * *********************************************************************************************************************************/ HRESULT MediumIO::getMedium(ComPtr &a_rPtrMedium) { a_rPtrMedium = m->ptrMedium; return S_OK; } HRESULT MediumIO::getWritable(BOOL *a_fWritable) { *a_fWritable = m->fWritable; return S_OK; } HRESULT MediumIO::getExplorer(ComPtr &a_rPtrExplorer) { RT_NOREF_PV(a_rPtrExplorer); return E_NOTIMPL; } /********************************************************************************************************************************* * IMediumIO methods * *********************************************************************************************************************************/ HRESULT MediumIO::read(LONG64 a_off, ULONG a_cbRead, std::vector &a_rData) { /* * Validate input. */ if (a_cbRead > _256K) return setError(E_INVALIDARG, tr("Max read size is 256KB, given: %u"), a_cbRead); if (a_cbRead == 0) return setError(E_INVALIDARG, tr("Zero byte read is not supported.")); /* * Allocate return buffer. */ try { a_rData.resize(a_cbRead); } catch (std::bad_alloc &) { return E_OUTOFMEMORY; } /* * Do the reading. To play safe we exclusivly lock the object while doing this. */ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); size_t cbActual = 0; int vrc = RTVfsFileReadAt(m->hVfsFile, a_off, &a_rData.front(), a_cbRead, &cbActual); alock.release(); /* * Manage the result. */ HRESULT hrc; if (RT_SUCCESS(vrc)) { if (cbActual != a_cbRead) { Assert(cbActual < a_cbRead); a_rData.resize(cbActual); } hrc = S_OK; } else { a_rData.resize(0); hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error reading %u bytes at %RU64: %Rrc", "", a_cbRead), a_cbRead, a_off, vrc); } return hrc; } HRESULT MediumIO::write(LONG64 a_off, const std::vector &a_rData, ULONG *a_pcbWritten) { /* * Validate input. */ size_t cbToWrite = a_rData.size(); if (cbToWrite == 0) return setError(E_INVALIDARG, tr("Zero byte write is not supported.")); if (!m->fWritable) return setError(E_ACCESSDENIED, tr("Medium not opened for writing.")); CheckComArgPointerValid(a_pcbWritten); *a_pcbWritten = 0; /* * Do the writing. To play safe we exclusivly lock the object while doing this. */ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); size_t cbActual = 0; int vrc = RTVfsFileWriteAt(m->hVfsFile, a_off, &a_rData.front(), cbToWrite, &cbActual); alock.release(); /* * Manage the result. */ HRESULT hrc; if (RT_SUCCESS(vrc)) { *a_pcbWritten = (ULONG)cbActual; hrc = S_OK; } else hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error writing %zu bytes at %RU64: %Rrc", "", cbToWrite), cbToWrite, a_off, vrc); return hrc; } HRESULT MediumIO::formatFAT(BOOL a_fQuick) { /* * Validate input. */ if (!m->fWritable) return setError(E_ACCESSDENIED, tr("Medium not opened for writing.")); /* * Format the medium as FAT and let the format API figure the parameters. * We exclusivly lock the object while doing this as concurrent medium access makes no sense. */ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); RTERRINFOSTATIC ErrInfo; int vrc = RTFsFatVolFormat(m->hVfsFile, 0, 0, a_fQuick ? RTFSFATVOL_FMT_F_QUICK : RTFSFATVOL_FMT_F_FULL, (uint16_t)m->cbSector, 0, RTFSFATTYPE_INVALID, 0, 0, 0, 0, 0, RTErrInfoInitStatic(&ErrInfo)); alock.release(); /* * Manage the result. */ HRESULT hrc; if (RT_SUCCESS(vrc)) hrc = S_OK; else if (RTErrInfoIsSet(&ErrInfo.Core)) hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error formatting (%Rrc): %s"), vrc, ErrInfo.Core.pszMsg); else hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error formatting: %Rrc"), vrc); return hrc; } HRESULT MediumIO::initializePartitionTable(PartitionTableType_T a_enmFormat, BOOL a_fWholeDiskInOneEntry) { /* * Validate input. */ const char *pszFormat; if (a_enmFormat == PartitionTableType_MBR) pszFormat = "MBR"; /* RTDVMFORMATTYPE_MBR */ else if (a_enmFormat == PartitionTableType_GPT) pszFormat = "GPT"; /* RTDVMFORMATTYPE_GPT */ else return setError(E_INVALIDARG, tr("Invalid partition format type: %d"), a_enmFormat); if (!m->fWritable) return setError(E_ACCESSDENIED, tr("Medium not opened for writing.")); if (a_fWholeDiskInOneEntry) return setError(E_NOTIMPL, tr("whole-disk-in-one-entry is not implemented yet, sorry.")); /* * Do the partitioning. * We exclusivly lock the object while doing this as concurrent medium access makes little sense. */ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); RTDVM hVolMgr; int vrc = RTDvmCreate(&hVolMgr, m->hVfsFile, m->cbSector, 0 /*fFlags*/); HRESULT hrc; if (RT_SUCCESS(vrc)) { vrc = RTDvmMapInitialize(hVolMgr, pszFormat); /** @todo Why doesn't RTDvmMapInitialize take RTDVMFORMATTYPE? */ if (RT_SUCCESS(vrc)) { /* * Create a partition for the whole disk? */ hrc = S_OK; /** @todo a_fWholeDiskInOneEntry requies RTDvm to get a function for creating partitions. */ } else hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("RTDvmMapInitialize failed: %Rrc"), vrc); RTDvmRelease(hVolMgr); } else hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("RTDvmCreate failed: %Rrc"), vrc); return hrc; } HRESULT MediumIO::convertToStream(const com::Utf8Str &aFormat, const std::vector &aVariant, ULONG aBufferSize, ComPtr &aStream, ComPtr &aProgress) { HRESULT hrc = S_OK; ComObjPtr pProgress; ComObjPtr pDataStream; MediumIO::StreamTask *pTask = NULL; try { pDataStream.createObject(); hrc = pDataStream->init(aBufferSize); if (FAILED(hrc)) throw hrc; pProgress.createObject(); hrc = pProgress->init(m->ptrVirtualBox, static_cast(this), BstrFmt(tr("Converting medium '%s' to data stream"), m->ptrMedium->i_getLocationFull().c_str()), TRUE /* aCancelable */); if (FAILED(hrc)) throw hrc; ULONG mediumVariantFlags = 0; if (aVariant.size()) { for (size_t i = 0; i < aVariant.size(); i++) mediumVariantFlags |= (ULONG)aVariant[i]; } /* setup task object to carry out the operation asynchronously */ pTask = new MediumIO::StreamTask(this, pDataStream, pProgress, aFormat.c_str(), (MediumVariant_T)mediumVariantFlags); hrc = pTask->hrc(); AssertComRC(hrc); if (FAILED(hrc)) throw hrc; } catch (HRESULT hrcXcpt) { hrc = hrcXcpt; } if (SUCCEEDED(hrc)) { hrc = pTask->createThread(); pTask = NULL; if (SUCCEEDED(hrc)) { pDataStream.queryInterfaceTo(aStream.asOutParam()); pProgress.queryInterfaceTo(aProgress.asOutParam()); } } else if (pTask != NULL) delete pTask; return hrc; } HRESULT MediumIO::close() { /* * We need a write lock here to exclude all other access. */ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); i_close(); return S_OK; } /********************************************************************************************************************************* * IMediumIO internal methods * *********************************************************************************************************************************/ /** * This is used by both uninit and close(). * * Expects exclusive access (write lock or autouninit) to the object. */ void MediumIO::i_close() { if (m->hVfsFile != NIL_RTVFSFILE) { uint32_t cRefs = RTVfsFileRelease(m->hVfsFile); Assert(cRefs == 0); NOREF(cRefs); m->hVfsFile = NIL_RTVFSFILE; } if (m->pHdd) { VDDestroy(m->pHdd); m->pHdd = NULL; } m->LockList.Clear(); m->ptrMedium.setNull(); m->PasswordStore.deleteAllSecretKeys(false /* fSuspend */, true /* fForce */); }