VirtualBox

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

Last change on this file since 74822 was 74822, checked in by vboxsync, 7 years ago

Main: bugref:9152 Implement the convertToStream API

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.1 KB
Line 
1/* $Id: MediumIOImpl.cpp 74822 2018-10-12 18:40:09Z 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.215389.xyz. 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 = RT_MIN(pStreamFile->cbFile - pStreamFile->uOffsetLast, sizeof(g_abRTZero64K));
271 size_t cbWritten = 0;
272
273 rc = pStreamFile->pDataStream->i_write(&g_abRTZero64K[0], cbThisWrite, &cbWritten);
274 if (RT_SUCCESS(rc))
275 pStreamFile->uOffsetLast += cbWritten;
276
277 } while ( RT_SUCCESS(rc)
278 && pStreamFile->uOffsetLast < pStreamFile->cbFile);
279 }
280
281 int rc2 = pStreamFile->pDataStream->i_close();
282 if (RT_SUCCESS(rc))
283 rc = rc2;
284
285 RTMemFree(pStreamFile);
286 return rc;
287}
288
289DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamDelete(void *pvUser, const char *pcszFilename)
290{
291 NOREF(pvUser);
292 NOREF(pcszFilename);
293 AssertFailedReturn(VERR_NOT_SUPPORTED);
294}
295
296DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
297{
298 NOREF(pvUser);
299 NOREF(pcszSrc);
300 NOREF(pcszDst);
301 NOREF(fMove);
302 AssertFailedReturn(VERR_NOT_SUPPORTED);
303}
304
305DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
306{
307 NOREF(pvUser);
308 NOREF(pcszFilename);
309 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
310 *pcbFreeSpace = INT64_MAX;
311 return VINF_SUCCESS;
312}
313
314DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
315{
316 NOREF(pvUser);
317 NOREF(pcszFilename);
318 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
319 AssertFailedReturn(VERR_NOT_SUPPORTED);
320}
321
322DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
323{
324 NOREF(pvUser);
325 PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
326 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
327
328 *pcbSize = pStreamFile->cbFile;
329 return VINF_SUCCESS;
330}
331
332DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
333{
334 RT_NOREF(pvUser);
335 PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
336
337 /* Reducing the size is not supported. */
338 int rc = VINF_SUCCESS;
339 if (pStreamFile->cbFile < cbSize)
340 pStreamFile->cbFile = cbSize;
341 else
342 rc = VERR_NOT_SUPPORTED;
343
344 return rc;
345}
346
347DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuffer, size_t cbBuffer,
348 size_t *pcbRead)
349{
350 NOREF(pvUser);
351 NOREF(pStorage);
352 NOREF(uOffset);
353 NOREF(cbBuffer);
354 NOREF(pcbRead);
355 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
356 AssertFailedReturn(VERR_NOT_SUPPORTED);
357}
358
359DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamWrite(void *pvUser, void *pStorage, uint64_t uOffset, const void *pvBuffer, size_t cbBuffer,
360 size_t *pcbWritten)
361{
362 RT_NOREF(pvUser);
363 PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
364 int rc = VINF_SUCCESS;
365
366 /* Fill up to the new offset if there is non consecutive access. */
367 if (pStreamFile->uOffsetLast < uOffset)
368 {
369 do
370 {
371 size_t cbThisWrite = RT_MIN(uOffset - pStreamFile->uOffsetLast, sizeof(g_abRTZero64K));
372 size_t cbWritten = 0;
373
374 rc = pStreamFile->pDataStream->i_write(&g_abRTZero64K[0], cbThisWrite, &cbWritten);
375 if (RT_SUCCESS(rc))
376 pStreamFile->uOffsetLast += cbWritten;
377
378 } while ( RT_SUCCESS(rc)
379 && pStreamFile->uOffsetLast < uOffset);
380 }
381
382 if (RT_SUCCESS(rc))
383 {
384 if (pcbWritten)
385 rc = pStreamFile->pDataStream->i_write(pvBuffer, cbBuffer, pcbWritten);
386 else
387 {
388 const uint8_t *pbBuf = (const uint8_t *)pvBuffer;
389 size_t cbLeft = cbBuffer;
390 size_t cbWritten = 0;
391 while ( cbLeft > 0
392 && RT_SUCCESS(rc))
393 {
394 rc = pStreamFile->pDataStream->i_write(pbBuf, cbLeft, &cbWritten);
395 if (RT_SUCCESS(rc))
396 {
397 pbBuf += cbWritten;
398 cbLeft -= cbWritten;
399 }
400 }
401 }
402
403 if (RT_SUCCESS(rc))
404 {
405 size_t cbWritten = pcbWritten ? *pcbWritten : cbBuffer;
406
407 /* Adjust file size. */
408 if (uOffset + cbWritten > pStreamFile->cbFile)
409 pStreamFile->cbFile = uOffset + cbWritten;
410
411 pStreamFile->uOffsetLast = uOffset + cbWritten;
412 }
413 }
414
415 return rc;
416}
417
418DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamFlush(void *pvUser, void *pStorage)
419{
420 NOREF(pvUser);
421 NOREF(pStorage);
422 return VINF_SUCCESS;
423}
424
425/**
426 * Implementation code for the "stream" task.
427 */
428HRESULT MediumIO::StreamTask::executeTask()
429{
430 HRESULT hrc = S_OK;
431 VDINTERFACEIO IfsOutputIO;
432 PVDINTERFACE pIfsOp = NULL;
433 PVDISK pDstDisk;
434
435 IfsOutputIO.pfnOpen = i_vdStreamOpen;
436 IfsOutputIO.pfnClose = i_vdStreamClose;
437 IfsOutputIO.pfnDelete = i_vdStreamDelete;
438 IfsOutputIO.pfnMove = i_vdStreamMove;
439 IfsOutputIO.pfnGetFreeSpace = i_vdStreamGetFreeSpace;
440 IfsOutputIO.pfnGetModificationTime = i_vdStreamGetModificationTime;
441 IfsOutputIO.pfnGetSize = i_vdStreamGetSize;
442 IfsOutputIO.pfnSetSize = i_vdStreamSetSize;
443 IfsOutputIO.pfnReadSync = i_vdStreamRead;
444 IfsOutputIO.pfnWriteSync = i_vdStreamWrite;
445 IfsOutputIO.pfnFlushSync = i_vdStreamFlush;
446 VDInterfaceAdd(&IfsOutputIO.Core, "stream", VDINTERFACETYPE_IO,
447 m_pDataStream, sizeof(VDINTERFACEIO), &pIfsOp);
448
449 int vrc = VDCreate(NULL, VDTYPE_HDD, &pDstDisk);
450 if (RT_SUCCESS(vrc))
451 {
452 /* Create the output image */
453 vrc = VDCopy(mMediumIO->m->pHdd, VD_LAST_IMAGE, pDstDisk, m_strFormat.c_str(),
454 "stream", false, 0, m_fMediumVariant, NULL,
455 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL, NULL,
456 pIfsOp, NULL);
457 if (RT_FAILURE(vrc))
458 hrc = mMediumIO->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
459 tr("Failed to convert and stream disk image"));
460
461 VDDestroy(pDstDisk);
462 }
463 else
464 hrc = mMediumIO->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
465 tr("Failed to create destination disk container"));
466
467 return hrc;
468}
469
470
471/*********************************************************************************************************************************
472* Boilerplate constructor & destructor *
473*********************************************************************************************************************************/
474
475DEFINE_EMPTY_CTOR_DTOR(MediumIO)
476
477HRESULT MediumIO::FinalConstruct()
478{
479 LogFlowThisFunc(("\n"));
480 return BaseFinalConstruct();
481}
482
483void MediumIO::FinalRelease()
484{
485 LogFlowThisFuncEnter();
486 uninit();
487 BaseFinalRelease();
488 LogFlowThisFuncLeave();
489}
490
491
492/*********************************************************************************************************************************
493* Initializer & uninitializer *
494*********************************************************************************************************************************/
495
496/**
497 * Initializes the medium I/O object.
498 *
499 * @param pMedium Pointer to the medium to access.
500 * @param pMedium Pointer to the VirtualBox object the medium is part of.
501 * @param fWritable Read-write (true) or readonly (false) access.
502 * @param rStrKeyId The key ID for an encrypted medium. Empty if not
503 * encrypted.
504 * @param rStrPassword The password for an encrypted medium. Empty if not
505 * encrypted.
506 *
507 */
508HRESULT MediumIO::initForMedium(Medium *pMedium, VirtualBox *pVirtualBox, bool fWritable,
509 com::Utf8Str const &rStrKeyId, com::Utf8Str const &rStrPassword)
510{
511 LogFlowThisFunc(("pMedium=%p pVirtualBox=%p fWritable=%RTbool\n", pMedium, pVirtualBox, fWritable));
512 CheckComArgExpr(rStrPassword, rStrPassword.isEmpty() == rStrKeyId.isEmpty()); /* checked by caller */
513
514 /*
515 * Enclose the state transition NotReady->InInit->Ready
516 */
517 AutoInitSpan autoInitSpan(this);
518 AssertReturn(autoInitSpan.isOk(), E_FAIL);
519
520 /*
521 * Allocate data instance.
522 */
523 HRESULT hrc = S_OK;
524 m = new(std::nothrow) Data(pMedium, pVirtualBox, fWritable);
525 if (m)
526 {
527 /*
528 * Add the password to the keystore if specified.
529 */
530 if (rStrKeyId.isNotEmpty())
531 {
532 int vrc = m->PasswordStore.addSecretKey(rStrKeyId, (const uint8_t *)rStrPassword.c_str(),
533 rStrPassword.length() + 1 /*including the Schwarzenegger character*/);
534 if (vrc == VERR_NO_MEMORY)
535 hrc = setError(E_OUTOFMEMORY, tr("Failed to allocate enough secure memory for the key/password"));
536 else if (RT_FAILURE(vrc))
537 hrc = setErrorBoth(E_FAIL, vrc, tr("Unknown error happened while adding a password (%Rrc)"), vrc);
538 }
539
540 /*
541 * Try open the medium and then get a VFS file handle for it.
542 */
543 if (SUCCEEDED(hrc))
544 {
545 hrc = pMedium->i_openForIO(fWritable, &m->PasswordStore, &m->pHdd, &m->LockList, &m->CryptoSettings);
546 if (SUCCEEDED(hrc))
547 {
548 int vrc = VDCreateVfsFileFromDisk(m->pHdd, 0 /*fFlags*/, &m->hVfsFile);
549 if (RT_FAILURE(vrc))
550 {
551 hrc = setErrorBoth(E_FAIL, vrc, tr("VDCreateVfsFileFromDisk failed: %Rrc"), vrc);
552 m->hVfsFile = NIL_RTVFSFILE;
553 }
554 }
555 }
556 }
557 else
558 hrc = E_OUTOFMEMORY;
559
560 /*
561 * Done. Just update object readiness state.
562 */
563 if (SUCCEEDED(hrc))
564 autoInitSpan.setSucceeded();
565 else
566 {
567 if (m)
568 i_close(); /* Free password and whatever i_openHddForIO() may accidentally leave around on failure. */
569 autoInitSpan.setFailed(hrc);
570 }
571
572 LogFlowThisFunc(("returns %Rhrc\n", hrc));
573 return hrc;
574}
575
576/**
577 * Uninitializes the instance (called from FinalRelease()).
578 */
579void MediumIO::uninit()
580{
581 LogFlowThisFuncEnter();
582
583 /* Enclose the state transition Ready->InUninit->NotReady */
584 AutoUninitSpan autoUninitSpan(this);
585 if (!autoUninitSpan.uninitDone())
586 {
587 if (m)
588 {
589 i_close();
590
591 delete m;
592 m = NULL;
593 }
594 }
595
596 LogFlowThisFuncLeave();
597}
598
599
600/*********************************************************************************************************************************
601* IMediumIO attributes *
602*********************************************************************************************************************************/
603
604HRESULT MediumIO::getMedium(ComPtr<IMedium> &a_rPtrMedium)
605{
606 a_rPtrMedium = m->ptrMedium;
607 return S_OK;
608}
609
610HRESULT MediumIO::getWritable(BOOL *a_fWritable)
611{
612 *a_fWritable = m->fWritable;
613 return S_OK;
614}
615
616HRESULT MediumIO::getExplorer(ComPtr<IVFSExplorer> &a_rPtrExplorer)
617{
618 RT_NOREF_PV(a_rPtrExplorer);
619 return E_NOTIMPL;
620}
621
622
623/*********************************************************************************************************************************
624* IMediumIO methods *
625*********************************************************************************************************************************/
626
627HRESULT MediumIO::read(LONG64 a_off, ULONG a_cbRead, std::vector<BYTE> &a_rData)
628{
629 /*
630 * Validate input.
631 */
632 if (a_cbRead > _256K)
633 return setError(E_INVALIDARG, tr("Max read size is 256KB, given: %u"), a_cbRead);
634 if (a_cbRead == 0)
635 return setError(E_INVALIDARG, tr("Zero byte read is not supported."));
636
637 /*
638 * Allocate return buffer.
639 */
640 try
641 {
642 a_rData.resize(a_cbRead);
643 }
644 catch (std::bad_alloc &)
645 {
646 return E_OUTOFMEMORY;
647 }
648
649 /*
650 * Do the reading. To play safe we exclusivly lock the object while doing this.
651 */
652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
653 size_t cbActual = 0;
654 int vrc = RTVfsFileReadAt(m->hVfsFile, a_off, &a_rData.front(), a_cbRead, &cbActual);
655 alock.release();
656
657 /*
658 * Manage the result.
659 */
660 HRESULT hrc;
661 if (RT_SUCCESS(vrc))
662 {
663 if (cbActual != a_cbRead)
664 {
665 Assert(cbActual < a_cbRead);
666 a_rData.resize(cbActual);
667 }
668 hrc = S_OK;
669 }
670 else
671 {
672 a_rData.resize(0);
673 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error reading %u bytes at %RU64: %Rrc"), a_cbRead, a_off, vrc);
674 }
675
676 return hrc;
677}
678
679HRESULT MediumIO::write(LONG64 a_off, const std::vector<BYTE> &a_rData, ULONG *a_pcbWritten)
680{
681 /*
682 * Validate input.
683 */
684 size_t cbToWrite = a_rData.size();
685 if (cbToWrite == 0)
686 return setError(E_INVALIDARG, tr("Zero byte write is not supported."));
687 if (!m->fWritable)
688 return setError(E_ACCESSDENIED, tr("Medium not opened for writing."));
689 CheckComArgPointerValid(a_pcbWritten);
690 *a_pcbWritten = 0;
691
692 /*
693 * Do the writing. To play safe we exclusivly lock the object while doing this.
694 */
695 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
696 size_t cbActual = 0;
697 int vrc = RTVfsFileWriteAt(m->hVfsFile, a_off, &a_rData.front(), cbToWrite, &cbActual);
698 alock.release();
699
700 /*
701 * Manage the result.
702 */
703 HRESULT hrc;
704 if (RT_SUCCESS(vrc))
705 {
706 *a_pcbWritten = (ULONG)cbActual;
707 hrc = S_OK;
708 }
709 else
710 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error writing %zu bytes at %RU64: %Rrc"), cbToWrite, a_off, vrc);
711
712 return hrc;
713}
714
715HRESULT MediumIO::formatFAT(BOOL a_fQuick)
716{
717 /*
718 * Validate input.
719 */
720 if (!m->fWritable)
721 return setError(E_ACCESSDENIED, tr("Medium not opened for writing."));
722
723 /*
724 * Format the medium as FAT and let the format API figure the parameters.
725 * We exclusivly lock the object while doing this as concurrent medium access makes no sense.
726 */
727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
728 RTERRINFOSTATIC ErrInfo;
729 int vrc = RTFsFatVolFormat(m->hVfsFile, 0, 0, a_fQuick ? RTFSFATVOL_FMT_F_QUICK : RTFSFATVOL_FMT_F_FULL,
730 (uint16_t)m->cbSector, 0, RTFSFATTYPE_INVALID, 0, 0, 0, 0, 0, RTErrInfoInitStatic(&ErrInfo));
731 alock.release();
732
733 /*
734 * Manage the result.
735 */
736 HRESULT hrc;
737 if (RT_SUCCESS(vrc))
738 hrc = S_OK;
739 else if (RTErrInfoIsSet(&ErrInfo.Core))
740 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error formatting (%Rrc): %s"), vrc, ErrInfo.Core.pszMsg);
741 else
742 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error formatting: %Rrc"), vrc);
743
744 return hrc;
745}
746
747HRESULT MediumIO::initializePartitionTable(PartitionTableType_T a_enmFormat, BOOL a_fWholeDiskInOneEntry)
748{
749 /*
750 * Validate input.
751 */
752 const char *pszFormat;
753 if (a_enmFormat == PartitionTableType_MBR)
754 pszFormat = "MBR"; /* RTDVMFORMATTYPE_MBR */
755 else if (a_enmFormat == PartitionTableType_GPT)
756 pszFormat = "GPT"; /* RTDVMFORMATTYPE_GPT */
757 else
758 return setError(E_INVALIDARG, tr("Invalid partition format type: %d"), a_enmFormat);
759 if (!m->fWritable)
760 return setError(E_ACCESSDENIED, tr("Medium not opened for writing."));
761 if (a_fWholeDiskInOneEntry)
762 return setError(E_NOTIMPL, tr("whole-disk-in-one-entry is not implemented yet, sorry."));
763
764 /*
765 * Do the partitioning.
766 * We exclusivly lock the object while doing this as concurrent medium access makes little sense.
767 */
768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
769
770 RTDVM hVolMgr;
771 int vrc = RTDvmCreate(&hVolMgr, m->hVfsFile, m->cbSector, 0 /*fFlags*/);
772 HRESULT hrc;
773 if (RT_SUCCESS(vrc))
774 {
775 vrc = RTDvmMapInitialize(hVolMgr, pszFormat); /** @todo Why doesn't RTDvmMapInitialize take RTDVMFORMATTYPE? */
776 if (RT_SUCCESS(vrc))
777 {
778 /*
779 * Create a partition for the whole disk?
780 */
781 hrc = S_OK; /** @todo a_fWholeDiskInOneEntry requies RTDvm to get a function for creating partitions. */
782 }
783 else
784 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("RTDvmMapInitialize failed: %Rrc"), vrc);
785 RTDvmRelease(hVolMgr);
786 }
787 else
788 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("RTDvmCreate failed: %Rrc"), vrc);
789
790 return hrc;
791}
792
793HRESULT MediumIO::convertToStream(const com::Utf8Str &aFormat,
794 const std::vector<MediumVariant_T> &aVariant,
795 ULONG aBufferSize,
796 ComPtr<IDataStream> &aStream,
797 ComPtr<IProgress> &aProgress)
798{
799 HRESULT rc = S_OK;
800 ComObjPtr<Progress> pProgress;
801 ComObjPtr<DataStream> pDataStream;
802 MediumIO::StreamTask *pTask = NULL;
803
804 pProgress.createObject();
805 pDataStream.createObject();
806
807 try
808 {
809 rc = pDataStream->init(aBufferSize);
810 if (FAILED(rc))
811 throw rc;
812
813 ULONG mediumVariantFlags = 0;
814
815 if (aVariant.size())
816 {
817 for (size_t i = 0; i < aVariant.size(); i++)
818 mediumVariantFlags |= (ULONG)aVariant[i];
819 }
820
821 /* setup task object to carry out the operation asynchronously */
822 pTask = new MediumIO::StreamTask(this, pDataStream, pProgress,
823 aFormat.c_str(), (MediumVariant_T)mediumVariantFlags);
824 rc = pTask->rc();
825 AssertComRC(rc);
826 if (FAILED(rc))
827 throw rc;
828 }
829 catch (HRESULT aRC) { rc = aRC; }
830
831 if (SUCCEEDED(rc))
832 {
833 rc = pTask->createThread();
834
835 if (SUCCEEDED(rc))
836 {
837 pDataStream.queryInterfaceTo(aStream.asOutParam());
838 pProgress.queryInterfaceTo(aProgress.asOutParam());
839 }
840 }
841 else if (pTask != NULL)
842 delete pTask;
843
844 return rc;
845}
846
847HRESULT MediumIO::close()
848{
849 /*
850 * We need a write lock here to exclude all other access.
851 */
852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
853 i_close();
854 return S_OK;
855}
856
857
858
859/*********************************************************************************************************************************
860* IMediumIO internal methods *
861*********************************************************************************************************************************/
862
863/**
864 * This is used by both uninit and close().
865 *
866 * Expects exclusive access (write lock or autouninit) to the object.
867 */
868void MediumIO::i_close()
869{
870 if (m->hVfsFile != NIL_RTVFSFILE)
871 {
872 uint32_t cRefs = RTVfsFileRelease(m->hVfsFile);
873 Assert(cRefs == 0);
874 NOREF(cRefs);
875
876 m->hVfsFile = NIL_RTVFSFILE;
877 }
878
879 if (m->pHdd)
880 {
881 VDDestroy(m->pHdd);
882 m->pHdd = NULL;
883 }
884
885 m->LockList.Clear();
886 m->ptrMedium.setNull();
887 m->PasswordStore.deleteAllSecretKeys(false /* fSuspend */, true /* fForce */);
888}
889
Note: See TracBrowser for help on using the repository browser.

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