VirtualBox

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

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

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