VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/MediumIOImpl.cpp@ 74897

Last change on this file since 74897 was 74825, checked in by vboxsync, 6 years ago

Main: bugref:9152 Implement the convertToStream API [doxygen build fix]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.3 KB
Line 
1/* $Id: MediumIOImpl.cpp 74825 2018-10-12 18:48:59Z vboxsync $ */
2/** @file
3 *
4 * VirtualBox COM class implementation: MediumIO
5 */
6
7/*
8 * Copyright (C) 2018 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20/*********************************************************************************************************************************
21* Header Files *
22*********************************************************************************************************************************/
23#include "MediumIOImpl.h"
24#include "MediumImpl.h"
25#include "MediumLock.h"
26#include "DataStreamImpl.h"
27#include "Global.h"
28#include "ProgressImpl.h"
29#include "VirtualBoxImpl.h"
30
31#include "AutoCaller.h"
32#include "Logging.h"
33#include "ThreadTask.h"
34
35#include <iprt/fsvfs.h>
36#include <iprt/dvm.h>
37#include <iprt/zero.h>
38#include <iprt/cpp/utils.h>
39
40
41/*********************************************************************************************************************************
42* Structures and Typedefs *
43*********************************************************************************************************************************/
44/**
45 * Private member data.
46 */
47struct MediumIO::Data
48{
49 Data(Medium * const a_pMedium, VirtualBox * const a_pVirtualBox, bool a_fWritable, uint32_t a_cbSector = 512)
50 : ptrMedium(a_pMedium)
51 , ptrVirtualBox(a_pVirtualBox)
52 , fWritable(a_fWritable)
53 , cbSector(a_cbSector)
54 , PasswordStore(false /*fKeyBufNonPageable*/)
55 , pHdd(NULL)
56 , hVfsFile(NIL_RTVFSFILE)
57 {
58 }
59
60 /** Reference to the medium we're accessing. */
61 ComPtr<Medium> ptrMedium;
62 /** Reference to the VirtualBox object the medium is part of. */
63 ComPtr<VirtualBox> ptrVirtualBox;
64 /** Set if writable, clear if readonly. */
65 bool fWritable;
66 /** The sector size. */
67 uint32_t cbSector;
68 /** Secret key store used to hold the passwords for encrypted medium. */
69 SecretKeyStore PasswordStore;
70 /** Crypto filter settings. */
71 MediumCryptoFilterSettings CryptoSettings;
72 /** Medium lock list. */
73 MediumLockList LockList;
74 /** The HDD instance. */
75 PVDISK pHdd;
76 /** VFS file for the HDD instance. */
77 RTVFSFILE hVfsFile;
78
79private:
80 Data() : PasswordStore(false) { }
81};
82
83
84/**
85 * MediumIO::StreamTask class for asynchronous convert to stream operation.
86 *
87 * @note Instances of this class must be created using new() because the
88 * task thread function will delete them when the task is complete.
89 *
90 * @note The constructor of this class adds a caller on the managed Medium
91 * object which is automatically released upon destruction.
92 */
93class MediumIO::StreamTask : public ThreadTask
94{
95public:
96 StreamTask(MediumIO *pMediumIO, DataStream *pDataStream, Progress *pProgress, const char *pszFormat,
97 MediumVariant_T fMediumVariant)
98 : ThreadTask("StreamTask"),
99 mVDOperationIfaces(NULL),
100 mMediumIO(pMediumIO),
101 mMediumCaller(pMediumIO->m->ptrMedium),
102 m_pDataStream(pDataStream),
103 m_fMediumVariant(fMediumVariant),
104 m_strFormat(pszFormat),
105 mProgress(pProgress),
106 mVirtualBoxCaller(NULL)
107 {
108 AssertReturnVoidStmt(pMediumIO, mRC = E_FAIL);
109 AssertReturnVoidStmt(pDataStream, mRC = E_FAIL);
110 mRC = mMediumCaller.rc();
111 if (FAILED(mRC))
112 return;
113
114 /* Get strong VirtualBox reference, see below. */
115 VirtualBox *pVirtualBox = pMediumIO->m->ptrVirtualBox;
116 mVirtualBox = pVirtualBox;
117 mVirtualBoxCaller.attach(pVirtualBox);
118 mRC = mVirtualBoxCaller.rc();
119 if (FAILED(mRC))
120 return;
121
122 /* Set up a per-operation progress interface, can be used freely (for
123 * binary operations you can use it either on the source or target). */
124 if (mProgress)
125 {
126 mVDIfProgress.pfnProgress = pProgress->i_vdProgressCallback;
127 int vrc = VDInterfaceAdd(&mVDIfProgress.Core,
128 "Medium::Task::vdInterfaceProgress",
129 VDINTERFACETYPE_PROGRESS,
130 mProgress,
131 sizeof(mVDIfProgress),
132 &mVDOperationIfaces);
133 AssertRC(vrc);
134 if (RT_FAILURE(vrc))
135 mRC = E_FAIL;
136 }
137 }
138
139 // Make all destructors virtual. Just in case.
140 virtual ~StreamTask()
141 {
142 /* send the notification of completion.*/
143 if ( isAsync()
144 && !mProgress.isNull())
145 mProgress->i_notifyComplete(mRC);
146 }
147
148 HRESULT rc() const { return mRC; }
149 bool isOk() const { return SUCCEEDED(rc()); }
150
151 const ComPtr<Progress>& GetProgressObject() const {return mProgress;}
152
153 /**
154 * Implementation code for the "create base" task.
155 * Used as function for execution from a standalone thread.
156 */
157 void handler()
158 {
159 LogFlowFuncEnter();
160 try
161 {
162 mRC = executeTask(); /* (destructor picks up mRC, see above) */
163 LogFlowFunc(("rc=%Rhrc\n", mRC));
164 }
165 catch (...)
166 {
167 LogRel(("Some exception in the function MediumIO::StreamTask:handler()\n"));
168 }
169
170 LogFlowFuncLeave();
171 }
172
173 PVDINTERFACE mVDOperationIfaces;
174
175 const ComObjPtr<MediumIO> mMediumIO;
176 AutoCaller mMediumCaller;
177
178protected:
179 HRESULT mRC;
180
181 DataStream *m_pDataStream;
182 MediumVariant_T m_fMediumVariant;
183 Utf8Str m_strFormat;
184
185private:
186 HRESULT executeTask();
187
188 const ComObjPtr<Progress> mProgress;
189
190 VDINTERFACEPROGRESS mVDIfProgress;
191
192 /* Must have a strong VirtualBox reference during a task otherwise the
193 * reference count might drop to 0 while a task is still running. This
194 * would result in weird behavior, including deadlocks due to uninit and
195 * locking order issues. The deadlock often is not detectable because the
196 * uninit uses event semaphores which sabotages deadlock detection. */
197 ComObjPtr<VirtualBox> mVirtualBox;
198 AutoCaller mVirtualBoxCaller;
199
200 static DECLCALLBACK(int) i_vdStreamOpen(void *pvUser, const char *pszLocation, uint32_t fOpen,
201 PFNVDCOMPLETED pfnCompleted, void **ppStorage);
202 static DECLCALLBACK(int) i_vdStreamClose(void *pvUser, void *pStorage);
203 static DECLCALLBACK(int) i_vdStreamDelete(void *pvUser, const char *pcszFilename);
204 static DECLCALLBACK(int) i_vdStreamMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove);
205 static DECLCALLBACK(int) i_vdStreamGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace);
206 static DECLCALLBACK(int) i_vdStreamGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime);
207 static DECLCALLBACK(int) i_vdStreamGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize);
208 static DECLCALLBACK(int) i_vdStreamSetSize(void *pvUser, void *pStorage, uint64_t cbSize);
209 static DECLCALLBACK(int) i_vdStreamRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuffer, size_t cbBuffer,
210 size_t *pcbRead);
211 static DECLCALLBACK(int) i_vdStreamWrite(void *pvUser, void *pStorage, uint64_t uOffset,
212 const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten);
213 static DECLCALLBACK(int) i_vdStreamFlush(void *pvUser, void *pStorage);
214};
215
216
217/**
218 * State of a streamed file.
219 */
220typedef struct STREAMFILE
221{
222 /** The data stream for this file state. */
223 DataStream *pDataStream;
224 /** The last seen offset used to stream zeroes for non consecutive writes. */
225 uint64_t uOffsetLast;
226 /** Set file size. */
227 uint64_t cbFile;
228} STREAMFILE;
229/** Pointer to the stream file state. */
230typedef STREAMFILE *PSTREAMFILE;
231
232
233
234DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamOpen(void *pvUser, const char *pszLocation, uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
235 void **ppStorage)
236{
237 RT_NOREF2(pvUser, pszLocation);
238
239 /* Validate input. */
240 AssertPtrReturn(ppStorage, VERR_INVALID_POINTER);
241 AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
242 AssertReturn((fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_WRITE, VERR_INVALID_PARAMETER);
243
244 int rc = VINF_SUCCESS;
245 PSTREAMFILE pStreamFile = (PSTREAMFILE)RTMemAllocZ(sizeof(*pStreamFile));
246 if (RT_LIKELY(pStreamFile))
247 {
248 pStreamFile->pDataStream = (DataStream *)pvUser;
249 pStreamFile->uOffsetLast = 0;
250 pStreamFile->cbFile = 0;
251 *ppStorage = pStreamFile;
252 }
253 else
254 rc = VERR_NO_MEMORY;
255
256 return rc;
257}
258
259DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamClose(void *pvUser, void *pStorage)
260{
261 RT_NOREF(pvUser);
262 PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
263 int rc = VINF_SUCCESS;
264
265 /* Fill up to the configured file size. */
266 if (pStreamFile->uOffsetLast < pStreamFile->cbFile)
267 {
268 do
269 {
270 size_t cbThisWrite = sizeof(g_abRTZero64K);
271 size_t cbWritten = 0;
272
273 if (pStreamFile->cbFile - pStreamFile->uOffsetLast < sizeof(g_abRTZero64K))
274 cbThisWrite = (size_t)(pStreamFile->cbFile - pStreamFile->uOffsetLast);
275
276 rc = pStreamFile->pDataStream->i_write(&g_abRTZero64K[0], cbThisWrite, &cbWritten);
277 if (RT_SUCCESS(rc))
278 pStreamFile->uOffsetLast += cbWritten;
279
280 } while ( RT_SUCCESS(rc)
281 && pStreamFile->uOffsetLast < pStreamFile->cbFile);
282 }
283
284 int rc2 = pStreamFile->pDataStream->i_close();
285 if (RT_SUCCESS(rc))
286 rc = rc2;
287
288 RTMemFree(pStreamFile);
289 return rc;
290}
291
292DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamDelete(void *pvUser, const char *pcszFilename)
293{
294 NOREF(pvUser);
295 NOREF(pcszFilename);
296 AssertFailedReturn(VERR_NOT_SUPPORTED);
297}
298
299DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
300{
301 NOREF(pvUser);
302 NOREF(pcszSrc);
303 NOREF(pcszDst);
304 NOREF(fMove);
305 AssertFailedReturn(VERR_NOT_SUPPORTED);
306}
307
308DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
309{
310 NOREF(pvUser);
311 NOREF(pcszFilename);
312 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
313 *pcbFreeSpace = INT64_MAX;
314 return VINF_SUCCESS;
315}
316
317DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
318{
319 NOREF(pvUser);
320 NOREF(pcszFilename);
321 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
322 AssertFailedReturn(VERR_NOT_SUPPORTED);
323}
324
325DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
326{
327 NOREF(pvUser);
328 PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
329 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
330
331 *pcbSize = pStreamFile->cbFile;
332 return VINF_SUCCESS;
333}
334
335DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
336{
337 RT_NOREF(pvUser);
338 PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
339
340 /* Reducing the size is not supported. */
341 int rc = VINF_SUCCESS;
342 if (pStreamFile->cbFile < cbSize)
343 pStreamFile->cbFile = cbSize;
344 else
345 rc = VERR_NOT_SUPPORTED;
346
347 return rc;
348}
349
350DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuffer, size_t cbBuffer,
351 size_t *pcbRead)
352{
353 NOREF(pvUser);
354 NOREF(pStorage);
355 NOREF(uOffset);
356 NOREF(cbBuffer);
357 NOREF(pcbRead);
358 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
359 AssertFailedReturn(VERR_NOT_SUPPORTED);
360}
361
362DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamWrite(void *pvUser, void *pStorage, uint64_t uOffset, const void *pvBuffer, size_t cbBuffer,
363 size_t *pcbWritten)
364{
365 RT_NOREF(pvUser);
366 PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
367 int rc = VINF_SUCCESS;
368
369 /* Fill up to the new offset if there is non consecutive access. */
370 if (pStreamFile->uOffsetLast < uOffset)
371 {
372 do
373 {
374 size_t cbThisWrite = sizeof(g_abRTZero64K);
375 size_t cbWritten = 0;
376
377 if (uOffset - pStreamFile->uOffsetLast < sizeof(g_abRTZero64K))
378 cbThisWrite = (size_t)(uOffset - pStreamFile->uOffsetLast);
379
380 rc = pStreamFile->pDataStream->i_write(&g_abRTZero64K[0], cbThisWrite, &cbWritten);
381 if (RT_SUCCESS(rc))
382 pStreamFile->uOffsetLast += cbWritten;
383
384 } while ( RT_SUCCESS(rc)
385 && pStreamFile->uOffsetLast < uOffset);
386 }
387
388 if (RT_SUCCESS(rc))
389 {
390 if (pcbWritten)
391 rc = pStreamFile->pDataStream->i_write(pvBuffer, cbBuffer, pcbWritten);
392 else
393 {
394 const uint8_t *pbBuf = (const uint8_t *)pvBuffer;
395 size_t cbLeft = cbBuffer;
396 size_t cbWritten = 0;
397 while ( cbLeft > 0
398 && RT_SUCCESS(rc))
399 {
400 rc = pStreamFile->pDataStream->i_write(pbBuf, cbLeft, &cbWritten);
401 if (RT_SUCCESS(rc))
402 {
403 pbBuf += cbWritten;
404 cbLeft -= cbWritten;
405 }
406 }
407 }
408
409 if (RT_SUCCESS(rc))
410 {
411 size_t cbWritten = pcbWritten ? *pcbWritten : cbBuffer;
412
413 /* Adjust file size. */
414 if (uOffset + cbWritten > pStreamFile->cbFile)
415 pStreamFile->cbFile = uOffset + cbWritten;
416
417 pStreamFile->uOffsetLast = uOffset + cbWritten;
418 }
419 }
420
421 return rc;
422}
423
424DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamFlush(void *pvUser, void *pStorage)
425{
426 NOREF(pvUser);
427 NOREF(pStorage);
428 return VINF_SUCCESS;
429}
430
431/**
432 * Implementation code for the "stream" task.
433 */
434HRESULT MediumIO::StreamTask::executeTask()
435{
436 HRESULT hrc = S_OK;
437 VDINTERFACEIO IfsOutputIO;
438 PVDINTERFACE pIfsOp = NULL;
439 PVDISK pDstDisk;
440
441 IfsOutputIO.pfnOpen = i_vdStreamOpen;
442 IfsOutputIO.pfnClose = i_vdStreamClose;
443 IfsOutputIO.pfnDelete = i_vdStreamDelete;
444 IfsOutputIO.pfnMove = i_vdStreamMove;
445 IfsOutputIO.pfnGetFreeSpace = i_vdStreamGetFreeSpace;
446 IfsOutputIO.pfnGetModificationTime = i_vdStreamGetModificationTime;
447 IfsOutputIO.pfnGetSize = i_vdStreamGetSize;
448 IfsOutputIO.pfnSetSize = i_vdStreamSetSize;
449 IfsOutputIO.pfnReadSync = i_vdStreamRead;
450 IfsOutputIO.pfnWriteSync = i_vdStreamWrite;
451 IfsOutputIO.pfnFlushSync = i_vdStreamFlush;
452 VDInterfaceAdd(&IfsOutputIO.Core, "stream", VDINTERFACETYPE_IO,
453 m_pDataStream, sizeof(VDINTERFACEIO), &pIfsOp);
454
455 int vrc = VDCreate(NULL, VDTYPE_HDD, &pDstDisk);
456 if (RT_SUCCESS(vrc))
457 {
458 /* Create the output image */
459 vrc = VDCopy(mMediumIO->m->pHdd, VD_LAST_IMAGE, pDstDisk, m_strFormat.c_str(),
460 "stream", false, 0, m_fMediumVariant, NULL,
461 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL, NULL,
462 pIfsOp, NULL);
463 if (RT_FAILURE(vrc))
464 hrc = mMediumIO->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
465 tr("Failed to convert and stream disk image"));
466
467 VDDestroy(pDstDisk);
468 }
469 else
470 hrc = mMediumIO->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
471 tr("Failed to create destination disk container"));
472
473 return hrc;
474}
475
476
477/*********************************************************************************************************************************
478* Boilerplate constructor & destructor *
479*********************************************************************************************************************************/
480
481DEFINE_EMPTY_CTOR_DTOR(MediumIO)
482
483HRESULT MediumIO::FinalConstruct()
484{
485 LogFlowThisFunc(("\n"));
486 return BaseFinalConstruct();
487}
488
489void MediumIO::FinalRelease()
490{
491 LogFlowThisFuncEnter();
492 uninit();
493 BaseFinalRelease();
494 LogFlowThisFuncLeave();
495}
496
497
498/*********************************************************************************************************************************
499* Initializer & uninitializer *
500*********************************************************************************************************************************/
501
502/**
503 * Initializes the medium I/O object.
504 *
505 * @param pMedium Pointer to the medium to access.
506 * @param pVirtualBox Pointer to the VirtualBox object the medium is part of.
507 * @param fWritable Read-write (true) or readonly (false) access.
508 * @param rStrKeyId The key ID for an encrypted medium. Empty if not
509 * encrypted.
510 * @param rStrPassword The password for an encrypted medium. Empty if not
511 * encrypted.
512 *
513 */
514HRESULT MediumIO::initForMedium(Medium *pMedium, VirtualBox *pVirtualBox, bool fWritable,
515 com::Utf8Str const &rStrKeyId, com::Utf8Str const &rStrPassword)
516{
517 LogFlowThisFunc(("pMedium=%p pVirtualBox=%p fWritable=%RTbool\n", pMedium, pVirtualBox, fWritable));
518 CheckComArgExpr(rStrPassword, rStrPassword.isEmpty() == rStrKeyId.isEmpty()); /* checked by caller */
519
520 /*
521 * Enclose the state transition NotReady->InInit->Ready
522 */
523 AutoInitSpan autoInitSpan(this);
524 AssertReturn(autoInitSpan.isOk(), E_FAIL);
525
526 /*
527 * Allocate data instance.
528 */
529 HRESULT hrc = S_OK;
530 m = new(std::nothrow) Data(pMedium, pVirtualBox, fWritable);
531 if (m)
532 {
533 /*
534 * Add the password to the keystore if specified.
535 */
536 if (rStrKeyId.isNotEmpty())
537 {
538 int vrc = m->PasswordStore.addSecretKey(rStrKeyId, (const uint8_t *)rStrPassword.c_str(),
539 rStrPassword.length() + 1 /*including the Schwarzenegger character*/);
540 if (vrc == VERR_NO_MEMORY)
541 hrc = setError(E_OUTOFMEMORY, tr("Failed to allocate enough secure memory for the key/password"));
542 else if (RT_FAILURE(vrc))
543 hrc = setErrorBoth(E_FAIL, vrc, tr("Unknown error happened while adding a password (%Rrc)"), vrc);
544 }
545
546 /*
547 * Try open the medium and then get a VFS file handle for it.
548 */
549 if (SUCCEEDED(hrc))
550 {
551 hrc = pMedium->i_openForIO(fWritable, &m->PasswordStore, &m->pHdd, &m->LockList, &m->CryptoSettings);
552 if (SUCCEEDED(hrc))
553 {
554 int vrc = VDCreateVfsFileFromDisk(m->pHdd, 0 /*fFlags*/, &m->hVfsFile);
555 if (RT_FAILURE(vrc))
556 {
557 hrc = setErrorBoth(E_FAIL, vrc, tr("VDCreateVfsFileFromDisk failed: %Rrc"), vrc);
558 m->hVfsFile = NIL_RTVFSFILE;
559 }
560 }
561 }
562 }
563 else
564 hrc = E_OUTOFMEMORY;
565
566 /*
567 * Done. Just update object readiness state.
568 */
569 if (SUCCEEDED(hrc))
570 autoInitSpan.setSucceeded();
571 else
572 {
573 if (m)
574 i_close(); /* Free password and whatever i_openHddForIO() may accidentally leave around on failure. */
575 autoInitSpan.setFailed(hrc);
576 }
577
578 LogFlowThisFunc(("returns %Rhrc\n", hrc));
579 return hrc;
580}
581
582/**
583 * Uninitializes the instance (called from FinalRelease()).
584 */
585void MediumIO::uninit()
586{
587 LogFlowThisFuncEnter();
588
589 /* Enclose the state transition Ready->InUninit->NotReady */
590 AutoUninitSpan autoUninitSpan(this);
591 if (!autoUninitSpan.uninitDone())
592 {
593 if (m)
594 {
595 i_close();
596
597 delete m;
598 m = NULL;
599 }
600 }
601
602 LogFlowThisFuncLeave();
603}
604
605
606/*********************************************************************************************************************************
607* IMediumIO attributes *
608*********************************************************************************************************************************/
609
610HRESULT MediumIO::getMedium(ComPtr<IMedium> &a_rPtrMedium)
611{
612 a_rPtrMedium = m->ptrMedium;
613 return S_OK;
614}
615
616HRESULT MediumIO::getWritable(BOOL *a_fWritable)
617{
618 *a_fWritable = m->fWritable;
619 return S_OK;
620}
621
622HRESULT MediumIO::getExplorer(ComPtr<IVFSExplorer> &a_rPtrExplorer)
623{
624 RT_NOREF_PV(a_rPtrExplorer);
625 return E_NOTIMPL;
626}
627
628
629/*********************************************************************************************************************************
630* IMediumIO methods *
631*********************************************************************************************************************************/
632
633HRESULT MediumIO::read(LONG64 a_off, ULONG a_cbRead, std::vector<BYTE> &a_rData)
634{
635 /*
636 * Validate input.
637 */
638 if (a_cbRead > _256K)
639 return setError(E_INVALIDARG, tr("Max read size is 256KB, given: %u"), a_cbRead);
640 if (a_cbRead == 0)
641 return setError(E_INVALIDARG, tr("Zero byte read is not supported."));
642
643 /*
644 * Allocate return buffer.
645 */
646 try
647 {
648 a_rData.resize(a_cbRead);
649 }
650 catch (std::bad_alloc &)
651 {
652 return E_OUTOFMEMORY;
653 }
654
655 /*
656 * Do the reading. To play safe we exclusivly lock the object while doing this.
657 */
658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
659 size_t cbActual = 0;
660 int vrc = RTVfsFileReadAt(m->hVfsFile, a_off, &a_rData.front(), a_cbRead, &cbActual);
661 alock.release();
662
663 /*
664 * Manage the result.
665 */
666 HRESULT hrc;
667 if (RT_SUCCESS(vrc))
668 {
669 if (cbActual != a_cbRead)
670 {
671 Assert(cbActual < a_cbRead);
672 a_rData.resize(cbActual);
673 }
674 hrc = S_OK;
675 }
676 else
677 {
678 a_rData.resize(0);
679 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error reading %u bytes at %RU64: %Rrc"), a_cbRead, a_off, vrc);
680 }
681
682 return hrc;
683}
684
685HRESULT MediumIO::write(LONG64 a_off, const std::vector<BYTE> &a_rData, ULONG *a_pcbWritten)
686{
687 /*
688 * Validate input.
689 */
690 size_t cbToWrite = a_rData.size();
691 if (cbToWrite == 0)
692 return setError(E_INVALIDARG, tr("Zero byte write is not supported."));
693 if (!m->fWritable)
694 return setError(E_ACCESSDENIED, tr("Medium not opened for writing."));
695 CheckComArgPointerValid(a_pcbWritten);
696 *a_pcbWritten = 0;
697
698 /*
699 * Do the writing. To play safe we exclusivly lock the object while doing this.
700 */
701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
702 size_t cbActual = 0;
703 int vrc = RTVfsFileWriteAt(m->hVfsFile, a_off, &a_rData.front(), cbToWrite, &cbActual);
704 alock.release();
705
706 /*
707 * Manage the result.
708 */
709 HRESULT hrc;
710 if (RT_SUCCESS(vrc))
711 {
712 *a_pcbWritten = (ULONG)cbActual;
713 hrc = S_OK;
714 }
715 else
716 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error writing %zu bytes at %RU64: %Rrc"), cbToWrite, a_off, vrc);
717
718 return hrc;
719}
720
721HRESULT MediumIO::formatFAT(BOOL a_fQuick)
722{
723 /*
724 * Validate input.
725 */
726 if (!m->fWritable)
727 return setError(E_ACCESSDENIED, tr("Medium not opened for writing."));
728
729 /*
730 * Format the medium as FAT and let the format API figure the parameters.
731 * We exclusivly lock the object while doing this as concurrent medium access makes no sense.
732 */
733 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
734 RTERRINFOSTATIC ErrInfo;
735 int vrc = RTFsFatVolFormat(m->hVfsFile, 0, 0, a_fQuick ? RTFSFATVOL_FMT_F_QUICK : RTFSFATVOL_FMT_F_FULL,
736 (uint16_t)m->cbSector, 0, RTFSFATTYPE_INVALID, 0, 0, 0, 0, 0, RTErrInfoInitStatic(&ErrInfo));
737 alock.release();
738
739 /*
740 * Manage the result.
741 */
742 HRESULT hrc;
743 if (RT_SUCCESS(vrc))
744 hrc = S_OK;
745 else if (RTErrInfoIsSet(&ErrInfo.Core))
746 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error formatting (%Rrc): %s"), vrc, ErrInfo.Core.pszMsg);
747 else
748 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error formatting: %Rrc"), vrc);
749
750 return hrc;
751}
752
753HRESULT MediumIO::initializePartitionTable(PartitionTableType_T a_enmFormat, BOOL a_fWholeDiskInOneEntry)
754{
755 /*
756 * Validate input.
757 */
758 const char *pszFormat;
759 if (a_enmFormat == PartitionTableType_MBR)
760 pszFormat = "MBR"; /* RTDVMFORMATTYPE_MBR */
761 else if (a_enmFormat == PartitionTableType_GPT)
762 pszFormat = "GPT"; /* RTDVMFORMATTYPE_GPT */
763 else
764 return setError(E_INVALIDARG, tr("Invalid partition format type: %d"), a_enmFormat);
765 if (!m->fWritable)
766 return setError(E_ACCESSDENIED, tr("Medium not opened for writing."));
767 if (a_fWholeDiskInOneEntry)
768 return setError(E_NOTIMPL, tr("whole-disk-in-one-entry is not implemented yet, sorry."));
769
770 /*
771 * Do the partitioning.
772 * We exclusivly lock the object while doing this as concurrent medium access makes little sense.
773 */
774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
775
776 RTDVM hVolMgr;
777 int vrc = RTDvmCreate(&hVolMgr, m->hVfsFile, m->cbSector, 0 /*fFlags*/);
778 HRESULT hrc;
779 if (RT_SUCCESS(vrc))
780 {
781 vrc = RTDvmMapInitialize(hVolMgr, pszFormat); /** @todo Why doesn't RTDvmMapInitialize take RTDVMFORMATTYPE? */
782 if (RT_SUCCESS(vrc))
783 {
784 /*
785 * Create a partition for the whole disk?
786 */
787 hrc = S_OK; /** @todo a_fWholeDiskInOneEntry requies RTDvm to get a function for creating partitions. */
788 }
789 else
790 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("RTDvmMapInitialize failed: %Rrc"), vrc);
791 RTDvmRelease(hVolMgr);
792 }
793 else
794 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("RTDvmCreate failed: %Rrc"), vrc);
795
796 return hrc;
797}
798
799HRESULT MediumIO::convertToStream(const com::Utf8Str &aFormat,
800 const std::vector<MediumVariant_T> &aVariant,
801 ULONG aBufferSize,
802 ComPtr<IDataStream> &aStream,
803 ComPtr<IProgress> &aProgress)
804{
805 HRESULT rc = S_OK;
806 ComObjPtr<Progress> pProgress;
807 ComObjPtr<DataStream> pDataStream;
808 MediumIO::StreamTask *pTask = NULL;
809
810 pProgress.createObject();
811 pDataStream.createObject();
812
813 try
814 {
815 rc = pDataStream->init(aBufferSize);
816 if (FAILED(rc))
817 throw rc;
818
819 ULONG mediumVariantFlags = 0;
820
821 if (aVariant.size())
822 {
823 for (size_t i = 0; i < aVariant.size(); i++)
824 mediumVariantFlags |= (ULONG)aVariant[i];
825 }
826
827 /* setup task object to carry out the operation asynchronously */
828 pTask = new MediumIO::StreamTask(this, pDataStream, pProgress,
829 aFormat.c_str(), (MediumVariant_T)mediumVariantFlags);
830 rc = pTask->rc();
831 AssertComRC(rc);
832 if (FAILED(rc))
833 throw rc;
834 }
835 catch (HRESULT aRC) { rc = aRC; }
836
837 if (SUCCEEDED(rc))
838 {
839 rc = pTask->createThread();
840
841 if (SUCCEEDED(rc))
842 {
843 pDataStream.queryInterfaceTo(aStream.asOutParam());
844 pProgress.queryInterfaceTo(aProgress.asOutParam());
845 }
846 }
847 else if (pTask != NULL)
848 delete pTask;
849
850 return rc;
851}
852
853HRESULT MediumIO::close()
854{
855 /*
856 * We need a write lock here to exclude all other access.
857 */
858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
859 i_close();
860 return S_OK;
861}
862
863
864
865/*********************************************************************************************************************************
866* IMediumIO internal methods *
867*********************************************************************************************************************************/
868
869/**
870 * This is used by both uninit and close().
871 *
872 * Expects exclusive access (write lock or autouninit) to the object.
873 */
874void MediumIO::i_close()
875{
876 if (m->hVfsFile != NIL_RTVFSFILE)
877 {
878 uint32_t cRefs = RTVfsFileRelease(m->hVfsFile);
879 Assert(cRefs == 0);
880 NOREF(cRefs);
881
882 m->hVfsFile = NIL_RTVFSFILE;
883 }
884
885 if (m->pHdd)
886 {
887 VDDestroy(m->pHdd);
888 m->pHdd = NULL;
889 }
890
891 m->LockList.Clear();
892 m->ptrMedium.setNull();
893 m->PasswordStore.deleteAllSecretKeys(false /* fSuspend */, true /* fForce */);
894}
895
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette