VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvVD.cpp@ 10850

Last change on this file since 10850 was 10850, checked in by vboxsync, 16 years ago

Fix invalid pointer to pDrvIns in DrvVD

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.3 KB
Line 
1/** $Id: DrvVD.cpp 10850 2008-07-24 09:28:44Z vboxsync $ */
2/** @file
3 *
4 * VBox storage devices:
5 * Media implementation for VBox disk container
6 */
7
8/*
9 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24
25/*******************************************************************************
26* Header files *
27*******************************************************************************/
28#define LOG_GROUP LOG_GROUP_DRV_VD
29#include <VBox/VBoxHDD-new.h>
30#include <VBox/pdmdrv.h>
31#include <iprt/alloc.h>
32#include <iprt/assert.h>
33#include <iprt/uuid.h>
34#include <iprt/file.h>
35#include <iprt/string.h>
36#include <iprt/cache.h>
37
38#include "Builtins.h"
39
40
41/*******************************************************************************
42* Defined types, constants and macros *
43*******************************************************************************/
44
45/** Converts a pointer to VDIDISK::IMedia to a PVBOXDISK. */
46#define PDMIMEDIA_2_VBOXDISK(pInterface) \
47 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMedia)) )
48
49/** Converts a pointer to PDMDRVINS::IBase to a PPDMDRVINS. */
50#define PDMIBASE_2_DRVINS(pInterface) \
51 ( (PPDMDRVINS)((uintptr_t)pInterface - RT_OFFSETOF(PDMDRVINS, IBase)) )
52
53/** Converts a pointer to PDMDRVINS::IBase to a PVBOXDISK. */
54#define PDMIBASE_2_VBOXDISK(pInterface) \
55 ( PDMINS2DATA(PDMIBASE_2_DRVINS(pInterface), PVBOXDISK) )
56
57/** Converts a pointer to VBOXDISK::IMediaAsync to a PVBOXDISK. */
58#define PDMIMEDIAASYNC_2_VBOXDISK(pInterface) \
59 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMediaAsync)) )
60
61/** Converts a pointer to VBOXDISK::ITransportAsyncPort to a PVBOXDISK. */
62#define PDMITRANSPORTASYNCPORT_2_VBOXDISK(pInterface) \
63 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, ITransportAsyncPort)) )
64
65/**
66 * Structure for an async I/O task.
67 */
68typedef struct DRVVDASYNCTASK
69{
70 /** Callback which is called on completion. */
71 PFNVDCOMPLETED pfnCompleted;
72 /** Opqaue user data which is passed on completion. */
73 void *pvUser;
74 /** Opaque user data the caller passed on transfer initiation. */
75 void *pvUserCaller;
76} DRVVDASYNCTASK, *PDRVVDASYNCTASK;
77
78/**
79 * VBox disk container media main structure, private part.
80 */
81typedef struct VBOXDISK
82{
83 /** The VBox disk container. */
84 PVBOXHDD pDisk;
85 /** The media interface. */
86 PDMIMEDIA IMedia;
87 /** Pointer to the driver instance. */
88 PPDMDRVINS pDrvIns;
89 /** Flag whether suspend has changed image open mode to read only. */
90 bool fTempReadOnly;
91 /** Common structure for the supported error interface. */
92 VDINTERFACE VDIError;
93 /** Callback table for error interface. */
94 VDINTERFACEERROR VDIErrorCallbacks;
95 /** Common structure for the supported async I/O interface. */
96 VDINTERFACE VDIAsyncIO;
97 /** Callback table for async I/O interface. */
98 VDINTERFACEASYNCIO VDIAsyncIOCallbacks;
99 /** Flag whether opened disk suppports async I/O operations. */
100 bool fAsyncIOSupported;
101 /** The async media interface. */
102 PDMIMEDIAASYNC IMediaAsync;
103 /** The async media port interface above. */
104 PPDMIMEDIAASYNCPORT pDrvMediaAsyncPort;
105 /** Pointer to the asynchronous media driver below. */
106 PPDMITRANSPORTASYNC pDrvTransportAsync;
107 /** Async transport port interface. */
108 PDMITRANSPORTASYNCPORT ITransportAsyncPort;
109 /** Our cache to reduce allocation overhead. */
110 PRTOBJCACHE pCache;
111} VBOXDISK, *PVBOXDISK;
112
113/*******************************************************************************
114* Error reporting callback *
115*******************************************************************************/
116
117static void drvvdErrorCallback(void *pvUser, int rc, RT_SRC_POS_DECL,
118 const char *pszFormat, va_list va)
119{
120 PPDMDRVINS pDrvIns = (PPDMDRVINS)pvUser;
121 pDrvIns->pDrvHlp->pfnVMSetErrorV(pDrvIns, rc, RT_SRC_POS_ARGS, pszFormat, va);
122}
123
124/*******************************************************************************
125* VD Async I/O interface implementation *
126*******************************************************************************/
127
128static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation, bool fReadonly, void **ppStorage)
129{
130 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
131
132 return pDrvVD->pDrvTransportAsync->pfnOpen(pDrvVD->pDrvTransportAsync, pszLocation, fReadonly, ppStorage);
133}
134
135static DECLCALLBACK(int) drvvdAsyncIOClose(void *pvUser, void *pStorage)
136{
137 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
138
139 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
140
141 return pDrvVD->pDrvTransportAsync->pfnClose(pDrvVD->pDrvTransportAsync, pStorage);
142}
143
144static DECLCALLBACK(int) drvvdAsyncIORead(void *pvUser, void *pStorage, uint64_t uOffset,
145 size_t cbRead, void *pvBuf, size_t *pcbRead)
146{
147 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
148
149 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
150
151 return pDrvVD->pDrvTransportAsync->pfnReadSynchronous(pDrvVD->pDrvTransportAsync,
152 pStorage,
153 uOffset, pvBuf, cbRead, pcbRead);
154}
155
156static DECLCALLBACK(int) drvvdAsyncIOWrite(void *pvUser, void *pStorage, uint64_t uOffset,
157 size_t cbWrite, const void *pvBuf, size_t *pcbWritten)
158{
159 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
160
161 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
162
163 return pDrvVD->pDrvTransportAsync->pfnWriteSynchronous(pDrvVD->pDrvTransportAsync,
164 pStorage,
165 uOffset, pvBuf, cbWrite, pcbWritten);
166}
167
168static DECLCALLBACK(int) drvvdAsyncIOFlush(void *pvUser, void *pStorage)
169{
170 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
171
172 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
173
174 return pDrvVD->pDrvTransportAsync->pfnFlushSynchronous(pDrvVD->pDrvTransportAsync, pStorage);
175}
176
177static DECLCALLBACK(int) drvvdAsyncIOPrepareRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuf, size_t cbRead, void **ppTask)
178{
179 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
180
181 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
182
183 return pDrvVD->pDrvTransportAsync->pfnPrepareRead(pDrvVD->pDrvTransportAsync, pStorage, uOffset, pvBuf, cbRead, ppTask);
184}
185
186static DECLCALLBACK(int) drvvdAsyncIOPrepareWrite(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuf, size_t cbWrite, void **ppTask)
187{
188 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
189
190 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
191
192 return pDrvVD->pDrvTransportAsync->pfnPrepareWrite(pDrvVD->pDrvTransportAsync, pStorage, uOffset, pvBuf, cbWrite, ppTask);
193}
194
195static DECLCALLBACK(int) drvvdAsyncIOTasksSubmit(void *pvUser, void *apTasks[], unsigned cTasks, void *pvUser2,
196 void *pvUserCaller, PFNVDCOMPLETED pfnTasksCompleted)
197{
198 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
199 PDRVVDASYNCTASK pDrvVDAsyncTask;
200 int rc;
201
202 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
203
204 rc = RTCacheRequest(pDrvVD->pCache, (void **)&pDrvVDAsyncTask);
205
206 if (RT_FAILURE(rc))
207 return rc;
208
209 pDrvVDAsyncTask->pfnCompleted = pfnTasksCompleted;
210 pDrvVDAsyncTask->pvUser = pvUser2;
211 pDrvVDAsyncTask->pvUserCaller = pvUserCaller;
212
213 return pDrvVD->pDrvTransportAsync->pfnTasksSubmit(pDrvVD->pDrvTransportAsync, apTasks, cTasks, pDrvVDAsyncTask);
214}
215
216/*******************************************************************************
217* Media interface methods *
218*******************************************************************************/
219
220/** @copydoc PDMIMEDIA::pfnRead */
221static DECLCALLBACK(int) drvvdRead(PPDMIMEDIA pInterface,
222 uint64_t off, void *pvBuf, size_t cbRead)
223{
224 LogFlow(("%s: off=%#llx pvBuf=%p cbRead=%d\n", __FUNCTION__,
225 off, pvBuf, cbRead));
226 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
227 int rc = VDRead(pData->pDisk, off, pvBuf, cbRead);
228 if (VBOX_SUCCESS(rc))
229 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d %.*Vhxd\n", __FUNCTION__,
230 off, pvBuf, cbRead, cbRead, pvBuf));
231 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
232 return rc;
233}
234
235/** @copydoc PDMIMEDIA::pfnWrite */
236static DECLCALLBACK(int) drvvdWrite(PPDMIMEDIA pInterface,
237 uint64_t off, const void *pvBuf,
238 size_t cbWrite)
239{
240 LogFlow(("%s: off=%#llx pvBuf=%p cbWrite=%d\n", __FUNCTION__,
241 off, pvBuf, cbWrite));
242 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
243 Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d %.*Vhxd\n", __FUNCTION__,
244 off, pvBuf, cbWrite, cbWrite, pvBuf));
245 int rc = VDWrite(pData->pDisk, off, pvBuf, cbWrite);
246 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
247 return rc;
248}
249
250/** @copydoc PDMIMEDIA::pfnFlush */
251static DECLCALLBACK(int) drvvdFlush(PPDMIMEDIA pInterface)
252{
253 LogFlow(("%s:\n", __FUNCTION__));
254 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
255 int rc = VDFlush(pData->pDisk);
256 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
257 return rc;
258}
259
260/** @copydoc PDMIMEDIA::pfnGetSize */
261static DECLCALLBACK(uint64_t) drvvdGetSize(PPDMIMEDIA pInterface)
262{
263 LogFlow(("%s:\n", __FUNCTION__));
264 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
265 uint64_t cb = VDGetSize(pData->pDisk, VD_LAST_IMAGE);
266 LogFlow(("%s: returns %#llx (%llu)\n", __FUNCTION__, cb, cb));
267 return cb;
268}
269
270/** @copydoc PDMIMEDIA::pfnIsReadOnly */
271static DECLCALLBACK(bool) drvvdIsReadOnly(PPDMIMEDIA pInterface)
272{
273 LogFlow(("%s:\n", __FUNCTION__));
274 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
275 bool f = VDIsReadOnly(pData->pDisk);
276 LogFlow(("%s: returns %d\n", __FUNCTION__, f));
277 return f;
278}
279
280/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
281static DECLCALLBACK(int) drvvdBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
282 PPDMMEDIAGEOMETRY pPCHSGeometry)
283{
284 LogFlow(("%s:\n", __FUNCTION__));
285 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
286 int rc = VDGetPCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
287 if (VBOX_FAILURE(rc))
288 {
289 Log(("%s: geometry not available.\n", __FUNCTION__));
290 rc = VERR_PDM_GEOMETRY_NOT_SET;
291 }
292 LogFlow(("%s: returns %Vrc (CHS=%d/%d/%d)\n", __FUNCTION__,
293 rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
294 return rc;
295}
296
297/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
298static DECLCALLBACK(int) drvvdBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
299 PCPDMMEDIAGEOMETRY pPCHSGeometry)
300{
301 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
302 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
303 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
304 int rc = VDSetPCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
305 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
306 return rc;
307}
308
309/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
310static DECLCALLBACK(int) drvvdBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
311 PPDMMEDIAGEOMETRY pLCHSGeometry)
312{
313 LogFlow(("%s:\n", __FUNCTION__));
314 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
315 int rc = VDGetLCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
316 if (VBOX_FAILURE(rc))
317 {
318 Log(("%s: geometry not available.\n", __FUNCTION__));
319 rc = VERR_PDM_GEOMETRY_NOT_SET;
320 }
321 LogFlow(("%s: returns %Vrc (CHS=%d/%d/%d)\n", __FUNCTION__,
322 rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
323 return rc;
324}
325
326/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
327static DECLCALLBACK(int) drvvdBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
328 PCPDMMEDIAGEOMETRY pLCHSGeometry)
329{
330 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
331 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
332 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
333 int rc = VDSetLCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
334 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
335 return rc;
336}
337
338/** @copydoc PDMIMEDIA::pfnGetUuid */
339static DECLCALLBACK(int) drvvdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
340{
341 LogFlow(("%s:\n", __FUNCTION__));
342 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
343 int rc = VDGetUuid(pData->pDisk, 0, pUuid);
344 LogFlow(("%s: returns %Vrc ({%Vuuid})\n", __FUNCTION__, rc, pUuid));
345 return rc;
346}
347
348/*******************************************************************************
349* Async Media interface methods *
350*******************************************************************************/
351
352static DECLCALLBACK(int) drvvdStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
353 PPDMDATASEG paSeg, unsigned cSeg,
354 size_t cbRead, void *pvUser)
355{
356 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d\n pvUser=%#p", __FUNCTION__,
357 uOffset, paSeg, cSeg, cbRead, pvUser));
358 PVBOXDISK pData = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
359 int rc = VDAsyncRead(pData->pDisk, uOffset, cbRead, paSeg, cSeg, pvUser);
360 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
361 return rc;
362}
363
364static DECLCALLBACK(int) drvvdStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
365 PPDMDATASEG paSeg, unsigned cSeg,
366 size_t cbWrite, void *pvUser)
367{
368 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d\n pvUser=%#p", __FUNCTION__,
369 uOffset, paSeg, cSeg, cbWrite, pvUser));
370 PVBOXDISK pData = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
371 int rc = VDAsyncWrite(pData->pDisk, uOffset, cbWrite, paSeg, cSeg, pvUser);
372 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
373 return rc;
374}
375
376/*******************************************************************************
377* Async transport port interface methods *
378*******************************************************************************/
379
380static DECLCALLBACK(int) drvvdTasksCompleteNotify(PPDMITRANSPORTASYNCPORT pInterface, void *pvUser)
381{
382 PVBOXDISK pData = PDMITRANSPORTASYNCPORT_2_VBOXDISK(pInterface);
383 PDRVVDASYNCTASK pDrvVDAsyncTask = (PDRVVDASYNCTASK)pvUser;
384 int rc = VINF_VDI_ASYNC_IO_FINISHED;
385
386 /* Having a completion callback for a task is not mandatory. */
387 if (pDrvVDAsyncTask->pfnCompleted)
388 rc = pDrvVDAsyncTask->pfnCompleted(pDrvVDAsyncTask->pvUser);
389
390 /* Check if the request is finished. */
391 if (rc == VINF_VDI_ASYNC_IO_FINISHED)
392 {
393 rc = pData->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pData->pDrvMediaAsyncPort, pDrvVDAsyncTask->pvUserCaller);
394 }
395 else if (rc == VERR_VDI_ASYNC_IO_IN_PROGRESS)
396 rc = VINF_SUCCESS;
397
398 rc = RTCacheInsert(pData->pCache, pDrvVDAsyncTask);
399 AssertRC(rc);
400
401 return rc;
402}
403
404
405/*******************************************************************************
406* Base interface methods *
407*******************************************************************************/
408
409/** @copydoc PDMIBASE::pfnQueryInterface */
410static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface,
411 PDMINTERFACE enmInterface)
412{
413 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
414 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
415 switch (enmInterface)
416 {
417 case PDMINTERFACE_BASE:
418 return &pDrvIns->IBase;
419 case PDMINTERFACE_MEDIA:
420 return &pData->IMedia;
421 case PDMINTERFACE_MEDIA_ASYNC:
422 return pData->fAsyncIOSupported ? &pData->IMediaAsync : NULL;
423 case PDMINTERFACE_TRANSPORT_ASYNC_PORT:
424 return &pData->ITransportAsyncPort;
425 default:
426 return NULL;
427 }
428}
429
430
431/*******************************************************************************
432* Driver methods *
433*******************************************************************************/
434
435
436/**
437 * Construct a VBox disk media driver instance.
438 *
439 * @returns VBox status.
440 * @param pDrvIns The driver instance data.
441 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
442 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
443 * of the driver instance. It's also found in pDrvIns->pCfgHandle as it's expected
444 * to be used frequently in this function.
445 */
446static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
447 PCFGMNODE pCfgHandle)
448{
449 LogFlow(("%s:\n", __FUNCTION__));
450 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
451 int rc = VINF_SUCCESS;
452 char *pszName = NULL; /**< The path of the disk image file. */
453 char *pszFormat = NULL; /**< The format backed to use for this image. */
454 bool fReadOnly; /**< True if the media is readonly. */
455 bool fHonorZeroWrites; /**< True if zero blocks should be written. */
456
457 /*
458 * Init the static parts.
459 */
460 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
461 pData->pDrvIns = pDrvIns;
462 pData->fTempReadOnly = false;
463 pData->pDisk = NULL;
464
465 /* IMedia */
466 pData->IMedia.pfnRead = drvvdRead;
467 pData->IMedia.pfnWrite = drvvdWrite;
468 pData->IMedia.pfnFlush = drvvdFlush;
469 pData->IMedia.pfnGetSize = drvvdGetSize;
470 pData->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
471 pData->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
472 pData->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
473 pData->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
474 pData->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
475 pData->IMedia.pfnGetUuid = drvvdGetUuid;
476
477 /* IMediaAsync */
478 pData->IMediaAsync.pfnStartRead = drvvdStartRead;
479 pData->IMediaAsync.pfnStartWrite = drvvdStartWrite;
480
481 /* ITransportAsyncPort */
482 pData->ITransportAsyncPort.pfnTaskCompleteNotify = drvvdTasksCompleteNotify;
483
484 /* Initialize supported VD interfaces. */
485 pData->VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
486 pData->VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
487 pData->VDIErrorCallbacks.pfnError = drvvdErrorCallback;
488
489 rc = VDInterfaceCreate(&pData->VDIError, "DrvVD_VDIError", VDINTERFACETYPE_ERROR,
490 &pData->VDIErrorCallbacks, pDrvIns, NULL);
491 AssertRC(rc);
492
493 pData->VDIAsyncIOCallbacks.cbSize = sizeof(VDINTERFACEASYNCIO);
494 pData->VDIAsyncIOCallbacks.enmInterface = VDINTERFACETYPE_ASYNCIO;
495 pData->VDIAsyncIOCallbacks.pfnOpen = drvvdAsyncIOOpen;
496 pData->VDIAsyncIOCallbacks.pfnClose = drvvdAsyncIOClose;
497 pData->VDIAsyncIOCallbacks.pfnRead = drvvdAsyncIORead;
498 pData->VDIAsyncIOCallbacks.pfnWrite = drvvdAsyncIOWrite;
499 pData->VDIAsyncIOCallbacks.pfnFlush = drvvdAsyncIOFlush;
500 pData->VDIAsyncIOCallbacks.pfnPrepareRead = drvvdAsyncIOPrepareRead;
501 pData->VDIAsyncIOCallbacks.pfnPrepareWrite = drvvdAsyncIOPrepareWrite;
502 pData->VDIAsyncIOCallbacks.pfnTasksSubmit = drvvdAsyncIOTasksSubmit;
503
504 rc = VDInterfaceCreate(&pData->VDIAsyncIO, "DrvVD_AsyncIO", VDINTERFACETYPE_ASYNCIO,
505 &pData->VDIAsyncIOCallbacks, pData, &pData->VDIError);
506 AssertRC(rc);
507
508 /* Try to attach async media port interface above.*/
509 pData->pDrvMediaAsyncPort = (PPDMIMEDIAASYNCPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_MEDIA_ASYNC_PORT);
510
511 /*
512 * Attach the async transport driver below of the device above us implements the
513 * async interface.
514 */
515 if (pData->pDrvMediaAsyncPort)
516 {
517 /* Try to attach the driver. */
518 PPDMIBASE pBase;
519
520 rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
521 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
522 {
523 /*
524 * Though the device supports async I/O the backend seems to not support it.
525 * Revert to non async I/O.
526 */
527 pData->pDrvMediaAsyncPort = NULL;
528 }
529 else if (VBOX_FAILURE(rc))
530 {
531 AssertMsgFailed(("Failed to attach async transport driver below rc=%Vrc\n", rc));
532 }
533 else
534 {
535 /* Success query the async transport interface. */
536 pData->pDrvTransportAsync = (PPDMITRANSPORTASYNC)pBase->pfnQueryInterface(pBase, PDMINTERFACE_TRANSPORT_ASYNC);
537 if (!pData->pDrvTransportAsync)
538 {
539 /* Whoops. */
540 AssertMsgFailed(("Configuration error: No async transport interface below!\n"));
541 return VERR_PDM_MISSING_INTERFACE_ABOVE;
542 }
543 }
544 }
545
546 /*
547 * Validate configuration and find all parent images.
548 * It's sort of up side down from the image dependency tree.
549 */
550 unsigned iLevel = 0;
551 PCFGMNODE pCurNode = pCfgHandle;
552 for (;;)
553 {
554 bool fValid;
555
556 if (pCurNode == pCfgHandle)
557 {
558 /* Toplevel configuration additionally contains the global image
559 * open flags. Some might be converted to per-image flags later. */
560 fValid = CFGMR3AreValuesValid(pCurNode,
561 "Format\0Path\0"
562 "ReadOnly\0HonorZeroWrites\0");
563 }
564 else
565 {
566 /* All other image configurations only contain image name and
567 * the format information. */
568 fValid = CFGMR3AreValuesValid(pCurNode, "Format\0Path\0");
569 }
570 if (!fValid)
571 {
572 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
573 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
574 break;
575 }
576
577 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
578 if (!pParent)
579 break;
580 pCurNode = pParent;
581 iLevel++;
582 }
583
584 /*
585 * Open the images.
586 */
587 if (VBOX_SUCCESS(rc))
588 {
589 rc = VDCreate(&pData->VDIAsyncIO, &pData->pDisk);
590 /* Error message is already set correctly. */
591 }
592
593 while (pCurNode && VBOX_SUCCESS(rc))
594 {
595 /*
596 * Read the image configuration.
597 */
598 rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
599 if (VBOX_FAILURE(rc))
600 {
601 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
602 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
603 break;
604 }
605
606 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Format", &pszFormat);
607 if (VBOX_FAILURE(rc))
608 {
609 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
610 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
611 break;
612 }
613
614 if (iLevel == 0)
615 {
616 rc = CFGMR3QueryBool(pCurNode, "ReadOnly", &fReadOnly);
617 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
618 fReadOnly = false;
619 else if (VBOX_FAILURE(rc))
620 {
621 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
622 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
623 break;
624 }
625
626 rc = CFGMR3QueryBool(pCfgHandle, "HonorZeroWrites", &fHonorZeroWrites);
627 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
628 fHonorZeroWrites = false;
629 else if (VBOX_FAILURE(rc))
630 {
631 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
632 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
633 break;
634 }
635 }
636 else
637 {
638 fReadOnly = true;
639 fHonorZeroWrites = false;
640 }
641
642 /*
643 * Open the image.
644 */
645 unsigned uOpenFlags;
646 if (fReadOnly)
647 uOpenFlags = VD_OPEN_FLAGS_READONLY;
648 else
649 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
650 if (fHonorZeroWrites)
651 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
652 if (pData->pDrvMediaAsyncPort)
653 uOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
654
655 /** Try to open backend in asyc I/O mode first. */
656 rc = VDOpen(pData->pDisk, pszFormat, pszName, uOpenFlags);
657 if (rc == VERR_NOT_SUPPORTED)
658 {
659 /* Seems asxnc I/O is not supported by the backend, open in normal mode. */
660 uOpenFlags &= ~VD_OPEN_FLAGS_ASYNC_IO;
661 rc = VDOpen(pData->pDisk, pszFormat, pszName, uOpenFlags);
662 }
663
664 if (VBOX_SUCCESS(rc))
665 Log(("%s: %d - Opened '%s' in %s mode\n", __FUNCTION__,
666 iLevel, pszName,
667 VDIsReadOnly(pData->pDisk) ? "read-only" : "read-write"));
668 else
669 {
670 AssertMsgFailed(("Failed to open image '%s' rc=%Vrc\n", pszName, rc));
671 break;
672 }
673
674
675 MMR3HeapFree(pszName);
676 pszName = NULL;
677 MMR3HeapFree(pszFormat);
678 pszFormat = NULL;
679
680 /* next */
681 iLevel--;
682 pCurNode = CFGMR3GetParent(pCurNode);
683 }
684
685 if (VBOX_FAILURE(rc))
686 {
687 if (VALID_PTR(pData->pDisk))
688 {
689 VDDestroy(pData->pDisk);
690 pData->pDisk = NULL;
691 }
692 if (VALID_PTR(pszName))
693 MMR3HeapFree(pszName);
694 if (VALID_PTR(pszFormat))
695 MMR3HeapFree(pszFormat);
696 }
697
698 /*
699 * Check for async I/O support. Every opened image has to support
700 * it.
701 */
702 pData->fAsyncIOSupported = true;
703 for (unsigned i = 0; i < VDGetCount(pData->pDisk); i++)
704 {
705 VDBACKENDINFO vdBackendInfo;
706
707 rc = VDBackendInfoSingle(pData->pDisk, i, &vdBackendInfo);
708 AssertRC(rc);
709
710 if (vdBackendInfo.uBackendCaps & VD_CAP_ASYNC)
711 {
712 /*
713 * Backend indicates support for at least some files.
714 * Check if current file is supported with async I/O)
715 */
716 rc = VDImageIsAsyncIOSupported(pData->pDisk, i, &pData->fAsyncIOSupported);
717 AssertRC(rc);
718
719 /*
720 * Check if current image is supported.
721 * If not we can stop checking because
722 * at least one does not support it.
723 */
724 if (!pData->fAsyncIOSupported)
725 break;
726 }
727 else
728 {
729 pData->fAsyncIOSupported = false;
730 break;
731 }
732 }
733
734 /* Create cache if async I/O is supported. */
735 if (pData->fAsyncIOSupported)
736 {
737 rc = RTCacheCreate(&pData->pCache, 0, sizeof(DRVVDASYNCTASK), RTOBJCACHE_PROTECT_INSERT);
738 AssertMsg(RT_SUCCESS(rc), ("Failed to create cache rc=%Vrc\n", rc));
739 }
740
741 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
742 return rc;
743}
744
745/**
746 * Destruct a driver instance.
747 *
748 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
749 * resources can be freed correctly.
750 *
751 * @param pDrvIns The driver instance data.
752 */
753static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
754{
755 int rc;
756 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
757 LogFlow(("%s:\n", __FUNCTION__));
758
759 if (pData->pCache)
760 {
761 rc = RTCacheDestroy(pData->pCache);
762 AssertRC(rc);
763 }
764}
765
766
767/**
768 * When the VM has been suspended we'll change the image mode to read-only
769 * so that main and others can read the VDIs. This is important when
770 * saving state and so forth.
771 *
772 * @param pDrvIns The driver instance data.
773 */
774static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
775{
776 LogFlow(("%s:\n", __FUNCTION__));
777 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
778 if (!VDIsReadOnly(pData->pDisk))
779 {
780 unsigned uOpenFlags;
781 int rc = VDGetOpenFlags(pData->pDisk, VD_LAST_IMAGE, &uOpenFlags);
782 AssertRC(rc);
783 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
784 rc = VDSetOpenFlags(pData->pDisk, VD_LAST_IMAGE, uOpenFlags);
785 AssertRC(rc);
786 pData->fTempReadOnly = true;
787 }
788}
789
790/**
791 * Before the VM resumes we'll have to undo the read-only mode change
792 * done in drvvdSuspend.
793 *
794 * @param pDrvIns The driver instance data.
795 */
796static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
797{
798 LogFlow(("%s:\n", __FUNCTION__));
799 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
800 if (pData->fTempReadOnly)
801 {
802 unsigned uOpenFlags;
803 int rc = VDGetOpenFlags(pData->pDisk, VD_LAST_IMAGE, &uOpenFlags);
804 AssertRC(rc);
805 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
806 rc = VDSetOpenFlags(pData->pDisk, VD_LAST_IMAGE, uOpenFlags);
807 AssertRC(rc);
808 pData->fTempReadOnly = false;
809 }
810}
811
812static DECLCALLBACK(void) drvvdPowerOff(PPDMDRVINS pDrvIns)
813{
814 LogFlow(("%s:\n", __FUNCTION__));
815 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
816
817 /*
818 * We must close the disk here to ensure that
819 * the backend closes all files before the
820 * async transport driver is destructed.
821 */
822 int rc = VDCloseAll(pData->pDisk);
823 AssertRC(rc);
824}
825
826/**
827 * VBox disk container media driver registration record.
828 */
829const PDMDRVREG g_DrvVD =
830{
831 /* u32Version */
832 PDM_DRVREG_VERSION,
833 /* szDriverName */
834 "VD",
835 /* pszDescription */
836 "Generic VBox disk media driver.",
837 /* fFlags */
838 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
839 /* fClass. */
840 PDM_DRVREG_CLASS_MEDIA,
841 /* cMaxInstances */
842 ~0,
843 /* cbInstance */
844 sizeof(VBOXDISK),
845 /* pfnConstruct */
846 drvvdConstruct,
847 /* pfnDestruct */
848 drvvdDestruct,
849 /* pfnIOCtl */
850 NULL,
851 /* pfnPowerOn */
852 NULL,
853 /* pfnReset */
854 NULL,
855 /* pfnSuspend */
856 drvvdSuspend,
857 /* pfnResume */
858 drvvdResume,
859 /* pfnDetach */
860 NULL,
861 /* pfnPowerOff */
862 drvvdPowerOff
863};
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