VirtualBox

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

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

Main: Introducing IMediumIO for accessing the content of a medium. The new IMedium::openForIO method instantiates and returns this interface. N.B. Write access hasn't been implemented yet.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.9 KB
Line 
1/* $Id: MediumIOImpl.cpp 72948 2018-07-07 16:20:42Z 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.virtualbox.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 "Global.h"
27
28#include "AutoCaller.h"
29#include "Logging.h"
30
31#include <iprt/fsvfs.h>
32#include <iprt/dvm.h>
33
34
35/*********************************************************************************************************************************
36* Structures and Typedefs *
37*********************************************************************************************************************************/
38/**
39 * Private member data.
40 */
41struct MediumIO::Data
42{
43 Data(Medium * const a_pMedium = NULL, bool a_fWritable = false, uint32_t a_cbSector = 512)
44 : ptrMedium(a_pMedium)
45 , fWritable(a_fWritable)
46 , cbSector(a_cbSector)
47 , SecretKeyStore(false /*fKeyBufNonPageable*/)
48 , pHdd(NULL)
49 , hVfsFile(NIL_RTVFSFILE)
50 {
51 }
52
53 /** Reference to the medium we're accessing. */
54 ComPtr<Medium> ptrMedium;
55 /** Set if writable, clear if readonly. */
56 bool fWritable;
57 /** The sector size. */
58 uint32_t cbSector;
59 /** Secret key store used to hold the passwords for encrypted medium. */
60 SecretKeyStore SecretKeyStore;
61 /** Crypto filter settings. */
62 MediumCryptoFilterSettings CryptoSettings;
63 /** Medium lock list. */
64 MediumLockList LockList;
65 /** The HDD instance. */
66 PVDISK pHdd;
67 /** VFS file for the HDD instance. */
68 RTVFSFILE hVfsFile;
69};
70
71
72/*********************************************************************************************************************************
73* Boilerplate constructor & destructor *
74*********************************************************************************************************************************/
75
76DEFINE_EMPTY_CTOR_DTOR(MediumIO)
77
78HRESULT MediumIO::FinalConstruct()
79{
80 LogFlowThisFunc(("\n"));
81 return BaseFinalConstruct();
82}
83
84void MediumIO::FinalRelease()
85{
86 LogFlowThisFuncEnter();
87 uninit();
88 BaseFinalRelease();
89 LogFlowThisFuncLeave();
90}
91
92
93/*********************************************************************************************************************************
94* Initializer & uninitializer *
95*********************************************************************************************************************************/
96
97/**
98 * Initializes the medium I/O object.
99 *
100 * @param pMedium Pointer to the medium to access.
101 * @param fWritable Read-write (true) or readonly (false) access.
102 * @param rStrKeyId The key ID for an encrypted medium. Empty if not
103 * encrypted.
104 * @param rStrPassword The password for an encrypted medium. Empty if not
105 * encrypted.
106 *
107 */
108HRESULT MediumIO::initForMedium(Medium *pMedium, bool fWritable, com::Utf8Str const &rStrKeyId, com::Utf8Str const &rStrPassword)
109{
110 LogFlowThisFunc(("pMedium=%p fWritable=%RTbool\n", pMedium, fWritable));
111 CheckComArgExpr(rStrPassword, rStrPassword.isEmpty() == rStrKeyId.isEmpty()); /* checked by caller */
112
113 /*
114 * Enclose the state transition NotReady->InInit->Ready
115 */
116 AutoInitSpan autoInitSpan(this);
117 AssertReturn(autoInitSpan.isOk(), E_FAIL);
118
119 /*
120 * Allocate data instance.
121 */
122 HRESULT hrc = S_OK;
123 m = new(std::nothrow) Data(pMedium, fWritable);
124 if (m)
125 {
126 /*
127 * Add the password to the keystore if specified.
128 */
129 if (rStrKeyId.isNotEmpty())
130 {
131 int vrc = m->SecretKeyStore.addSecretKey(rStrKeyId, (const uint8_t *)rStrPassword.c_str(),
132 rStrPassword.length() + 1 /*including the Schwarzenegger character*/);
133 if (vrc == VERR_NO_MEMORY)
134 hrc = setError(E_OUTOFMEMORY, tr("Failed to allocate enough secure memory for the key/password"));
135 else if (RT_FAILURE(vrc))
136 hrc = setErrorBoth(E_FAIL, vrc, tr("Unknown error happened while adding a password (%Rrc)"), vrc);
137 }
138
139 /*
140 * Try open the medium and then get a VFS file handle for it.
141 */
142 if (SUCCEEDED(hrc))
143 {
144 hrc = pMedium->i_openHddForIO(fWritable, &m->SecretKeyStore, &m->pHdd, &m->LockList, &m->CryptoSettings);
145 if (SUCCEEDED(hrc))
146 {
147 int vrc = VDCreateVfsFileFromDisk(m->pHdd, 0 /*fFlags*/, &m->hVfsFile);
148 if (RT_FAILURE(vrc))
149 {
150 hrc = setErrorBoth(E_FAIL, vrc, tr("VDCreateVfsFileFromDisk failed: %Rrc"), vrc);
151 m->hVfsFile = NIL_RTVFSFILE;
152 }
153 }
154 }
155 }
156 else
157 hrc = E_OUTOFMEMORY;
158
159 /*
160 * Done. Just update object readiness state.
161 */
162 if (SUCCEEDED(hrc))
163 autoInitSpan.setSucceeded();
164 else
165 {
166 if (m)
167 i_close(); /* Free password and whatever i_openHddForIO() may accidentally leave around on failure. */
168 autoInitSpan.setFailed(hrc);
169 }
170
171 LogFlowThisFunc(("returns %Rhrc\n", hrc));
172 return hrc;
173}
174
175/**
176 * Uninitializes the instance (called from FinalRelease()).
177 */
178void MediumIO::uninit()
179{
180 LogFlowThisFuncEnter();
181
182 /* Enclose the state transition Ready->InUninit->NotReady */
183 AutoUninitSpan autoUninitSpan(this);
184 if (!autoUninitSpan.uninitDone())
185 {
186 if (m)
187 {
188 i_close();
189
190 delete m;
191 m = NULL;
192 }
193 }
194
195 LogFlowThisFuncLeave();
196}
197
198
199/*********************************************************************************************************************************
200* IMediumIO attributes *
201*********************************************************************************************************************************/
202
203HRESULT MediumIO::getMedium(ComPtr<IMedium> &a_rPtrMedium)
204{
205 a_rPtrMedium = m->ptrMedium;
206 return S_OK;
207}
208
209HRESULT MediumIO::getWritable(BOOL *a_fWritable)
210{
211 *a_fWritable = m->fWritable;
212 return S_OK;
213}
214
215HRESULT MediumIO::getExplorer(ComPtr<IVFSExplorer> &a_rPtrExplorer)
216{
217 RT_NOREF_PV(a_rPtrExplorer);
218 return E_NOTIMPL;
219}
220
221
222/*********************************************************************************************************************************
223* IMediumIO methods *
224*********************************************************************************************************************************/
225
226HRESULT MediumIO::read(LONG64 a_off, ULONG a_cbRead, std::vector<BYTE> &a_rData)
227{
228 /*
229 * Validate input.
230 */
231 if (a_cbRead > _256K)
232 return setError(E_INVALIDARG, tr("Max read size is 256KB, given: %u"), a_cbRead);
233 if (a_cbRead == 0)
234 return setError(E_INVALIDARG, tr("Zero byte read is not supported."));
235
236 /*
237 * Allocate return buffer.
238 */
239 try
240 {
241 a_rData.resize(a_cbRead);
242 }
243 catch (std::bad_alloc)
244 {
245 return E_OUTOFMEMORY;
246 }
247
248 /*
249 * Do the reading. To play safe we exclusivly lock the object while doing this.
250 */
251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
252 size_t cbActual = 0;
253 int vrc = RTVfsFileReadAt(m->hVfsFile, a_off, &a_rData.front(), a_cbRead, &cbActual);
254 alock.release();
255
256 /*
257 * Manage the result.
258 */
259 HRESULT hrc;
260 if (RT_SUCCESS(vrc))
261 {
262 if (cbActual != a_cbRead)
263 {
264 Assert(cbActual < a_cbRead);
265 a_rData.resize(cbActual);
266 }
267 hrc = S_OK;
268 }
269 else
270 {
271 a_rData.resize(0);
272 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error reading %u bytes at %RU64: %Rrc"), a_cbRead, a_off, vrc);
273 }
274
275 return hrc;
276}
277
278HRESULT MediumIO::write(LONG64 a_off, const std::vector<BYTE> &a_rData, ULONG *a_pcbWritten)
279{
280 /*
281 * Validate input.
282 */
283 size_t cbToWrite = a_rData.size();
284 if (cbToWrite == 0)
285 return setError(E_INVALIDARG, tr("Zero byte write is not supported."));
286 if (!m->fWritable)
287 return setError(E_ACCESSDENIED, tr("Medium not opened for writing."));
288 CheckComArgPointerValid(a_pcbWritten);
289 *a_pcbWritten = 0;
290
291 /*
292 * Do the writing. To play safe we exclusivly lock the object while doing this.
293 */
294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
295 size_t cbActual = 0;
296 int vrc = RTVfsFileWriteAt(m->hVfsFile, a_off, &a_rData.front(), cbToWrite, &cbActual);
297 alock.release();
298
299 /*
300 * Manage the result.
301 */
302 HRESULT hrc;
303 if (RT_SUCCESS(vrc))
304 {
305 *a_pcbWritten = (ULONG)cbActual;
306 hrc = S_OK;
307 }
308 else
309 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error writing %zu bytes at %RU64: %Rrc"), cbToWrite, a_off, vrc);
310
311 return hrc;
312}
313
314HRESULT MediumIO::formatFAT(BOOL a_fQuick)
315{
316 /*
317 * Validate input.
318 */
319 if (!m->fWritable)
320 return setError(E_ACCESSDENIED, tr("Medium not opened for writing."));
321
322 /*
323 * Format the medium as FAT and let the format API figure the parameters.
324 * We exclusivly lock the object while doing this as concurrent medium access makes no sense.
325 */
326 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
327 RTERRINFOSTATIC ErrInfo;
328 int vrc = RTFsFatVolFormat(m->hVfsFile, 0, 0, a_fQuick ? RTFSFATVOL_FMT_F_QUICK : RTFSFATVOL_FMT_F_FULL, m->cbSector, 0,
329 RTFSFATTYPE_INVALID, 0, 0, 0, 0, 0, RTErrInfoInitStatic(&ErrInfo));
330 alock.release();
331
332 /*
333 * Manage the result.
334 */
335 HRESULT hrc;
336 if (RT_SUCCESS(vrc))
337 hrc = S_OK;
338 else if (RTErrInfoIsSet(&ErrInfo.Core))
339 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error formatting (%Rrc): %s"), vrc, ErrInfo.Core.pszMsg);
340 else
341 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error formatting: %Rrc"), vrc);
342
343 return hrc;
344}
345
346HRESULT MediumIO::initializePartitionTable(PartitionTableType_T a_enmFormat, BOOL a_fWholeDiskInOneEntry)
347{
348 /*
349 * Validate input.
350 */
351 const char *pszFormat;
352 if (a_enmFormat == PartitionTableType_MBR)
353 pszFormat = "MBR"; /* RTDVMFORMATTYPE_MBR */
354 else if (a_enmFormat == PartitionTableType_GPT)
355 pszFormat = "GPT"; /* RTDVMFORMATTYPE_GPT */
356 else
357 return setError(E_INVALIDARG, tr("Invalid partition format type: %d"), a_enmFormat);
358 if (!m->fWritable)
359 return setError(E_ACCESSDENIED, tr("Medium not opened for writing."));
360 if (a_fWholeDiskInOneEntry)
361 return setError(E_NOTIMPL, tr("whole-disk-in-one-entry is not implemented yet, sorry."));
362
363 /*
364 * Do the partitioning.
365 * We exclusivly lock the object while doing this as concurrent medium access makes little sense.
366 */
367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
368
369 RTDVM hVolMgr;
370 int vrc = RTDvmCreate(&hVolMgr, m->hVfsFile, m->cbSector, 0 /*fFlags*/);
371 HRESULT hrc;
372 if (RT_SUCCESS(vrc))
373 {
374 vrc = RTDvmMapInitialize(hVolMgr, pszFormat); /** @todo Why doesn't RTDvmMapInitialize take RTDVMFORMATTYPE? */
375 if (RT_SUCCESS(vrc))
376 {
377 /*
378 * Create a partition for the whole disk?
379 */
380 hrc = S_OK; /** @todo a_fWholeDiskInOneEntry requies RTDvm to get a function for creating partitions. */
381 }
382 else
383 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("RTDvmMapInitialize failed: %Rrc"), vrc);
384 RTDvmRelease(hVolMgr);
385 }
386 else
387 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("RTDvmCreate failed: %Rrc"), vrc);
388
389 return hrc;
390}
391
392HRESULT MediumIO::close()
393{
394 /*
395 * We need a write lock here to exclude all other access.
396 */
397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
398 i_close();
399 return S_OK;
400}
401
402
403
404/*********************************************************************************************************************************
405* IMediumIO internal methods *
406*********************************************************************************************************************************/
407
408/**
409 * This is used by both uninit and close().
410 *
411 * Expects exclusive access (write lock or autouninit) to the object.
412 */
413void MediumIO::i_close()
414{
415 if (m->hVfsFile != NIL_RTVFSFILE)
416 {
417 uint32_t cRefs = RTVfsFileRelease(m->hVfsFile);
418 Assert(cRefs == 0);
419 NOREF(cRefs);
420
421 m->hVfsFile = NIL_RTVFSFILE;
422 }
423
424 if (m->pHdd)
425 {
426 VDDestroy(m->pHdd);
427 m->pHdd = NULL;
428 }
429
430 m->LockList.Clear();
431 m->ptrMedium.setNull();
432 m->SecretKeyStore.deleteAllSecretKeys(false /* fSuspend */, true /* fForce */);
433}
434
Note: See TracBrowser for help on using the repository browser.

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