VirtualBox

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

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

Storage: Temporary integration of configuration interface.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.9 KB
Line 
1/** $Id: DrvVD.cpp 11047 2008-07-31 20:10: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 /** Common structure for the configuration information interface. */
100 VDINTERFACE VDIConfig;
101 /** Callback table for the configuration information interface. */
102 VDINTERFACECONFIG VDIConfigCallbacks;
103 /** Flag whether opened disk suppports async I/O operations. */
104 bool fAsyncIOSupported;
105 /** The async media interface. */
106 PDMIMEDIAASYNC IMediaAsync;
107 /** The async media port interface above. */
108 PPDMIMEDIAASYNCPORT pDrvMediaAsyncPort;
109 /** Pointer to the asynchronous media driver below. */
110 PPDMITRANSPORTASYNC pDrvTransportAsync;
111 /** Async transport port interface. */
112 PDMITRANSPORTASYNCPORT ITransportAsyncPort;
113 /** Our cache to reduce allocation overhead. */
114 PRTOBJCACHE pCache;
115} VBOXDISK, *PVBOXDISK;
116
117/*******************************************************************************
118* Error reporting callback *
119*******************************************************************************/
120
121static void drvvdErrorCallback(void *pvUser, int rc, RT_SRC_POS_DECL,
122 const char *pszFormat, va_list va)
123{
124 PPDMDRVINS pDrvIns = (PPDMDRVINS)pvUser;
125 pDrvIns->pDrvHlp->pfnVMSetErrorV(pDrvIns, rc, RT_SRC_POS_ARGS, pszFormat, va);
126}
127
128/*******************************************************************************
129* VD Async I/O interface implementation *
130*******************************************************************************/
131
132static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation, bool fReadonly, void **ppStorage)
133{
134 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
135
136 return pDrvVD->pDrvTransportAsync->pfnOpen(pDrvVD->pDrvTransportAsync, pszLocation, fReadonly, ppStorage);
137}
138
139static DECLCALLBACK(int) drvvdAsyncIOClose(void *pvUser, void *pStorage)
140{
141 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
142
143 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
144
145 return pDrvVD->pDrvTransportAsync->pfnClose(pDrvVD->pDrvTransportAsync, pStorage);
146}
147
148static DECLCALLBACK(int) drvvdAsyncIORead(void *pvUser, void *pStorage, uint64_t uOffset,
149 size_t cbRead, void *pvBuf, size_t *pcbRead)
150{
151 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
152
153 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
154
155 return pDrvVD->pDrvTransportAsync->pfnReadSynchronous(pDrvVD->pDrvTransportAsync,
156 pStorage,
157 uOffset, pvBuf, cbRead, pcbRead);
158}
159
160static DECLCALLBACK(int) drvvdAsyncIOWrite(void *pvUser, void *pStorage, uint64_t uOffset,
161 size_t cbWrite, const void *pvBuf, size_t *pcbWritten)
162{
163 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
164
165 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
166
167 return pDrvVD->pDrvTransportAsync->pfnWriteSynchronous(pDrvVD->pDrvTransportAsync,
168 pStorage,
169 uOffset, pvBuf, cbWrite, pcbWritten);
170}
171
172static DECLCALLBACK(int) drvvdAsyncIOFlush(void *pvUser, void *pStorage)
173{
174 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
175
176 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
177
178 return pDrvVD->pDrvTransportAsync->pfnFlushSynchronous(pDrvVD->pDrvTransportAsync, pStorage);
179}
180
181static DECLCALLBACK(int) drvvdAsyncIOPrepareRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuf, size_t cbRead, void **ppTask)
182{
183 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
184
185 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
186
187 return pDrvVD->pDrvTransportAsync->pfnPrepareRead(pDrvVD->pDrvTransportAsync, pStorage, uOffset, pvBuf, cbRead, ppTask);
188}
189
190static DECLCALLBACK(int) drvvdAsyncIOPrepareWrite(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuf, size_t cbWrite, void **ppTask)
191{
192 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
193
194 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
195
196 return pDrvVD->pDrvTransportAsync->pfnPrepareWrite(pDrvVD->pDrvTransportAsync, pStorage, uOffset, pvBuf, cbWrite, ppTask);
197}
198
199static DECLCALLBACK(int) drvvdAsyncIOTasksSubmit(void *pvUser, void *apTasks[], unsigned cTasks, void *pvUser2,
200 void *pvUserCaller, PFNVDCOMPLETED pfnTasksCompleted)
201{
202 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
203 PDRVVDASYNCTASK pDrvVDAsyncTask;
204 int rc;
205
206 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
207
208 rc = RTCacheRequest(pDrvVD->pCache, (void **)&pDrvVDAsyncTask);
209
210 if (RT_FAILURE(rc))
211 return rc;
212
213 pDrvVDAsyncTask->pfnCompleted = pfnTasksCompleted;
214 pDrvVDAsyncTask->pvUser = pvUser2;
215 pDrvVDAsyncTask->pvUserCaller = pvUserCaller;
216
217 return pDrvVD->pDrvTransportAsync->pfnTasksSubmit(pDrvVD->pDrvTransportAsync, apTasks, cTasks, pDrvVDAsyncTask);
218}
219
220/*******************************************************************************
221* VD Configuration interface implementation *
222*******************************************************************************/
223
224static bool drvvdCfgAreValuesValid(PVDCFGNODE pNode, const char *pszzValid)
225{
226 return CFGMR3AreValuesValid((PCFGMNODE)pNode, pszzValid);
227}
228
229static int drvvdCfgQueryType(PVDCFGNODE pNode, const char *pszName, PVDCFGVALUETYPE penmType)
230{
231 Assert(VDCFGVALUETYPE_INTEGER == CFGMVALUETYPE_INTEGER);
232 Assert(VDCFGVALUETYPE_STRING == CFGMVALUETYPE_STRING);
233 Assert(VDCFGVALUETYPE_BYTES == CFGMVALUETYPE_BYTES);
234 return CFGMR3QueryType((PCFGMNODE)pNode, pszName, (PCFGMVALUETYPE)penmType);
235}
236
237static int drvvdCfgQuerySize(PVDCFGNODE pNode, const char *pszName, size_t *pcb)
238{
239 return CFGMR3QuerySize((PCFGMNODE)pNode, pszName, pcb);
240}
241
242static int drvvdCfgQueryInteger(PVDCFGNODE pNode, const char *pszName, uint64_t *pu64)
243{
244 return CFGMR3QueryInteger((PCFGMNODE)pNode, pszName, pu64);
245}
246
247static int drvvdCfgQueryIntegerDef(PVDCFGNODE pNode, const char *pszName, uint64_t *pu64, uint64_t u64Def)
248{
249 return CFGMR3QueryInteger((PCFGMNODE)pNode, pszName, pu64);
250}
251
252static int drvvdCfgQueryString(PVDCFGNODE pNode, const char *pszName, char *pszString, size_t cchString)
253{
254 return CFGMR3QueryString((PCFGMNODE)pNode, pszName, pszString, cchString);
255}
256
257static int drvvdCfgQueryStringDef(PVDCFGNODE pNode, const char *pszName, char *pszString, size_t cchString, const char *pszDef)
258{
259 return CFGMR3QueryStringDef((PCFGMNODE)pNode, pszName, pszString, cchString, pszDef);
260}
261
262static int drvvdCfgQueryBytes(PVDCFGNODE pNode, const char *pszName, void *pvData, size_t cbData)
263{
264 return CFGMR3QueryBytes((PCFGMNODE)pNode, pszName, pvData, cbData);
265}
266
267
268/*******************************************************************************
269* Media interface methods *
270*******************************************************************************/
271
272/** @copydoc PDMIMEDIA::pfnRead */
273static DECLCALLBACK(int) drvvdRead(PPDMIMEDIA pInterface,
274 uint64_t off, void *pvBuf, size_t cbRead)
275{
276 LogFlow(("%s: off=%#llx pvBuf=%p cbRead=%d\n", __FUNCTION__,
277 off, pvBuf, cbRead));
278 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
279 int rc = VDRead(pData->pDisk, off, pvBuf, cbRead);
280 if (VBOX_SUCCESS(rc))
281 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d %.*Vhxd\n", __FUNCTION__,
282 off, pvBuf, cbRead, cbRead, pvBuf));
283 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
284 return rc;
285}
286
287/** @copydoc PDMIMEDIA::pfnWrite */
288static DECLCALLBACK(int) drvvdWrite(PPDMIMEDIA pInterface,
289 uint64_t off, const void *pvBuf,
290 size_t cbWrite)
291{
292 LogFlow(("%s: off=%#llx pvBuf=%p cbWrite=%d\n", __FUNCTION__,
293 off, pvBuf, cbWrite));
294 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
295 Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d %.*Vhxd\n", __FUNCTION__,
296 off, pvBuf, cbWrite, cbWrite, pvBuf));
297 int rc = VDWrite(pData->pDisk, off, pvBuf, cbWrite);
298 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
299 return rc;
300}
301
302/** @copydoc PDMIMEDIA::pfnFlush */
303static DECLCALLBACK(int) drvvdFlush(PPDMIMEDIA pInterface)
304{
305 LogFlow(("%s:\n", __FUNCTION__));
306 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
307 int rc = VDFlush(pData->pDisk);
308 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
309 return rc;
310}
311
312/** @copydoc PDMIMEDIA::pfnGetSize */
313static DECLCALLBACK(uint64_t) drvvdGetSize(PPDMIMEDIA pInterface)
314{
315 LogFlow(("%s:\n", __FUNCTION__));
316 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
317 uint64_t cb = VDGetSize(pData->pDisk, VD_LAST_IMAGE);
318 LogFlow(("%s: returns %#llx (%llu)\n", __FUNCTION__, cb, cb));
319 return cb;
320}
321
322/** @copydoc PDMIMEDIA::pfnIsReadOnly */
323static DECLCALLBACK(bool) drvvdIsReadOnly(PPDMIMEDIA pInterface)
324{
325 LogFlow(("%s:\n", __FUNCTION__));
326 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
327 bool f = VDIsReadOnly(pData->pDisk);
328 LogFlow(("%s: returns %d\n", __FUNCTION__, f));
329 return f;
330}
331
332/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
333static DECLCALLBACK(int) drvvdBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
334 PPDMMEDIAGEOMETRY pPCHSGeometry)
335{
336 LogFlow(("%s:\n", __FUNCTION__));
337 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
338 int rc = VDGetPCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
339 if (VBOX_FAILURE(rc))
340 {
341 Log(("%s: geometry not available.\n", __FUNCTION__));
342 rc = VERR_PDM_GEOMETRY_NOT_SET;
343 }
344 LogFlow(("%s: returns %Vrc (CHS=%d/%d/%d)\n", __FUNCTION__,
345 rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
346 return rc;
347}
348
349/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
350static DECLCALLBACK(int) drvvdBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
351 PCPDMMEDIAGEOMETRY pPCHSGeometry)
352{
353 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
354 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
355 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
356 int rc = VDSetPCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
357 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
358 return rc;
359}
360
361/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
362static DECLCALLBACK(int) drvvdBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
363 PPDMMEDIAGEOMETRY pLCHSGeometry)
364{
365 LogFlow(("%s:\n", __FUNCTION__));
366 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
367 int rc = VDGetLCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
368 if (VBOX_FAILURE(rc))
369 {
370 Log(("%s: geometry not available.\n", __FUNCTION__));
371 rc = VERR_PDM_GEOMETRY_NOT_SET;
372 }
373 LogFlow(("%s: returns %Vrc (CHS=%d/%d/%d)\n", __FUNCTION__,
374 rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
375 return rc;
376}
377
378/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
379static DECLCALLBACK(int) drvvdBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
380 PCPDMMEDIAGEOMETRY pLCHSGeometry)
381{
382 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
383 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
384 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
385 int rc = VDSetLCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
386 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
387 return rc;
388}
389
390/** @copydoc PDMIMEDIA::pfnGetUuid */
391static DECLCALLBACK(int) drvvdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
392{
393 LogFlow(("%s:\n", __FUNCTION__));
394 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
395 int rc = VDGetUuid(pData->pDisk, 0, pUuid);
396 LogFlow(("%s: returns %Vrc ({%Vuuid})\n", __FUNCTION__, rc, pUuid));
397 return rc;
398}
399
400/*******************************************************************************
401* Async Media interface methods *
402*******************************************************************************/
403
404static DECLCALLBACK(int) drvvdStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
405 PPDMDATASEG paSeg, unsigned cSeg,
406 size_t cbRead, void *pvUser)
407{
408 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d\n pvUser=%#p", __FUNCTION__,
409 uOffset, paSeg, cSeg, cbRead, pvUser));
410 PVBOXDISK pData = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
411 int rc = VDAsyncRead(pData->pDisk, uOffset, cbRead, paSeg, cSeg, pvUser);
412 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
413 return rc;
414}
415
416static DECLCALLBACK(int) drvvdStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
417 PPDMDATASEG paSeg, unsigned cSeg,
418 size_t cbWrite, void *pvUser)
419{
420 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d\n pvUser=%#p", __FUNCTION__,
421 uOffset, paSeg, cSeg, cbWrite, pvUser));
422 PVBOXDISK pData = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
423 int rc = VDAsyncWrite(pData->pDisk, uOffset, cbWrite, paSeg, cSeg, pvUser);
424 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
425 return rc;
426}
427
428/*******************************************************************************
429* Async transport port interface methods *
430*******************************************************************************/
431
432static DECLCALLBACK(int) drvvdTasksCompleteNotify(PPDMITRANSPORTASYNCPORT pInterface, void *pvUser)
433{
434 PVBOXDISK pData = PDMITRANSPORTASYNCPORT_2_VBOXDISK(pInterface);
435 PDRVVDASYNCTASK pDrvVDAsyncTask = (PDRVVDASYNCTASK)pvUser;
436 int rc = VINF_VDI_ASYNC_IO_FINISHED;
437
438 /* Having a completion callback for a task is not mandatory. */
439 if (pDrvVDAsyncTask->pfnCompleted)
440 rc = pDrvVDAsyncTask->pfnCompleted(pDrvVDAsyncTask->pvUser);
441
442 /* Check if the request is finished. */
443 if (rc == VINF_VDI_ASYNC_IO_FINISHED)
444 {
445 rc = pData->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pData->pDrvMediaAsyncPort, pDrvVDAsyncTask->pvUserCaller);
446 }
447 else if (rc == VERR_VDI_ASYNC_IO_IN_PROGRESS)
448 rc = VINF_SUCCESS;
449
450 rc = RTCacheInsert(pData->pCache, pDrvVDAsyncTask);
451 AssertRC(rc);
452
453 return rc;
454}
455
456
457/*******************************************************************************
458* Base interface methods *
459*******************************************************************************/
460
461/** @copydoc PDMIBASE::pfnQueryInterface */
462static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface,
463 PDMINTERFACE enmInterface)
464{
465 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
466 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
467 switch (enmInterface)
468 {
469 case PDMINTERFACE_BASE:
470 return &pDrvIns->IBase;
471 case PDMINTERFACE_MEDIA:
472 return &pData->IMedia;
473 case PDMINTERFACE_MEDIA_ASYNC:
474 return pData->fAsyncIOSupported ? &pData->IMediaAsync : NULL;
475 case PDMINTERFACE_TRANSPORT_ASYNC_PORT:
476 return &pData->ITransportAsyncPort;
477 default:
478 return NULL;
479 }
480}
481
482
483/*******************************************************************************
484* Driver methods *
485*******************************************************************************/
486
487
488/**
489 * Construct a VBox disk media driver instance.
490 *
491 * @returns VBox status.
492 * @param pDrvIns The driver instance data.
493 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
494 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
495 * of the driver instance. It's also found in pDrvIns->pCfgHandle as it's expected
496 * to be used frequently in this function.
497 */
498static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
499 PCFGMNODE pCfgHandle)
500{
501 LogFlow(("%s:\n", __FUNCTION__));
502 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
503 int rc = VINF_SUCCESS;
504 char *pszName = NULL; /**< The path of the disk image file. */
505 char *pszFormat = NULL; /**< The format backed to use for this image. */
506 bool fReadOnly; /**< True if the media is readonly. */
507 bool fHonorZeroWrites; /**< True if zero blocks should be written. */
508
509 /*
510 * Init the static parts.
511 */
512 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
513 pData->pDrvIns = pDrvIns;
514 pData->fTempReadOnly = false;
515 pData->pDisk = NULL;
516
517 /* IMedia */
518 pData->IMedia.pfnRead = drvvdRead;
519 pData->IMedia.pfnWrite = drvvdWrite;
520 pData->IMedia.pfnFlush = drvvdFlush;
521 pData->IMedia.pfnGetSize = drvvdGetSize;
522 pData->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
523 pData->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
524 pData->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
525 pData->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
526 pData->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
527 pData->IMedia.pfnGetUuid = drvvdGetUuid;
528
529 /* IMediaAsync */
530 pData->IMediaAsync.pfnStartRead = drvvdStartRead;
531 pData->IMediaAsync.pfnStartWrite = drvvdStartWrite;
532
533 /* ITransportAsyncPort */
534 pData->ITransportAsyncPort.pfnTaskCompleteNotify = drvvdTasksCompleteNotify;
535
536 /* Initialize supported VD interfaces. */
537 pData->VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
538 pData->VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
539 pData->VDIErrorCallbacks.pfnError = drvvdErrorCallback;
540
541 rc = VDInterfaceCreate(&pData->VDIError, "DrvVD_VDIError", VDINTERFACETYPE_ERROR,
542 &pData->VDIErrorCallbacks, pDrvIns, NULL);
543 AssertRC(rc);
544
545 pData->VDIAsyncIOCallbacks.cbSize = sizeof(VDINTERFACEASYNCIO);
546 pData->VDIAsyncIOCallbacks.enmInterface = VDINTERFACETYPE_ASYNCIO;
547 pData->VDIAsyncIOCallbacks.pfnOpen = drvvdAsyncIOOpen;
548 pData->VDIAsyncIOCallbacks.pfnClose = drvvdAsyncIOClose;
549 pData->VDIAsyncIOCallbacks.pfnRead = drvvdAsyncIORead;
550 pData->VDIAsyncIOCallbacks.pfnWrite = drvvdAsyncIOWrite;
551 pData->VDIAsyncIOCallbacks.pfnFlush = drvvdAsyncIOFlush;
552 pData->VDIAsyncIOCallbacks.pfnPrepareRead = drvvdAsyncIOPrepareRead;
553 pData->VDIAsyncIOCallbacks.pfnPrepareWrite = drvvdAsyncIOPrepareWrite;
554 pData->VDIAsyncIOCallbacks.pfnTasksSubmit = drvvdAsyncIOTasksSubmit;
555
556 rc = VDInterfaceCreate(&pData->VDIAsyncIO, "DrvVD_AsyncIO", VDINTERFACETYPE_ASYNCIO,
557 &pData->VDIAsyncIOCallbacks, pData, &pData->VDIError);
558 AssertRC(rc);
559
560 pData->VDIConfigCallbacks.cbSize = sizeof(VDINTERFACECONFIG);
561 pData->VDIConfigCallbacks.enmInterface = VDINTERFACETYPE_CONFIG;
562 pData->VDIConfigCallbacks.pfnAreValuesValid = drvvdCfgAreValuesValid;
563 pData->VDIConfigCallbacks.pfnQueryType = drvvdCfgQueryType;
564 pData->VDIConfigCallbacks.pfnQuerySize = drvvdCfgQuerySize;
565 pData->VDIConfigCallbacks.pfnQueryInteger = drvvdCfgQueryInteger;
566 pData->VDIConfigCallbacks.pfnQueryIntegerDef = drvvdCfgQueryIntegerDef;
567 pData->VDIConfigCallbacks.pfnQueryString = drvvdCfgQueryString;
568 pData->VDIConfigCallbacks.pfnQueryStringDef = drvvdCfgQueryStringDef;
569 pData->VDIConfigCallbacks.pfnQueryBytes = drvvdCfgQueryBytes;
570
571 /** @todo TEMP! this isn't really correct - this needs to be made per image,
572 * as CFGM needs access to the right configuration node for each image.
573 * At the moment this is harmless, as iSCSI can only be used as a base
574 * image, and no other backend uses the private data for these callbacks. */
575 rc = VDInterfaceCreate(&pData->VDIConfig, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
576 &pData->VDIConfigCallbacks, NULL /**< @todo TEMP */, &pData->VDIAsyncIO);
577 AssertRC(rc);
578
579 /* Try to attach async media port interface above.*/
580 pData->pDrvMediaAsyncPort = (PPDMIMEDIAASYNCPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_MEDIA_ASYNC_PORT);
581
582 /*
583 * Attach the async transport driver below of the device above us implements the
584 * async interface.
585 */
586 if (pData->pDrvMediaAsyncPort)
587 {
588 /* Try to attach the driver. */
589 PPDMIBASE pBase;
590
591 rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
592 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
593 {
594 /*
595 * Though the device supports async I/O the backend seems to not support it.
596 * Revert to non async I/O.
597 */
598 pData->pDrvMediaAsyncPort = NULL;
599 }
600 else if (VBOX_FAILURE(rc))
601 {
602 AssertMsgFailed(("Failed to attach async transport driver below rc=%Vrc\n", rc));
603 }
604 else
605 {
606 /* Success query the async transport interface. */
607 pData->pDrvTransportAsync = (PPDMITRANSPORTASYNC)pBase->pfnQueryInterface(pBase, PDMINTERFACE_TRANSPORT_ASYNC);
608 if (!pData->pDrvTransportAsync)
609 {
610 /* Whoops. */
611 AssertMsgFailed(("Configuration error: No async transport interface below!\n"));
612 return VERR_PDM_MISSING_INTERFACE_ABOVE;
613 }
614 }
615 }
616
617 /*
618 * Validate configuration and find all parent images.
619 * It's sort of up side down from the image dependency tree.
620 */
621 unsigned iLevel = 0;
622 PCFGMNODE pCurNode = pCfgHandle;
623 for (;;)
624 {
625 bool fValid;
626
627 if (pCurNode == pCfgHandle)
628 {
629 /* Toplevel configuration additionally contains the global image
630 * open flags. Some might be converted to per-image flags later. */
631 fValid = CFGMR3AreValuesValid(pCurNode,
632 "Format\0Path\0"
633 "ReadOnly\0HonorZeroWrites\0");
634 }
635 else
636 {
637 /* All other image configurations only contain image name and
638 * the format information. */
639 fValid = CFGMR3AreValuesValid(pCurNode, "Format\0Path\0");
640 }
641 if (!fValid)
642 {
643 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
644 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
645 break;
646 }
647
648 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
649 if (!pParent)
650 break;
651 pCurNode = pParent;
652 iLevel++;
653 }
654
655 /*
656 * Open the images.
657 */
658 if (VBOX_SUCCESS(rc))
659 {
660 /** @todo TEMP! later the iSCSI config callbacks won't be included here */
661 rc = VDCreate(&pData->VDIConfig, &pData->pDisk);
662 /* Error message is already set correctly. */
663 }
664
665 unsigned cImages = iLevel;
666 while (pCurNode && VBOX_SUCCESS(rc))
667 {
668 /*
669 * Read the image configuration.
670 */
671 rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
672 if (VBOX_FAILURE(rc))
673 {
674 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
675 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
676 break;
677 }
678
679 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Format", &pszFormat);
680 if (VBOX_FAILURE(rc))
681 {
682 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
683 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
684 break;
685 }
686
687 if (iLevel == 0)
688 {
689 rc = CFGMR3QueryBool(pCurNode, "ReadOnly", &fReadOnly);
690 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
691 fReadOnly = false;
692 else if (VBOX_FAILURE(rc))
693 {
694 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
695 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
696 break;
697 }
698
699 rc = CFGMR3QueryBool(pCfgHandle, "HonorZeroWrites", &fHonorZeroWrites);
700 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
701 fHonorZeroWrites = false;
702 else if (VBOX_FAILURE(rc))
703 {
704 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
705 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
706 break;
707 }
708 }
709 else
710 {
711 fReadOnly = true;
712 fHonorZeroWrites = false;
713 }
714
715 /** @todo TEMP! Later this needs to be done for each image. */
716 if (iLevel == cImages)
717 {
718 PCFGMNODE pCfg = CFGMR3GetChild(pCurNode, "VDConfig");
719 pData->VDIConfig.pvUser = pCfg; /**< @todo TEMP! */
720 }
721
722 /*
723 * Open the image.
724 */
725 unsigned uOpenFlags;
726 if (fReadOnly)
727 uOpenFlags = VD_OPEN_FLAGS_READONLY;
728 else
729 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
730 if (fHonorZeroWrites)
731 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
732 if (pData->pDrvMediaAsyncPort)
733 uOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
734
735 /** Try to open backend in asyc I/O mode first. */
736 rc = VDOpen(pData->pDisk, pszFormat, pszName, uOpenFlags);
737 if (rc == VERR_NOT_SUPPORTED)
738 {
739 /* Seems async I/O is not supported by the backend, open in normal mode. */
740 uOpenFlags &= ~VD_OPEN_FLAGS_ASYNC_IO;
741 rc = VDOpen(pData->pDisk, pszFormat, pszName, uOpenFlags);
742 }
743
744 if (VBOX_SUCCESS(rc))
745 Log(("%s: %d - Opened '%s' in %s mode\n", __FUNCTION__,
746 iLevel, pszName,
747 VDIsReadOnly(pData->pDisk) ? "read-only" : "read-write"));
748 else
749 {
750 AssertMsgFailed(("Failed to open image '%s' rc=%Vrc\n", pszName, rc));
751 break;
752 }
753
754
755 MMR3HeapFree(pszName);
756 pszName = NULL;
757 MMR3HeapFree(pszFormat);
758 pszFormat = NULL;
759
760 /* next */
761 iLevel--;
762 pCurNode = CFGMR3GetParent(pCurNode);
763 }
764
765 if (VBOX_FAILURE(rc))
766 {
767 if (VALID_PTR(pData->pDisk))
768 {
769 VDDestroy(pData->pDisk);
770 pData->pDisk = NULL;
771 }
772 if (VALID_PTR(pszName))
773 MMR3HeapFree(pszName);
774 if (VALID_PTR(pszFormat))
775 MMR3HeapFree(pszFormat);
776 }
777
778 /*
779 * Check for async I/O support. Every opened image has to support
780 * it.
781 */
782 pData->fAsyncIOSupported = true;
783 for (unsigned i = 0; i < VDGetCount(pData->pDisk); i++)
784 {
785 VDBACKENDINFO vdBackendInfo;
786
787 rc = VDBackendInfoSingle(pData->pDisk, i, &vdBackendInfo);
788 AssertRC(rc);
789
790 if (vdBackendInfo.uBackendCaps & VD_CAP_ASYNC)
791 {
792 /*
793 * Backend indicates support for at least some files.
794 * Check if current file is supported with async I/O)
795 */
796 rc = VDImageIsAsyncIOSupported(pData->pDisk, i, &pData->fAsyncIOSupported);
797 AssertRC(rc);
798
799 /*
800 * Check if current image is supported.
801 * If not we can stop checking because
802 * at least one does not support it.
803 */
804 if (!pData->fAsyncIOSupported)
805 break;
806 }
807 else
808 {
809 pData->fAsyncIOSupported = false;
810 break;
811 }
812 }
813
814 /* Create cache if async I/O is supported. */
815 if (pData->fAsyncIOSupported)
816 {
817 rc = RTCacheCreate(&pData->pCache, 0, sizeof(DRVVDASYNCTASK), RTOBJCACHE_PROTECT_INSERT);
818 AssertMsg(RT_SUCCESS(rc), ("Failed to create cache rc=%Vrc\n", rc));
819 }
820
821 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
822 return rc;
823}
824
825/**
826 * Destruct a driver instance.
827 *
828 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
829 * resources can be freed correctly.
830 *
831 * @param pDrvIns The driver instance data.
832 */
833static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
834{
835 int rc;
836 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
837 LogFlow(("%s:\n", __FUNCTION__));
838
839 if (pData->pCache)
840 {
841 rc = RTCacheDestroy(pData->pCache);
842 AssertRC(rc);
843 }
844}
845
846
847/**
848 * When the VM has been suspended we'll change the image mode to read-only
849 * so that main and others can read the VDIs. This is important when
850 * saving state and so forth.
851 *
852 * @param pDrvIns The driver instance data.
853 */
854static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
855{
856 LogFlow(("%s:\n", __FUNCTION__));
857 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
858 if (!VDIsReadOnly(pData->pDisk))
859 {
860 unsigned uOpenFlags;
861 int rc = VDGetOpenFlags(pData->pDisk, VD_LAST_IMAGE, &uOpenFlags);
862 AssertRC(rc);
863 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
864 rc = VDSetOpenFlags(pData->pDisk, VD_LAST_IMAGE, uOpenFlags);
865 AssertRC(rc);
866 pData->fTempReadOnly = true;
867 }
868}
869
870/**
871 * Before the VM resumes we'll have to undo the read-only mode change
872 * done in drvvdSuspend.
873 *
874 * @param pDrvIns The driver instance data.
875 */
876static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
877{
878 LogFlow(("%s:\n", __FUNCTION__));
879 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
880 if (pData->fTempReadOnly)
881 {
882 unsigned uOpenFlags;
883 int rc = VDGetOpenFlags(pData->pDisk, VD_LAST_IMAGE, &uOpenFlags);
884 AssertRC(rc);
885 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
886 rc = VDSetOpenFlags(pData->pDisk, VD_LAST_IMAGE, uOpenFlags);
887 AssertRC(rc);
888 pData->fTempReadOnly = false;
889 }
890}
891
892static DECLCALLBACK(void) drvvdPowerOff(PPDMDRVINS pDrvIns)
893{
894 LogFlow(("%s:\n", __FUNCTION__));
895 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
896
897 /*
898 * We must close the disk here to ensure that
899 * the backend closes all files before the
900 * async transport driver is destructed.
901 */
902 int rc = VDCloseAll(pData->pDisk);
903 AssertRC(rc);
904}
905
906/**
907 * VBox disk container media driver registration record.
908 */
909const PDMDRVREG g_DrvVD =
910{
911 /* u32Version */
912 PDM_DRVREG_VERSION,
913 /* szDriverName */
914 "VD",
915 /* pszDescription */
916 "Generic VBox disk media driver.",
917 /* fFlags */
918 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
919 /* fClass. */
920 PDM_DRVREG_CLASS_MEDIA,
921 /* cMaxInstances */
922 ~0,
923 /* cbInstance */
924 sizeof(VBOXDISK),
925 /* pfnConstruct */
926 drvvdConstruct,
927 /* pfnDestruct */
928 drvvdDestruct,
929 /* pfnIOCtl */
930 NULL,
931 /* pfnPowerOn */
932 NULL,
933 /* pfnReset */
934 NULL,
935 /* pfnSuspend */
936 drvvdSuspend,
937 /* pfnResume */
938 drvvdResume,
939 /* pfnDetach */
940 NULL,
941 /* pfnPowerOff */
942 drvvdPowerOff
943};
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