summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-server/MediumIOImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-server/MediumIOImpl.cpp')
-rw-r--r--src/VBox/Main/src-server/MediumIOImpl.cpp905
1 files changed, 905 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/MediumIOImpl.cpp b/src/VBox/Main/src-server/MediumIOImpl.cpp
new file mode 100644
index 00000000..57382b2e
--- /dev/null
+++ b/src/VBox/Main/src-server/MediumIOImpl.cpp
@@ -0,0 +1,905 @@
+/* $Id: MediumIOImpl.cpp $ */
+/** @file
+ * VirtualBox COM class implementation: MediumIO
+ */
+
+/*
+ * Copyright (C) 2018-2022 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* 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 <iprt/fsvfs.h>
+#include <iprt/dvm.h>
+#include <iprt/zero.h>
+#include <iprt/cpp/utils.h>
+
+
+/*********************************************************************************************************************************
+* 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<Medium> ptrMedium;
+ /** Reference to the VirtualBox object the medium is part of. */
+ ComPtr<VirtualBox> 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, mRC = E_FAIL);
+ AssertReturnVoidStmt(pDataStream, mRC = E_FAIL);
+ mRC = mMediumCaller.rc();
+ if (FAILED(mRC))
+ return;
+
+ /* Get strong VirtualBox reference, see below. */
+ VirtualBox *pVirtualBox = pMediumIO->m->ptrVirtualBox;
+ mVirtualBox = pVirtualBox;
+ mVirtualBoxCaller.attach(pVirtualBox);
+ mRC = mVirtualBoxCaller.rc();
+ if (FAILED(mRC))
+ return;
+ }
+
+ // Make all destructors virtual. Just in case.
+ virtual ~StreamTask()
+ {
+ /* send the notification of completion.*/
+ if ( isAsync()
+ && !mProgress.isNull())
+ mProgress->i_notifyComplete(mRC);
+ }
+
+ HRESULT rc() const { return mRC; }
+ bool isOk() const { return SUCCEEDED(rc()); }
+
+ const ComPtr<Progress>& GetProgressObject() const {return mProgress;}
+
+ /**
+ * Implementation code for the "create base" task.
+ * Used as function for execution from a standalone thread.
+ */
+ void handler()
+ {
+ LogFlowFuncEnter();
+ try
+ {
+ mRC = executeTask(); /* (destructor picks up mRC, see above) */
+ LogFlowFunc(("rc=%Rhrc\n", mRC));
+ }
+ catch (...)
+ {
+ LogRel(("Some exception in the function MediumIO::StreamTask:handler()\n"));
+ }
+
+ LogFlowFuncLeave();
+ }
+
+ const ComObjPtr<MediumIO> mMediumIO;
+ AutoCaller mMediumCaller;
+
+protected:
+ HRESULT mRC;
+
+ ComObjPtr<DataStream> m_pDataStream;
+ MediumVariant_T m_fMediumVariant;
+ Utf8Str m_strFormat;
+
+private:
+ HRESULT executeTask();
+
+ const ComObjPtr<Progress> 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<VirtualBox> 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 rc = 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
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamClose(void *pvUser, void *pStorage)
+{
+ RT_NOREF(pvUser);
+ PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
+ int rc = 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);
+
+ rc = pStreamFile->pDataStream->i_write(&g_abRTZero64K[0], cbThisWrite, &cbWritten);
+ if (RT_SUCCESS(rc))
+ pStreamFile->uOffsetLast += cbWritten;
+
+ } while ( RT_SUCCESS(rc)
+ && pStreamFile->uOffsetLast < pStreamFile->cbFile);
+ }
+
+ int rc2 = pStreamFile->pDataStream->i_close();
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+
+ RTMemFree(pStreamFile);
+ return rc;
+}
+
+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 rc = VINF_SUCCESS;
+ if (pStreamFile->cbFile < cbSize)
+ pStreamFile->cbFile = cbSize;
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ return rc;
+}
+
+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 rc = 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);
+
+ rc = pStreamFile->pDataStream->i_write(&g_abRTZero64K[0], cbThisWrite, &cbWritten);
+ if (RT_SUCCESS(rc))
+ pStreamFile->uOffsetLast += cbWritten;
+
+ } while ( RT_SUCCESS(rc)
+ && pStreamFile->uOffsetLast < uOffset);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (pcbWritten)
+ rc = 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(rc))
+ {
+ rc = pStreamFile->pDataStream->i_write(pbBuf, cbLeft, &cbWritten);
+ if (RT_SUCCESS(rc))
+ {
+ pbBuf += cbWritten;
+ cbLeft -= cbWritten;
+ }
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbWritten = pcbWritten ? *pcbWritten : cbBuffer;
+
+ /* Adjust file size. */
+ if (uOffset + cbWritten > pStreamFile->cbFile)
+ pStreamFile->cbFile = uOffset + cbWritten;
+
+ pStreamFile->uOffsetLast = uOffset + cbWritten;
+ }
+ }
+
+ return rc;
+}
+
+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<IMedium> &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<IVFSExplorer> &a_rPtrExplorer)
+{
+ RT_NOREF_PV(a_rPtrExplorer);
+ return E_NOTIMPL;
+}
+
+
+/*********************************************************************************************************************************
+* IMediumIO methods *
+*********************************************************************************************************************************/
+
+HRESULT MediumIO::read(LONG64 a_off, ULONG a_cbRead, std::vector<BYTE> &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<BYTE> &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<MediumVariant_T> &aVariant,
+ ULONG aBufferSize,
+ ComPtr<IDataStream> &aStream,
+ ComPtr<IProgress> &aProgress)
+{
+ HRESULT rc = S_OK;
+ ComObjPtr<Progress> pProgress;
+ ComObjPtr<DataStream> pDataStream;
+ MediumIO::StreamTask *pTask = NULL;
+
+ try
+ {
+ pDataStream.createObject();
+ rc = pDataStream->init(aBufferSize);
+ if (FAILED(rc))
+ throw rc;
+
+ pProgress.createObject();
+ rc = pProgress->init(m->ptrVirtualBox,
+ static_cast<IMediumIO*>(this),
+ BstrFmt(tr("Converting medium '%s' to data stream"), m->ptrMedium->i_getLocationFull().c_str()),
+ TRUE /* aCancelable */);
+ if (FAILED(rc))
+ throw rc;
+
+ 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);
+ rc = pTask->rc();
+ AssertComRC(rc);
+ if (FAILED(rc))
+ throw rc;
+ }
+ catch (HRESULT aRC) { rc = aRC; }
+
+ if (SUCCEEDED(rc))
+ {
+ rc = pTask->createThread();
+ pTask = NULL;
+ if (SUCCEEDED(rc))
+ {
+ pDataStream.queryInterfaceTo(aStream.asOutParam());
+ pProgress.queryInterfaceTo(aProgress.asOutParam());
+ }
+ }
+ else if (pTask != NULL)
+ delete pTask;
+
+ return rc;
+}
+
+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 */);
+}
+