VirtualBox

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

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

Main/MediumIO: Fix progress reporting when streaming the disk image [build fix]

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 28.9 KB
 
1/* $Id: MediumIOImpl.cpp 74938 2018-10-19 09:54:12Z 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 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 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 PVDISK pDstDisk;
420
421 if (mProgress)
422 {
423 IfsProgress.pfnProgress = mProgress->i_vdProgressCallback;
424 VDInterfaceAdd(&IfsProgress.Core,
425 "Medium::StreamTask::vdInterfaceProgress",
426 VDINTERFACETYPE_PROGRESS,
427 mProgress,
428 sizeof(IfsProgress),
429 &pIfsOp);
430 }
431
432 IfsOutputIO.pfnOpen = i_vdStreamOpen;
433 IfsOutputIO.pfnClose = i_vdStreamClose;
434 IfsOutputIO.pfnDelete = i_vdStreamDelete;
435 IfsOutputIO.pfnMove = i_vdStreamMove;
436 IfsOutputIO.pfnGetFreeSpace = i_vdStreamGetFreeSpace;
437 IfsOutputIO.pfnGetModificationTime = i_vdStreamGetModificationTime;
438 IfsOutputIO.pfnGetSize = i_vdStreamGetSize;
439 IfsOutputIO.pfnSetSize = i_vdStreamSetSize;
440 IfsOutputIO.pfnReadSync = i_vdStreamRead;
441 IfsOutputIO.pfnWriteSync = i_vdStreamWrite;
442 IfsOutputIO.pfnFlushSync = i_vdStreamFlush;
443 VDInterfaceAdd(&IfsOutputIO.Core, "stream", VDINTERFACETYPE_IO,
444 m_pDataStream, sizeof(VDINTERFACEIO), &pIfsOp);
445
446 int vrc = VDCreate(NULL, VDTYPE_HDD, &pDstDisk);
447 if (RT_SUCCESS(vrc))
448 {
449 /* Create the output image */
450 vrc = VDCopy(mMediumIO->m->pHdd, VD_LAST_IMAGE, pDstDisk, m_strFormat.c_str(),
451 "stream", false, 0, m_fMediumVariant, NULL,
452 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL, NULL,
453 pIfsOp, NULL);
454 if (RT_FAILURE(vrc))
455 hrc = mMediumIO->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
456 tr("Failed to convert and stream disk image"));
457
458 VDDestroy(pDstDisk);
459 }
460 else
461 hrc = mMediumIO->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
462 tr("Failed to create destination disk container"));
463
464 return hrc;
465}
466
467
468/*********************************************************************************************************************************
469* Boilerplate constructor & destructor *
470*********************************************************************************************************************************/
471
472DEFINE_EMPTY_CTOR_DTOR(MediumIO)
473
474HRESULT MediumIO::FinalConstruct()
475{
476 LogFlowThisFunc(("\n"));
477 return BaseFinalConstruct();
478}
479
480void MediumIO::FinalRelease()
481{
482 LogFlowThisFuncEnter();
483 uninit();
484 BaseFinalRelease();
485 LogFlowThisFuncLeave();
486}
487
488
489/*********************************************************************************************************************************
490* Initializer & uninitializer *
491*********************************************************************************************************************************/
492
493/**
494 * Initializes the medium I/O object.
495 *
496 * @param pMedium Pointer to the medium to access.
497 * @param pVirtualBox Pointer to the VirtualBox object the medium is part of.
498 * @param fWritable Read-write (true) or readonly (false) access.
499 * @param rStrKeyId The key ID for an encrypted medium. Empty if not
500 * encrypted.
501 * @param rStrPassword The password for an encrypted medium. Empty if not
502 * encrypted.
503 *
504 */
505HRESULT MediumIO::initForMedium(Medium *pMedium, VirtualBox *pVirtualBox, bool fWritable,
506 com::Utf8Str const &rStrKeyId, com::Utf8Str const &rStrPassword)
507{
508 LogFlowThisFunc(("pMedium=%p pVirtualBox=%p fWritable=%RTbool\n", pMedium, pVirtualBox, fWritable));
509 CheckComArgExpr(rStrPassword, rStrPassword.isEmpty() == rStrKeyId.isEmpty()); /* checked by caller */
510
511 /*
512 * Enclose the state transition NotReady->InInit->Ready
513 */
514 AutoInitSpan autoInitSpan(this);
515 AssertReturn(autoInitSpan.isOk(), E_FAIL);
516
517 /*
518 * Allocate data instance.
519 */
520 HRESULT hrc = S_OK;
521 m = new(std::nothrow) Data(pMedium, pVirtualBox, fWritable);
522 if (m)
523 {
524 /*
525 * Add the password to the keystore if specified.
526 */
527 if (rStrKeyId.isNotEmpty())
528 {
529 int vrc = m->PasswordStore.addSecretKey(rStrKeyId, (const uint8_t *)rStrPassword.c_str(),
530 rStrPassword.length() + 1 /*including the Schwarzenegger character*/);
531 if (vrc == VERR_NO_MEMORY)
532 hrc = setError(E_OUTOFMEMORY, tr("Failed to allocate enough secure memory for the key/password"));
533 else if (RT_FAILURE(vrc))
534 hrc = setErrorBoth(E_FAIL, vrc, tr("Unknown error happened while adding a password (%Rrc)"), vrc);
535 }
536
537 /*
538 * Try open the medium and then get a VFS file handle for it.
539 */
540 if (SUCCEEDED(hrc))
541 {
542 hrc = pMedium->i_openForIO(fWritable, &m->PasswordStore, &m->pHdd, &m->LockList, &m->CryptoSettings);
543 if (SUCCEEDED(hrc))
544 {
545 int vrc = VDCreateVfsFileFromDisk(m->pHdd, 0 /*fFlags*/, &m->hVfsFile);
546 if (RT_FAILURE(vrc))
547 {
548 hrc = setErrorBoth(E_FAIL, vrc, tr("VDCreateVfsFileFromDisk failed: %Rrc"), vrc);
549 m->hVfsFile = NIL_RTVFSFILE;
550 }
551 }
552 }
553 }
554 else
555 hrc = E_OUTOFMEMORY;
556
557 /*
558 * Done. Just update object readiness state.
559 */
560 if (SUCCEEDED(hrc))
561 autoInitSpan.setSucceeded();
562 else
563 {
564 if (m)
565 i_close(); /* Free password and whatever i_openHddForIO() may accidentally leave around on failure. */
566 autoInitSpan.setFailed(hrc);
567 }
568
569 LogFlowThisFunc(("returns %Rhrc\n", hrc));
570 return hrc;
571}
572
573/**
574 * Uninitializes the instance (called from FinalRelease()).
575 */
576void MediumIO::uninit()
577{
578 LogFlowThisFuncEnter();
579
580 /* Enclose the state transition Ready->InUninit->NotReady */
581 AutoUninitSpan autoUninitSpan(this);
582 if (!autoUninitSpan.uninitDone())
583 {
584 if (m)
585 {
586 i_close();
587
588 delete m;
589 m = NULL;
590 }
591 }
592
593 LogFlowThisFuncLeave();
594}
595
596
597/*********************************************************************************************************************************
598* IMediumIO attributes *
599*********************************************************************************************************************************/
600
601HRESULT MediumIO::getMedium(ComPtr<IMedium> &a_rPtrMedium)
602{
603 a_rPtrMedium = m->ptrMedium;
604 return S_OK;
605}
606
607HRESULT MediumIO::getWritable(BOOL *a_fWritable)
608{
609 *a_fWritable = m->fWritable;
610 return S_OK;
611}
612
613HRESULT MediumIO::getExplorer(ComPtr<IVFSExplorer> &a_rPtrExplorer)
614{
615 RT_NOREF_PV(a_rPtrExplorer);
616 return E_NOTIMPL;
617}
618
619
620/*********************************************************************************************************************************
621* IMediumIO methods *
622*********************************************************************************************************************************/
623
624HRESULT MediumIO::read(LONG64 a_off, ULONG a_cbRead, std::vector<BYTE> &a_rData)
625{
626 /*
627 * Validate input.
628 */
629 if (a_cbRead > _256K)
630 return setError(E_INVALIDARG, tr("Max read size is 256KB, given: %u"), a_cbRead);
631 if (a_cbRead == 0)
632 return setError(E_INVALIDARG, tr("Zero byte read is not supported."));
633
634 /*
635 * Allocate return buffer.
636 */
637 try
638 {
639 a_rData.resize(a_cbRead);
640 }
641 catch (std::bad_alloc &)
642 {
643 return E_OUTOFMEMORY;
644 }
645
646 /*
647 * Do the reading. To play safe we exclusivly lock the object while doing this.
648 */
649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
650 size_t cbActual = 0;
651 int vrc = RTVfsFileReadAt(m->hVfsFile, a_off, &a_rData.front(), a_cbRead, &cbActual);
652 alock.release();
653
654 /*
655 * Manage the result.
656 */
657 HRESULT hrc;
658 if (RT_SUCCESS(vrc))
659 {
660 if (cbActual != a_cbRead)
661 {
662 Assert(cbActual < a_cbRead);
663 a_rData.resize(cbActual);
664 }
665 hrc = S_OK;
666 }
667 else
668 {
669 a_rData.resize(0);
670 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error reading %u bytes at %RU64: %Rrc"), a_cbRead, a_off, vrc);
671 }
672
673 return hrc;
674}
675
676HRESULT MediumIO::write(LONG64 a_off, const std::vector<BYTE> &a_rData, ULONG *a_pcbWritten)
677{
678 /*
679 * Validate input.
680 */
681 size_t cbToWrite = a_rData.size();
682 if (cbToWrite == 0)
683 return setError(E_INVALIDARG, tr("Zero byte write is not supported."));
684 if (!m->fWritable)
685 return setError(E_ACCESSDENIED, tr("Medium not opened for writing."));
686 CheckComArgPointerValid(a_pcbWritten);
687 *a_pcbWritten = 0;
688
689 /*
690 * Do the writing. To play safe we exclusivly lock the object while doing this.
691 */
692 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
693 size_t cbActual = 0;
694 int vrc = RTVfsFileWriteAt(m->hVfsFile, a_off, &a_rData.front(), cbToWrite, &cbActual);
695 alock.release();
696
697 /*
698 * Manage the result.
699 */
700 HRESULT hrc;
701 if (RT_SUCCESS(vrc))
702 {
703 *a_pcbWritten = (ULONG)cbActual;
704 hrc = S_OK;
705 }
706 else
707 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error writing %zu bytes at %RU64: %Rrc"), cbToWrite, a_off, vrc);
708
709 return hrc;
710}
711
712HRESULT MediumIO::formatFAT(BOOL a_fQuick)
713{
714 /*
715 * Validate input.
716 */
717 if (!m->fWritable)
718 return setError(E_ACCESSDENIED, tr("Medium not opened for writing."));
719
720 /*
721 * Format the medium as FAT and let the format API figure the parameters.
722 * We exclusivly lock the object while doing this as concurrent medium access makes no sense.
723 */
724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
725 RTERRINFOSTATIC ErrInfo;
726 int vrc = RTFsFatVolFormat(m->hVfsFile, 0, 0, a_fQuick ? RTFSFATVOL_FMT_F_QUICK : RTFSFATVOL_FMT_F_FULL,
727 (uint16_t)m->cbSector, 0, RTFSFATTYPE_INVALID, 0, 0, 0, 0, 0, RTErrInfoInitStatic(&ErrInfo));
728 alock.release();
729
730 /*
731 * Manage the result.
732 */
733 HRESULT hrc;
734 if (RT_SUCCESS(vrc))
735 hrc = S_OK;
736 else if (RTErrInfoIsSet(&ErrInfo.Core))
737 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error formatting (%Rrc): %s"), vrc, ErrInfo.Core.pszMsg);
738 else
739 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error formatting: %Rrc"), vrc);
740
741 return hrc;
742}
743
744HRESULT MediumIO::initializePartitionTable(PartitionTableType_T a_enmFormat, BOOL a_fWholeDiskInOneEntry)
745{
746 /*
747 * Validate input.
748 */
749 const char *pszFormat;
750 if (a_enmFormat == PartitionTableType_MBR)
751 pszFormat = "MBR"; /* RTDVMFORMATTYPE_MBR */
752 else if (a_enmFormat == PartitionTableType_GPT)
753 pszFormat = "GPT"; /* RTDVMFORMATTYPE_GPT */
754 else
755 return setError(E_INVALIDARG, tr("Invalid partition format type: %d"), a_enmFormat);
756 if (!m->fWritable)
757 return setError(E_ACCESSDENIED, tr("Medium not opened for writing."));
758 if (a_fWholeDiskInOneEntry)
759 return setError(E_NOTIMPL, tr("whole-disk-in-one-entry is not implemented yet, sorry."));
760
761 /*
762 * Do the partitioning.
763 * We exclusivly lock the object while doing this as concurrent medium access makes little sense.
764 */
765 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
766
767 RTDVM hVolMgr;
768 int vrc = RTDvmCreate(&hVolMgr, m->hVfsFile, m->cbSector, 0 /*fFlags*/);
769 HRESULT hrc;
770 if (RT_SUCCESS(vrc))
771 {
772 vrc = RTDvmMapInitialize(hVolMgr, pszFormat); /** @todo Why doesn't RTDvmMapInitialize take RTDVMFORMATTYPE? */
773 if (RT_SUCCESS(vrc))
774 {
775 /*
776 * Create a partition for the whole disk?
777 */
778 hrc = S_OK; /** @todo a_fWholeDiskInOneEntry requies RTDvm to get a function for creating partitions. */
779 }
780 else
781 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("RTDvmMapInitialize failed: %Rrc"), vrc);
782 RTDvmRelease(hVolMgr);
783 }
784 else
785 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("RTDvmCreate failed: %Rrc"), vrc);
786
787 return hrc;
788}
789
790HRESULT MediumIO::convertToStream(const com::Utf8Str &aFormat,
791 const std::vector<MediumVariant_T> &aVariant,
792 ULONG aBufferSize,
793 ComPtr<IDataStream> &aStream,
794 ComPtr<IProgress> &aProgress)
795{
796 HRESULT rc = S_OK;
797 ComObjPtr<Progress> pProgress;
798 ComObjPtr<DataStream> pDataStream;
799 MediumIO::StreamTask *pTask = NULL;
800
801 pProgress.createObject();
802 pDataStream.createObject();
803
804 try
805 {
806 rc = pDataStream->init(aBufferSize);
807 if (FAILED(rc))
808 throw rc;
809
810 ULONG mediumVariantFlags = 0;
811
812 if (aVariant.size())
813 {
814 for (size_t i = 0; i < aVariant.size(); i++)
815 mediumVariantFlags |= (ULONG)aVariant[i];
816 }
817
818 /* setup task object to carry out the operation asynchronously */
819 pTask = new MediumIO::StreamTask(this, pDataStream, pProgress,
820 aFormat.c_str(), (MediumVariant_T)mediumVariantFlags);
821 rc = pTask->rc();
822 AssertComRC(rc);
823 if (FAILED(rc))
824 throw rc;
825 }
826 catch (HRESULT aRC) { rc = aRC; }
827
828 if (SUCCEEDED(rc))
829 {
830 rc = pTask->createThread();
831
832 if (SUCCEEDED(rc))
833 {
834 pDataStream.queryInterfaceTo(aStream.asOutParam());
835 pProgress.queryInterfaceTo(aProgress.asOutParam());
836 }
837 }
838 else if (pTask != NULL)
839 delete pTask;
840
841 return rc;
842}
843
844HRESULT MediumIO::close()
845{
846 /*
847 * We need a write lock here to exclude all other access.
848 */
849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
850 i_close();
851 return S_OK;
852}
853
854
855
856/*********************************************************************************************************************************
857* IMediumIO internal methods *
858*********************************************************************************************************************************/
859
860/**
861 * This is used by both uninit and close().
862 *
863 * Expects exclusive access (write lock or autouninit) to the object.
864 */
865void MediumIO::i_close()
866{
867 if (m->hVfsFile != NIL_RTVFSFILE)
868 {
869 uint32_t cRefs = RTVfsFileRelease(m->hVfsFile);
870 Assert(cRefs == 0);
871 NOREF(cRefs);
872
873 m->hVfsFile = NIL_RTVFSFILE;
874 }
875
876 if (m->pHdd)
877 {
878 VDDestroy(m->pHdd);
879 m->pHdd = NULL;
880 }
881
882 m->LockList.Clear();
883 m->ptrMedium.setNull();
884 m->PasswordStore.deleteAllSecretKeys(false /* fSuspend */, true /* fForce */);
885}
886
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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