VirtualBox

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

Last change on this file since 86506 was 82968, checked in by vboxsync, 4 years ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use