VirtualBox

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

最後變更 在這個檔案從74937是 74937,由 vboxsync 提交於 6 年 前

Main/MediumIO: Fix progress reporting when streaming the disk image

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 29.0 KB
 
1/* $Id: MediumIOImpl.cpp 74937 2018-10-19 09:50:40Z 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.alldomusa.eu.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
123 // Make all destructors virtual. Just in case.
124 virtual ~StreamTask()
125 {
126 /* send the notification of completion.*/
127 if ( isAsync()
128 && !mProgress.isNull())
129 mProgress->i_notifyComplete(mRC);
130 }
131
132 HRESULT rc() const { return mRC; }
133 bool isOk() const { return SUCCEEDED(rc()); }
134
135 const ComPtr<Progress>& GetProgressObject() const {return mProgress;}
136
137 /**
138 * Implementation code for the "create base" task.
139 * Used as function for execution from a standalone thread.
140 */
141 void handler()
142 {
143 LogFlowFuncEnter();
144 try
145 {
146 mRC = executeTask(); /* (destructor picks up mRC, see above) */
147 LogFlowFunc(("rc=%Rhrc\n", mRC));
148 }
149 catch (...)
150 {
151 LogRel(("Some exception in the function MediumIO::StreamTask:handler()\n"));
152 }
153
154 LogFlowFuncLeave();
155 }
156
157 const ComObjPtr<MediumIO> mMediumIO;
158 AutoCaller mMediumCaller;
159
160protected:
161 HRESULT mRC;
162
163 DataStream *m_pDataStream;
164 MediumVariant_T m_fMediumVariant;
165 Utf8Str m_strFormat;
166
167private:
168 HRESULT executeTask();
169
170 const ComObjPtr<Progress> mProgress;
171
172 /* Must have a strong VirtualBox reference during a task otherwise the
173 * reference count might drop to 0 while a task is still running. This
174 * would result in weird behavior, including deadlocks due to uninit and
175 * locking order issues. The deadlock often is not detectable because the
176 * uninit uses event semaphores which sabotages deadlock detection. */
177 ComObjPtr<VirtualBox> mVirtualBox;
178 AutoCaller mVirtualBoxCaller;
179
180 static DECLCALLBACK(int) i_vdStreamOpen(void *pvUser, const char *pszLocation, uint32_t fOpen,
181 PFNVDCOMPLETED pfnCompleted, void **ppStorage);
182 static DECLCALLBACK(int) i_vdStreamClose(void *pvUser, void *pStorage);
183 static DECLCALLBACK(int) i_vdStreamDelete(void *pvUser, const char *pcszFilename);
184 static DECLCALLBACK(int) i_vdStreamMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove);
185 static DECLCALLBACK(int) i_vdStreamGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace);
186 static DECLCALLBACK(int) i_vdStreamGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime);
187 static DECLCALLBACK(int) i_vdStreamGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize);
188 static DECLCALLBACK(int) i_vdStreamSetSize(void *pvUser, void *pStorage, uint64_t cbSize);
189 static DECLCALLBACK(int) i_vdStreamRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuffer, size_t cbBuffer,
190 size_t *pcbRead);
191 static DECLCALLBACK(int) i_vdStreamWrite(void *pvUser, void *pStorage, uint64_t uOffset,
192 const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten);
193 static DECLCALLBACK(int) i_vdStreamFlush(void *pvUser, void *pStorage);
194};
195
196
197/**
198 * State of a streamed file.
199 */
200typedef struct STREAMFILE
201{
202 /** The data stream for this file state. */
203 DataStream *pDataStream;
204 /** The last seen offset used to stream zeroes for non consecutive writes. */
205 uint64_t uOffsetLast;
206 /** Set file size. */
207 uint64_t cbFile;
208} STREAMFILE;
209/** Pointer to the stream file state. */
210typedef STREAMFILE *PSTREAMFILE;
211
212
213
214DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamOpen(void *pvUser, const char *pszLocation, uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
215 void **ppStorage)
216{
217 RT_NOREF2(pvUser, pszLocation);
218
219 /* Validate input. */
220 AssertPtrReturn(ppStorage, VERR_INVALID_POINTER);
221 AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
222 AssertReturn((fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_WRITE, VERR_INVALID_PARAMETER);
223
224 int rc = VINF_SUCCESS;
225 PSTREAMFILE pStreamFile = (PSTREAMFILE)RTMemAllocZ(sizeof(*pStreamFile));
226 if (RT_LIKELY(pStreamFile))
227 {
228 pStreamFile->pDataStream = (DataStream *)pvUser;
229 pStreamFile->uOffsetLast = 0;
230 pStreamFile->cbFile = 0;
231 *ppStorage = pStreamFile;
232 }
233 else
234 rc = VERR_NO_MEMORY;
235
236 return rc;
237}
238
239DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamClose(void *pvUser, void *pStorage)
240{
241 RT_NOREF(pvUser);
242 PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
243 int rc = VINF_SUCCESS;
244
245 /* Fill up to the configured file size. */
246 if (pStreamFile->uOffsetLast < pStreamFile->cbFile)
247 {
248 do
249 {
250 size_t cbThisWrite = sizeof(g_abRTZero64K);
251 size_t cbWritten = 0;
252
253 if (pStreamFile->cbFile - pStreamFile->uOffsetLast < sizeof(g_abRTZero64K))
254 cbThisWrite = (size_t)(pStreamFile->cbFile - pStreamFile->uOffsetLast);
255
256 rc = pStreamFile->pDataStream->i_write(&g_abRTZero64K[0], cbThisWrite, &cbWritten);
257 if (RT_SUCCESS(rc))
258 pStreamFile->uOffsetLast += cbWritten;
259
260 } while ( RT_SUCCESS(rc)
261 && pStreamFile->uOffsetLast < pStreamFile->cbFile);
262 }
263
264 int rc2 = pStreamFile->pDataStream->i_close();
265 if (RT_SUCCESS(rc))
266 rc = rc2;
267
268 RTMemFree(pStreamFile);
269 return rc;
270}
271
272DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamDelete(void *pvUser, const char *pcszFilename)
273{
274 NOREF(pvUser);
275 NOREF(pcszFilename);
276 AssertFailedReturn(VERR_NOT_SUPPORTED);
277}
278
279DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
280{
281 NOREF(pvUser);
282 NOREF(pcszSrc);
283 NOREF(pcszDst);
284 NOREF(fMove);
285 AssertFailedReturn(VERR_NOT_SUPPORTED);
286}
287
288DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
289{
290 NOREF(pvUser);
291 NOREF(pcszFilename);
292 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
293 *pcbFreeSpace = INT64_MAX;
294 return VINF_SUCCESS;
295}
296
297DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
298{
299 NOREF(pvUser);
300 NOREF(pcszFilename);
301 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
302 AssertFailedReturn(VERR_NOT_SUPPORTED);
303}
304
305DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
306{
307 NOREF(pvUser);
308 PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
309 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
310
311 *pcbSize = pStreamFile->cbFile;
312 return VINF_SUCCESS;
313}
314
315DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
316{
317 RT_NOREF(pvUser);
318 PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
319
320 /* Reducing the size is not supported. */
321 int rc = VINF_SUCCESS;
322 if (pStreamFile->cbFile < cbSize)
323 pStreamFile->cbFile = cbSize;
324 else
325 rc = VERR_NOT_SUPPORTED;
326
327 return rc;
328}
329
330DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuffer, size_t cbBuffer,
331 size_t *pcbRead)
332{
333 NOREF(pvUser);
334 NOREF(pStorage);
335 NOREF(uOffset);
336 NOREF(cbBuffer);
337 NOREF(pcbRead);
338 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
339 AssertFailedReturn(VERR_NOT_SUPPORTED);
340}
341
342DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamWrite(void *pvUser, void *pStorage, uint64_t uOffset, const void *pvBuffer, size_t cbBuffer,
343 size_t *pcbWritten)
344{
345 RT_NOREF(pvUser);
346 PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
347 int rc = VINF_SUCCESS;
348
349 /* Fill up to the new offset if there is non consecutive access. */
350 if (pStreamFile->uOffsetLast < uOffset)
351 {
352 do
353 {
354 size_t cbThisWrite = sizeof(g_abRTZero64K);
355 size_t cbWritten = 0;
356
357 if (uOffset - pStreamFile->uOffsetLast < sizeof(g_abRTZero64K))
358 cbThisWrite = (size_t)(uOffset - pStreamFile->uOffsetLast);
359
360 rc = pStreamFile->pDataStream->i_write(&g_abRTZero64K[0], cbThisWrite, &cbWritten);
361 if (RT_SUCCESS(rc))
362 pStreamFile->uOffsetLast += cbWritten;
363
364 } while ( RT_SUCCESS(rc)
365 && pStreamFile->uOffsetLast < uOffset);
366 }
367
368 if (RT_SUCCESS(rc))
369 {
370 if (pcbWritten)
371 rc = pStreamFile->pDataStream->i_write(pvBuffer, cbBuffer, pcbWritten);
372 else
373 {
374 const uint8_t *pbBuf = (const uint8_t *)pvBuffer;
375 size_t cbLeft = cbBuffer;
376 size_t cbWritten = 0;
377 while ( cbLeft > 0
378 && RT_SUCCESS(rc))
379 {
380 rc = pStreamFile->pDataStream->i_write(pbBuf, cbLeft, &cbWritten);
381 if (RT_SUCCESS(rc))
382 {
383 pbBuf += cbWritten;
384 cbLeft -= cbWritten;
385 }
386 }
387 }
388
389 if (RT_SUCCESS(rc))
390 {
391 size_t cbWritten = pcbWritten ? *pcbWritten : cbBuffer;
392
393 /* Adjust file size. */
394 if (uOffset + cbWritten > pStreamFile->cbFile)
395 pStreamFile->cbFile = uOffset + cbWritten;
396
397 pStreamFile->uOffsetLast = uOffset + cbWritten;
398 }
399 }
400
401 return rc;
402}
403
404DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamFlush(void *pvUser, void *pStorage)
405{
406 NOREF(pvUser);
407 NOREF(pStorage);
408 return VINF_SUCCESS;
409}
410
411/**
412 * Implementation code for the "stream" task.
413 */
414HRESULT MediumIO::StreamTask::executeTask()
415{
416 HRESULT hrc = S_OK;
417 VDINTERFACEIO IfsOutputIO;
418 VDINTERFACEPROGRESS IfsProgress;
419 PVDINTERFACE pIfsOp = NULL;
420 PVDISK pDstDisk;
421
422 if (mProgress)
423 {
424 IfsProgress.pfnProgress = pProgress->i_vdProgressCallback;
425 int vrc = 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), &pIfsOp);
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, NULL,
454 pIfsOp, 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 pProgress.createObject();
803 pDataStream.createObject();
804
805 try
806 {
807 rc = pDataStream->init(aBufferSize);
808 if (FAILED(rc))
809 throw rc;
810
811 ULONG mediumVariantFlags = 0;
812
813 if (aVariant.size())
814 {
815 for (size_t i = 0; i < aVariant.size(); i++)
816 mediumVariantFlags |= (ULONG)aVariant[i];
817 }
818
819 /* setup task object to carry out the operation asynchronously */
820 pTask = new MediumIO::StreamTask(this, pDataStream, pProgress,
821 aFormat.c_str(), (MediumVariant_T)mediumVariantFlags);
822 rc = pTask->rc();
823 AssertComRC(rc);
824 if (FAILED(rc))
825 throw rc;
826 }
827 catch (HRESULT aRC) { rc = aRC; }
828
829 if (SUCCEEDED(rc))
830 {
831 rc = pTask->createThread();
832
833 if (SUCCEEDED(rc))
834 {
835 pDataStream.queryInterfaceTo(aStream.asOutParam());
836 pProgress.queryInterfaceTo(aProgress.asOutParam());
837 }
838 }
839 else if (pTask != NULL)
840 delete pTask;
841
842 return rc;
843}
844
845HRESULT MediumIO::close()
846{
847 /*
848 * We need a write lock here to exclude all other access.
849 */
850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
851 i_close();
852 return S_OK;
853}
854
855
856
857/*********************************************************************************************************************************
858* IMediumIO internal methods *
859*********************************************************************************************************************************/
860
861/**
862 * This is used by both uninit and close().
863 *
864 * Expects exclusive access (write lock or autouninit) to the object.
865 */
866void MediumIO::i_close()
867{
868 if (m->hVfsFile != NIL_RTVFSFILE)
869 {
870 uint32_t cRefs = RTVfsFileRelease(m->hVfsFile);
871 Assert(cRefs == 0);
872 NOREF(cRefs);
873
874 m->hVfsFile = NIL_RTVFSFILE;
875 }
876
877 if (m->pHdd)
878 {
879 VDDestroy(m->pHdd);
880 m->pHdd = NULL;
881 }
882
883 m->LockList.Clear();
884 m->ptrMedium.setNull();
885 m->PasswordStore.deleteAllSecretKeys(false /* fSuspend */, true /* fForce */);
886}
887
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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