VirtualBox

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

Last change on this file since 73768 was 73505, checked in by vboxsync, 6 years ago

Main,Config.kmk: GCC 8.2.0 fixes

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

© 2023 Oracle
ContactPrivacy policyTerms of Use