diff options
Diffstat (limited to 'src/VBox/Main/src-server/MediumIOImpl.cpp')
-rw-r--r-- | src/VBox/Main/src-server/MediumIOImpl.cpp | 905 |
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 */); +} + |