VirtualBox

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

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

Storage/DrvVD: switch to runtime error signalling at the end of driver construction.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 44.8 KB
Line 
1/* $Id: DrvVD.cpp 18766 2009-04-06 14:39:15Z vboxsync $ */
2/** @file
3 * DrvVD - Generic VBox disk media driver.
4 */
5
6/*
7 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DRV_VD
27#include <VBox/VBoxHDD.h>
28#include <VBox/pdmdrv.h>
29#include <iprt/alloc.h>
30#include <iprt/assert.h>
31#include <iprt/uuid.h>
32#include <iprt/file.h>
33#include <iprt/string.h>
34#include <iprt/cache.h>
35#include <iprt/tcp.h>
36
37#ifdef VBOX_WITH_INIP
38/* All lwip header files are not C++ safe. So hack around this. */
39__BEGIN_DECLS
40#include <lwip/inet.h>
41#include <lwip/tcp.h>
42#include <lwip/sockets.h>
43__END_DECLS
44#endif /* VBOX_WITH_INIP */
45
46#include "Builtins.h"
47
48#ifdef VBOX_WITH_INIP
49/* Small hack to get at lwIP initialized status */
50extern bool DevINIPConfigured(void);
51#endif /* VBOX_WITH_INIP */
52
53
54/*******************************************************************************
55* Defined types, constants and macros *
56*******************************************************************************/
57
58/** Converts a pointer to VDIDISK::IMedia to a PVBOXDISK. */
59#define PDMIMEDIA_2_VBOXDISK(pInterface) \
60 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMedia)) )
61
62/** Converts a pointer to PDMDRVINS::IBase to a PPDMDRVINS. */
63#define PDMIBASE_2_DRVINS(pInterface) \
64 ( (PPDMDRVINS)((uintptr_t)pInterface - RT_OFFSETOF(PDMDRVINS, IBase)) )
65
66/** Converts a pointer to PDMDRVINS::IBase to a PVBOXDISK. */
67#define PDMIBASE_2_VBOXDISK(pInterface) \
68 ( PDMINS_2_DATA(PDMIBASE_2_DRVINS(pInterface), PVBOXDISK) )
69
70/** Converts a pointer to VBOXDISK::IMediaAsync to a PVBOXDISK. */
71#define PDMIMEDIAASYNC_2_VBOXDISK(pInterface) \
72 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMediaAsync)) )
73
74/** Converts a pointer to VBOXDISK::ITransportAsyncPort to a PVBOXDISK. */
75#define PDMITRANSPORTASYNCPORT_2_VBOXDISK(pInterface) \
76 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, ITransportAsyncPort)) )
77
78/**
79 * Structure for an async I/O task.
80 */
81typedef struct DRVVDASYNCTASK
82{
83 /** Callback which is called on completion. */
84 PFNVDCOMPLETED pfnCompleted;
85 /** Opqaue user data which is passed on completion. */
86 void *pvUser;
87 /** Opaque user data the caller passed on transfer initiation. */
88 void *pvUserCaller;
89} DRVVDASYNCTASK, *PDRVVDASYNCTASK;
90
91/**
92 * VBox disk container, image information, private part.
93 */
94
95typedef struct VBOXIMAGE
96{
97 /** Pointer to next image. */
98 struct VBOXIMAGE *pNext;
99 /** Pointer to list of VD interfaces. Per-image. */
100 PVDINTERFACE pVDIfsImage;
101 /** Common structure for the configuration information interface. */
102 VDINTERFACE VDIConfig;
103} VBOXIMAGE, *PVBOXIMAGE;
104
105/**
106 * VBox disk container media main structure, private part.
107 */
108typedef struct VBOXDISK
109{
110 /** The VBox disk container. */
111 PVBOXHDD pDisk;
112 /** The media interface. */
113 PDMIMEDIA IMedia;
114 /** Pointer to the driver instance. */
115 PPDMDRVINS pDrvIns;
116 /** Flag whether suspend has changed image open mode to read only. */
117 bool fTempReadOnly;
118 /** Flag whether to use the runtime (true) or startup error facility. */
119 bool fErrorUseRuntime;
120 /** Pointer to list of VD interfaces. Per-disk. */
121 PVDINTERFACE pVDIfsDisk;
122 /** Common structure for the supported error interface. */
123 VDINTERFACE VDIError;
124 /** Callback table for error interface. */
125 VDINTERFACEERROR VDIErrorCallbacks;
126 /** Common structure for the supported TCP network stack interface. */
127 VDINTERFACE VDITcpNet;
128 /** Callback table for TCP network stack interface. */
129 VDINTERFACETCPNET VDITcpNetCallbacks;
130 /** Common structure for the supported async I/O interface. */
131 VDINTERFACE VDIAsyncIO;
132 /** Callback table for async I/O interface. */
133 VDINTERFACEASYNCIO VDIAsyncIOCallbacks;
134 /** Callback table for the configuration information interface. */
135 VDINTERFACECONFIG VDIConfigCallbacks;
136 /** Flag whether opened disk suppports async I/O operations. */
137 bool fAsyncIOSupported;
138 /** The async media interface. */
139 PDMIMEDIAASYNC IMediaAsync;
140 /** The async media port interface above. */
141 PPDMIMEDIAASYNCPORT pDrvMediaAsyncPort;
142 /** Pointer to the asynchronous media driver below. */
143 PPDMITRANSPORTASYNC pDrvTransportAsync;
144 /** Async transport port interface. */
145 PDMITRANSPORTASYNCPORT ITransportAsyncPort;
146 /** Our cache to reduce allocation overhead. */
147 PRTOBJCACHE pCache;
148 /** Pointer to the list of data we need to keep per image. */
149 PVBOXIMAGE pImages;
150} VBOXDISK, *PVBOXDISK;
151
152/*******************************************************************************
153* Error reporting callback *
154*******************************************************************************/
155
156static void drvvdErrorCallback(void *pvUser, int rc, RT_SRC_POS_DECL,
157 const char *pszFormat, va_list va)
158{
159 PPDMDRVINS pDrvIns = (PPDMDRVINS)pvUser;
160 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
161 if (pThis->fErrorUseRuntime)
162 pDrvIns->pDrvHlp->pfnVMSetRuntimeErrorV(pDrvIns, VMSETRTERR_FLAGS_FATAL, "DrvVD", pszFormat, va);
163 else
164 pDrvIns->pDrvHlp->pfnVMSetErrorV(pDrvIns, rc, RT_SRC_POS_ARGS, pszFormat, va);
165}
166
167
168/**
169 * Internal: allocate new image descriptor and put it in the list
170 */
171static PVBOXIMAGE drvvdNewImage(PVBOXDISK pThis)
172{
173 AssertPtr(pThis);
174 PVBOXIMAGE pImage = (PVBOXIMAGE)RTMemAllocZ(sizeof(VBOXIMAGE));
175 if (pImage)
176 {
177 pImage->pVDIfsImage = NULL;
178 PVBOXIMAGE *pp = &pThis->pImages;
179 while (*pp != NULL)
180 pp = &(*pp)->pNext;
181 *pp = pImage;
182 pImage->pNext = NULL;
183 }
184
185 return pImage;
186}
187
188/**
189 * Internal: free the list of images descriptors.
190 */
191static void drvvdFreeImages(PVBOXDISK pThis)
192{
193 while (pThis->pImages != NULL)
194 {
195 PVBOXIMAGE p = pThis->pImages;
196 pThis->pImages = pThis->pImages->pNext;
197 RTMemFree(p);
198 }
199}
200
201/*******************************************************************************
202* VD Async I/O interface implementation *
203*******************************************************************************/
204
205static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation, bool fReadonly, void **ppStorage)
206{
207 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
208
209 return pDrvVD->pDrvTransportAsync->pfnOpen(pDrvVD->pDrvTransportAsync, pszLocation, fReadonly, ppStorage);
210}
211
212static DECLCALLBACK(int) drvvdAsyncIOClose(void *pvUser, void *pStorage)
213{
214 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
215
216 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
217
218 return pDrvVD->pDrvTransportAsync->pfnClose(pDrvVD->pDrvTransportAsync, pStorage);
219}
220
221static DECLCALLBACK(int) drvvdAsyncIORead(void *pvUser, void *pStorage, uint64_t uOffset,
222 size_t cbRead, void *pvBuf, size_t *pcbRead)
223{
224 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
225
226 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
227
228 return pDrvVD->pDrvTransportAsync->pfnReadSynchronous(pDrvVD->pDrvTransportAsync,
229 pStorage,
230 uOffset, pvBuf, cbRead, pcbRead);
231}
232
233static DECLCALLBACK(int) drvvdAsyncIOWrite(void *pvUser, void *pStorage, uint64_t uOffset,
234 size_t cbWrite, const void *pvBuf, size_t *pcbWritten)
235{
236 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
237
238 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
239
240 return pDrvVD->pDrvTransportAsync->pfnWriteSynchronous(pDrvVD->pDrvTransportAsync,
241 pStorage,
242 uOffset, pvBuf, cbWrite, pcbWritten);
243}
244
245static DECLCALLBACK(int) drvvdAsyncIOFlush(void *pvUser, void *pStorage)
246{
247 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
248
249 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
250
251 return pDrvVD->pDrvTransportAsync->pfnFlushSynchronous(pDrvVD->pDrvTransportAsync, pStorage);
252}
253
254static DECLCALLBACK(int) drvvdAsyncIOPrepareRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuf, size_t cbRead, void **ppTask)
255{
256 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
257
258 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
259
260 return pDrvVD->pDrvTransportAsync->pfnPrepareRead(pDrvVD->pDrvTransportAsync, pStorage, uOffset, pvBuf, cbRead, ppTask);
261}
262
263static DECLCALLBACK(int) drvvdAsyncIOPrepareWrite(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuf, size_t cbWrite, void **ppTask)
264{
265 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
266
267 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
268
269 return pDrvVD->pDrvTransportAsync->pfnPrepareWrite(pDrvVD->pDrvTransportAsync, pStorage, uOffset, pvBuf, cbWrite, ppTask);
270}
271
272static DECLCALLBACK(int) drvvdAsyncIOTasksSubmit(void *pvUser, void *apTasks[], unsigned cTasks, void *pvUser2,
273 void *pvUserCaller, PFNVDCOMPLETED pfnTasksCompleted)
274{
275 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
276 PDRVVDASYNCTASK pDrvVDAsyncTask;
277 int rc;
278
279 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
280
281 rc = RTCacheRequest(pDrvVD->pCache, (void **)&pDrvVDAsyncTask);
282
283 if (RT_FAILURE(rc))
284 return rc;
285
286 pDrvVDAsyncTask->pfnCompleted = pfnTasksCompleted;
287 pDrvVDAsyncTask->pvUser = pvUser2;
288 pDrvVDAsyncTask->pvUserCaller = pvUserCaller;
289
290 return pDrvVD->pDrvTransportAsync->pfnTasksSubmit(pDrvVD->pDrvTransportAsync, apTasks, cTasks, pDrvVDAsyncTask);
291}
292
293/*******************************************************************************
294* VD Configuration interface implementation *
295*******************************************************************************/
296
297static bool drvvdCfgAreKeysValid(void *pvUser, const char *pszzValid)
298{
299 return CFGMR3AreValuesValid((PCFGMNODE)pvUser, pszzValid);
300}
301
302static int drvvdCfgQuerySize(void *pvUser, const char *pszName, size_t *pcb)
303{
304 return CFGMR3QuerySize((PCFGMNODE)pvUser, pszName, pcb);
305}
306
307static int drvvdCfgQuery(void *pvUser, const char *pszName, char *pszString, size_t cchString)
308{
309 return CFGMR3QueryString((PCFGMNODE)pvUser, pszName, pszString, cchString);
310}
311
312
313#ifdef VBOX_WITH_INIP
314/*******************************************************************************
315* VD TCP network stack interface implementation - INIP case *
316*******************************************************************************/
317
318/** @copydoc VDINTERFACETCPNET::pfnClientConnect */
319static DECLCALLBACK(int) drvvdINIPClientConnect(const char *pszAddress, uint32_t uPort, PRTSOCKET pSock)
320{
321 int rc = VINF_SUCCESS;
322 /* First check whether lwIP is set up in this VM instance. */
323 if (!DevINIPConfigured())
324 {
325 LogRelFunc(("no IP stack\n"));
326 return VERR_NET_HOST_UNREACHABLE;
327 }
328 /* Resolve hostname. As there is no standard resolver for lwIP yet,
329 * just accept numeric IP addresses for now. */
330 struct in_addr ip;
331 if (!lwip_inet_aton(pszAddress, &ip))
332 {
333 LogRelFunc(("cannot resolve IP %s\n", pszAddress));
334 return VERR_NET_HOST_UNREACHABLE;
335 }
336 /* Create socket and connect. */
337 RTSOCKET Sock = lwip_socket(PF_INET, SOCK_STREAM, 0);
338 if (Sock != -1)
339 {
340 struct sockaddr_in InAddr = {0};
341 InAddr.sin_family = AF_INET;
342 InAddr.sin_port = htons(uPort);
343 InAddr.sin_addr = ip;
344 if (!lwip_connect(Sock, (struct sockaddr *)&InAddr, sizeof(InAddr)))
345 {
346 *pSock = Sock;
347 return VINF_SUCCESS;
348 }
349 rc = VERR_NET_CONNECTION_REFUSED; /* @todo real solution needed */
350 lwip_close(Sock);
351 }
352 else
353 rc = VERR_NET_CONNECTION_REFUSED; /* @todo real solution needed */
354 return rc;
355}
356
357/** @copydoc VDINTERFACETCPNET::pfnClientClose */
358static DECLCALLBACK(int) drvvdINIPClientClose(RTSOCKET Sock)
359{
360 lwip_close(Sock);
361 return VINF_SUCCESS; /** @todo real solution needed */
362}
363
364/** @copydoc VDINTERFACETCPNET::pfnSelectOne */
365static DECLCALLBACK(int) drvvdINIPSelectOne(RTSOCKET Sock, unsigned cMillies)
366{
367 fd_set fdsetR;
368 FD_ZERO(&fdsetR);
369 FD_SET(Sock, &fdsetR);
370 fd_set fdsetE = fdsetR;
371
372 int rc;
373 if (cMillies == RT_INDEFINITE_WAIT)
374 rc = lwip_select(Sock + 1, &fdsetR, NULL, &fdsetE, NULL);
375 else
376 {
377 struct timeval timeout;
378 timeout.tv_sec = cMillies / 1000;
379 timeout.tv_usec = (cMillies % 1000) * 1000;
380 rc = lwip_select(Sock + 1, &fdsetR, NULL, &fdsetE, &timeout);
381 }
382 if (rc > 0)
383 return VINF_SUCCESS;
384 if (rc == 0)
385 return VERR_TIMEOUT;
386 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
387}
388
389/** @copydoc VDINTERFACETCPNET::pfnRead */
390static DECLCALLBACK(int) drvvdINIPRead(RTSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
391{
392 /* Do params checking */
393 if (!pvBuffer || !cbBuffer)
394 {
395 AssertMsgFailed(("Invalid params\n"));
396 return VERR_INVALID_PARAMETER;
397 }
398
399 /*
400 * Read loop.
401 * If pcbRead is NULL we have to fill the entire buffer!
402 */
403 size_t cbRead = 0;
404 size_t cbToRead = cbBuffer;
405 for (;;)
406 {
407 /** @todo this clipping here is just in case (the send function
408 * needed it, so I added it here, too). Didn't investigate if this
409 * really has issues. Better be safe than sorry. */
410 ssize_t cbBytesRead = lwip_recv(Sock, (char *)pvBuffer + cbRead,
411 RT_MIN(cbToRead, 32768), 0);
412 if (cbBytesRead < 0)
413 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution */
414 if (cbBytesRead == 0 && errno)
415 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution */
416 if (pcbRead)
417 {
418 /* return partial data */
419 *pcbRead = cbBytesRead;
420 break;
421 }
422
423 /* read more? */
424 cbRead += cbBytesRead;
425 if (cbRead == cbBuffer)
426 break;
427
428 /* next */
429 cbToRead = cbBuffer - cbRead;
430 }
431
432 return VINF_SUCCESS;
433}
434
435/** @copydoc VDINTERFACETCPNET::pfnWrite */
436static DECLCALLBACK(int) drvvdINIPWrite(RTSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
437{
438 do
439 {
440 /** @todo lwip send only supports up to 65535 bytes in a single
441 * send (stupid limitation buried in the code), so make sure we
442 * don't get any wraparounds. This should be moved to DevINIP
443 * stack interface once that's implemented. */
444 ssize_t cbWritten = lwip_send(Sock, (void *)pvBuffer,
445 RT_MIN(cbBuffer, 32768), 0);
446 if (cbWritten < 0)
447 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
448 AssertMsg(cbBuffer >= (size_t)cbWritten, ("Wrote more than we requested!!! cbWritten=%d cbBuffer=%d\n",
449 cbWritten, cbBuffer));
450 cbBuffer -= cbWritten;
451 pvBuffer = (const char *)pvBuffer + cbWritten;
452 } while (cbBuffer);
453
454 return VINF_SUCCESS;
455}
456
457/** @copydoc VDINTERFACETCPNET::pfnFlush */
458static DECLCALLBACK(int) drvvdINIPFlush(RTSOCKET Sock)
459{
460 int fFlag = 1;
461 lwip_setsockopt(Sock, IPPROTO_TCP, TCP_NODELAY,
462 (const char *)&fFlag, sizeof(fFlag));
463 fFlag = 0;
464 lwip_setsockopt(Sock, IPPROTO_TCP, TCP_NODELAY,
465 (const char *)&fFlag, sizeof(fFlag));
466 return VINF_SUCCESS;
467}
468#endif /* VBOX_WITH_INIP */
469
470
471/*******************************************************************************
472* Media interface methods *
473*******************************************************************************/
474
475/** @copydoc PDMIMEDIA::pfnRead */
476static DECLCALLBACK(int) drvvdRead(PPDMIMEDIA pInterface,
477 uint64_t off, void *pvBuf, size_t cbRead)
478{
479 LogFlow(("%s: off=%#llx pvBuf=%p cbRead=%d\n", __FUNCTION__,
480 off, pvBuf, cbRead));
481 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
482 int rc = VDRead(pThis->pDisk, off, pvBuf, cbRead);
483 if (RT_SUCCESS(rc))
484 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d %.*Rhxd\n", __FUNCTION__,
485 off, pvBuf, cbRead, cbRead, pvBuf));
486 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
487 return rc;
488}
489
490/** @copydoc PDMIMEDIA::pfnWrite */
491static DECLCALLBACK(int) drvvdWrite(PPDMIMEDIA pInterface,
492 uint64_t off, const void *pvBuf,
493 size_t cbWrite)
494{
495 LogFlow(("%s: off=%#llx pvBuf=%p cbWrite=%d\n", __FUNCTION__,
496 off, pvBuf, cbWrite));
497 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
498 Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d %.*Rhxd\n", __FUNCTION__,
499 off, pvBuf, cbWrite, cbWrite, pvBuf));
500 int rc = VDWrite(pThis->pDisk, off, pvBuf, cbWrite);
501 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
502 return rc;
503}
504
505/** @copydoc PDMIMEDIA::pfnFlush */
506static DECLCALLBACK(int) drvvdFlush(PPDMIMEDIA pInterface)
507{
508 LogFlow(("%s:\n", __FUNCTION__));
509 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
510 int rc = VDFlush(pThis->pDisk);
511 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
512 return rc;
513}
514
515/** @copydoc PDMIMEDIA::pfnGetSize */
516static DECLCALLBACK(uint64_t) drvvdGetSize(PPDMIMEDIA pInterface)
517{
518 LogFlow(("%s:\n", __FUNCTION__));
519 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
520 uint64_t cb = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
521 LogFlow(("%s: returns %#llx (%llu)\n", __FUNCTION__, cb, cb));
522 return cb;
523}
524
525/** @copydoc PDMIMEDIA::pfnIsReadOnly */
526static DECLCALLBACK(bool) drvvdIsReadOnly(PPDMIMEDIA pInterface)
527{
528 LogFlow(("%s:\n", __FUNCTION__));
529 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
530 bool f = VDIsReadOnly(pThis->pDisk);
531 LogFlow(("%s: returns %d\n", __FUNCTION__, f));
532 return f;
533}
534
535/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
536static DECLCALLBACK(int) drvvdBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
537 PPDMMEDIAGEOMETRY pPCHSGeometry)
538{
539 LogFlow(("%s:\n", __FUNCTION__));
540 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
541 int rc = VDGetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
542 if (RT_FAILURE(rc))
543 {
544 Log(("%s: geometry not available.\n", __FUNCTION__));
545 rc = VERR_PDM_GEOMETRY_NOT_SET;
546 }
547 LogFlow(("%s: returns %Rrc (CHS=%d/%d/%d)\n", __FUNCTION__,
548 rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
549 return rc;
550}
551
552/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
553static DECLCALLBACK(int) drvvdBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
554 PCPDMMEDIAGEOMETRY pPCHSGeometry)
555{
556 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
557 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
558 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
559 int rc = VDSetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
560 if (rc == VERR_VD_GEOMETRY_NOT_SET)
561 rc = VERR_PDM_GEOMETRY_NOT_SET;
562 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
563 return rc;
564}
565
566/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
567static DECLCALLBACK(int) drvvdBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
568 PPDMMEDIAGEOMETRY pLCHSGeometry)
569{
570 LogFlow(("%s:\n", __FUNCTION__));
571 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
572 int rc = VDGetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
573 if (RT_FAILURE(rc))
574 {
575 Log(("%s: geometry not available.\n", __FUNCTION__));
576 rc = VERR_PDM_GEOMETRY_NOT_SET;
577 }
578 LogFlow(("%s: returns %Rrc (CHS=%d/%d/%d)\n", __FUNCTION__,
579 rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
580 return rc;
581}
582
583/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
584static DECLCALLBACK(int) drvvdBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
585 PCPDMMEDIAGEOMETRY pLCHSGeometry)
586{
587 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
588 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
589 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
590 int rc = VDSetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
591 if (rc == VERR_VD_GEOMETRY_NOT_SET)
592 rc = VERR_PDM_GEOMETRY_NOT_SET;
593 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
594 return rc;
595}
596
597/** @copydoc PDMIMEDIA::pfnGetUuid */
598static DECLCALLBACK(int) drvvdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
599{
600 LogFlow(("%s:\n", __FUNCTION__));
601 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
602 int rc = VDGetUuid(pThis->pDisk, 0, pUuid);
603 LogFlow(("%s: returns %Rrc ({%RTuuid})\n", __FUNCTION__, rc, pUuid));
604 return rc;
605}
606
607/*******************************************************************************
608* Async Media interface methods *
609*******************************************************************************/
610
611static DECLCALLBACK(int) drvvdStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
612 PPDMDATASEG paSeg, unsigned cSeg,
613 size_t cbRead, void *pvUser)
614{
615 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d\n pvUser=%#p", __FUNCTION__,
616 uOffset, paSeg, cSeg, cbRead, pvUser));
617 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
618 int rc = VDAsyncRead(pThis->pDisk, uOffset, cbRead, paSeg, cSeg, pvUser);
619 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
620 return rc;
621}
622
623static DECLCALLBACK(int) drvvdStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
624 PPDMDATASEG paSeg, unsigned cSeg,
625 size_t cbWrite, void *pvUser)
626{
627 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d\n pvUser=%#p", __FUNCTION__,
628 uOffset, paSeg, cSeg, cbWrite, pvUser));
629 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
630 int rc = VDAsyncWrite(pThis->pDisk, uOffset, cbWrite, paSeg, cSeg, pvUser);
631 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
632 return rc;
633}
634
635/*******************************************************************************
636* Async transport port interface methods *
637*******************************************************************************/
638
639static DECLCALLBACK(int) drvvdTasksCompleteNotify(PPDMITRANSPORTASYNCPORT pInterface, void *pvUser)
640{
641 PVBOXDISK pThis = PDMITRANSPORTASYNCPORT_2_VBOXDISK(pInterface);
642 PDRVVDASYNCTASK pDrvVDAsyncTask = (PDRVVDASYNCTASK)pvUser;
643 int rc = VINF_VD_ASYNC_IO_FINISHED;
644
645 /* Having a completion callback for a task is not mandatory. */
646 if (pDrvVDAsyncTask->pfnCompleted)
647 rc = pDrvVDAsyncTask->pfnCompleted(pDrvVDAsyncTask->pvUser);
648
649 /* Check if the request is finished. */
650 if (rc == VINF_VD_ASYNC_IO_FINISHED)
651 {
652 rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort, pDrvVDAsyncTask->pvUserCaller);
653 }
654 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
655 rc = VINF_SUCCESS;
656
657 rc = RTCacheInsert(pThis->pCache, pDrvVDAsyncTask);
658 AssertRC(rc);
659
660 return rc;
661}
662
663
664/*******************************************************************************
665* Base interface methods *
666*******************************************************************************/
667
668/** @copydoc PDMIBASE::pfnQueryInterface */
669static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface,
670 PDMINTERFACE enmInterface)
671{
672 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
673 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
674 switch (enmInterface)
675 {
676 case PDMINTERFACE_BASE:
677 return &pDrvIns->IBase;
678 case PDMINTERFACE_MEDIA:
679 return &pThis->IMedia;
680 case PDMINTERFACE_MEDIA_ASYNC:
681 return pThis->fAsyncIOSupported ? &pThis->IMediaAsync : NULL;
682 case PDMINTERFACE_TRANSPORT_ASYNC_PORT:
683 return &pThis->ITransportAsyncPort;
684 default:
685 return NULL;
686 }
687}
688
689
690/*******************************************************************************
691* Driver methods *
692*******************************************************************************/
693
694
695/**
696 * Construct a VBox disk media driver instance.
697 *
698 * @returns VBox status.
699 * @param pDrvIns The driver instance data.
700 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
701 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
702 * of the driver instance. It's also found in pDrvIns->pCfgHandle as it's expected
703 * to be used frequently in this function.
704 */
705static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
706 PCFGMNODE pCfgHandle)
707{
708 LogFlow(("%s:\n", __FUNCTION__));
709 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
710 int rc = VINF_SUCCESS;
711 char *pszName = NULL; /**< The path of the disk image file. */
712 char *pszFormat = NULL; /**< The format backed to use for this image. */
713 bool fReadOnly; /**< True if the media is readonly. */
714 bool fHonorZeroWrites; /**< True if zero blocks should be written. */
715
716 /*
717 * Init the static parts.
718 */
719 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
720 pThis->pDrvIns = pDrvIns;
721 pThis->fTempReadOnly = false;
722 pThis->pDisk = NULL;
723 pThis->fAsyncIOSupported = false;
724
725 /* IMedia */
726 pThis->IMedia.pfnRead = drvvdRead;
727 pThis->IMedia.pfnWrite = drvvdWrite;
728 pThis->IMedia.pfnFlush = drvvdFlush;
729 pThis->IMedia.pfnGetSize = drvvdGetSize;
730 pThis->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
731 pThis->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
732 pThis->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
733 pThis->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
734 pThis->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
735 pThis->IMedia.pfnGetUuid = drvvdGetUuid;
736
737 /* IMediaAsync */
738 pThis->IMediaAsync.pfnStartRead = drvvdStartRead;
739 pThis->IMediaAsync.pfnStartWrite = drvvdStartWrite;
740
741 /* ITransportAsyncPort */
742 pThis->ITransportAsyncPort.pfnTaskCompleteNotify = drvvdTasksCompleteNotify;
743
744 /* Initialize supported VD interfaces. */
745 pThis->pVDIfsDisk = NULL;
746
747 pThis->VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
748 pThis->VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
749 pThis->VDIErrorCallbacks.pfnError = drvvdErrorCallback;
750
751 rc = VDInterfaceAdd(&pThis->VDIError, "DrvVD_VDIError", VDINTERFACETYPE_ERROR,
752 &pThis->VDIErrorCallbacks, pDrvIns, &pThis->pVDIfsDisk);
753 AssertRC(rc);
754
755 pThis->VDIAsyncIOCallbacks.cbSize = sizeof(VDINTERFACEASYNCIO);
756 pThis->VDIAsyncIOCallbacks.enmInterface = VDINTERFACETYPE_ASYNCIO;
757 pThis->VDIAsyncIOCallbacks.pfnOpen = drvvdAsyncIOOpen;
758 pThis->VDIAsyncIOCallbacks.pfnClose = drvvdAsyncIOClose;
759 pThis->VDIAsyncIOCallbacks.pfnRead = drvvdAsyncIORead;
760 pThis->VDIAsyncIOCallbacks.pfnWrite = drvvdAsyncIOWrite;
761 pThis->VDIAsyncIOCallbacks.pfnFlush = drvvdAsyncIOFlush;
762 pThis->VDIAsyncIOCallbacks.pfnPrepareRead = drvvdAsyncIOPrepareRead;
763 pThis->VDIAsyncIOCallbacks.pfnPrepareWrite = drvvdAsyncIOPrepareWrite;
764 pThis->VDIAsyncIOCallbacks.pfnTasksSubmit = drvvdAsyncIOTasksSubmit;
765
766 rc = VDInterfaceAdd(&pThis->VDIAsyncIO, "DrvVD_AsyncIO", VDINTERFACETYPE_ASYNCIO,
767 &pThis->VDIAsyncIOCallbacks, pThis, &pThis->pVDIfsDisk);
768 AssertRC(rc);
769
770 /* This is just prepared here, the actual interface is per-image, so it's
771 * added later. No need to have separate callback tables. */
772 pThis->VDIConfigCallbacks.cbSize = sizeof(VDINTERFACECONFIG);
773 pThis->VDIConfigCallbacks.enmInterface = VDINTERFACETYPE_CONFIG;
774 pThis->VDIConfigCallbacks.pfnAreKeysValid = drvvdCfgAreKeysValid;
775 pThis->VDIConfigCallbacks.pfnQuerySize = drvvdCfgQuerySize;
776 pThis->VDIConfigCallbacks.pfnQuery = drvvdCfgQuery;
777
778 /* List of images is empty now. */
779 pThis->pImages = NULL;
780
781 /* Try to attach async media port interface above.*/
782 pThis->pDrvMediaAsyncPort = (PPDMIMEDIAASYNCPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_MEDIA_ASYNC_PORT);
783
784 /*
785 * Attach the async transport driver below if the device above us implements the
786 * async interface.
787 */
788 if (pThis->pDrvMediaAsyncPort)
789 {
790 /* Try to attach the driver. */
791 PPDMIBASE pBase;
792
793 rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
794 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
795 {
796 /*
797 * Though the device supports async I/O there is no transport driver
798 * which processes async requests.
799 * Revert to non async I/O.
800 */
801 rc = VINF_SUCCESS;
802 pThis->pDrvMediaAsyncPort = NULL;
803 pThis->fAsyncIOSupported = false;
804 }
805 else if (RT_FAILURE(rc))
806 {
807 AssertMsgFailed(("Failed to attach async transport driver below rc=%Rrc\n", rc));
808 }
809 else
810 {
811 /*
812 * The device supports async I/O and we successfully attached the transport driver.
813 * Indicate that async I/O is supported for now as we check if the image backend supports
814 * it later.
815 */
816 pThis->fAsyncIOSupported = true;
817
818 /* Success query the async transport interface. */
819 pThis->pDrvTransportAsync = (PPDMITRANSPORTASYNC)pBase->pfnQueryInterface(pBase, PDMINTERFACE_TRANSPORT_ASYNC);
820 if (!pThis->pDrvTransportAsync)
821 {
822 /* An attached driver without an async transport interface - impossible. */
823 AssertMsgFailed(("Configuration error: No async transport interface below!\n"));
824 return VERR_PDM_MISSING_INTERFACE_ABOVE;
825 }
826 }
827 }
828
829 /*
830 * Validate configuration and find all parent images.
831 * It's sort of up side down from the image dependency tree.
832 */
833 bool fHostIP = false;
834 unsigned iLevel = 0;
835 PCFGMNODE pCurNode = pCfgHandle;
836
837 for (;;)
838 {
839 bool fValid;
840
841 if (pCurNode == pCfgHandle)
842 {
843 /* Toplevel configuration additionally contains the global image
844 * open flags. Some might be converted to per-image flags later. */
845 fValid = CFGMR3AreValuesValid(pCurNode,
846 "Format\0Path\0"
847 "ReadOnly\0HonorZeroWrites\0"
848 "HostIPStack\0");
849 }
850 else
851 {
852 /* All other image configurations only contain image name and
853 * the format information. */
854 fValid = CFGMR3AreValuesValid(pCurNode, "Format\0Path\0");
855 }
856 if (!fValid)
857 {
858 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
859 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
860 break;
861 }
862
863 if (pCurNode == pCfgHandle)
864 {
865 rc = CFGMR3QueryBool(pCurNode, "HostIPStack", &fHostIP);
866 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
867 {
868 fHostIP = true;
869 rc = VINF_SUCCESS;
870 }
871 else if (RT_FAILURE(rc))
872 {
873 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
874 N_("DrvVD: Configuration error: Querying \"HostIPStack\" as boolean failed"));
875 break;
876 }
877
878 rc = CFGMR3QueryBool(pCurNode, "HonorZeroWrites", &fHonorZeroWrites);
879 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
880 {
881 fHonorZeroWrites = false;
882 rc = VINF_SUCCESS;
883 }
884 else if (RT_FAILURE(rc))
885 {
886 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
887 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
888 break;
889 }
890
891 rc = CFGMR3QueryBool(pCurNode, "ReadOnly", &fReadOnly);
892 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
893 {
894 fReadOnly = false;
895 rc = VINF_SUCCESS;
896 }
897 else if (RT_FAILURE(rc))
898 {
899 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
900 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
901 break;
902 }
903 }
904
905 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
906 if (!pParent)
907 break;
908 pCurNode = pParent;
909 iLevel++;
910 }
911
912 /*
913 * Open the images.
914 */
915 if (RT_SUCCESS(rc))
916 {
917 /* First of all figure out what kind of TCP networking stack interface
918 * to use. This is done unconditionally, as backends which don't need
919 * it will just ignore it. */
920 if (fHostIP)
921 {
922 pThis->VDITcpNetCallbacks.cbSize = sizeof(VDINTERFACETCPNET);
923 pThis->VDITcpNetCallbacks.enmInterface = VDINTERFACETYPE_TCPNET;
924 pThis->VDITcpNetCallbacks.pfnClientConnect = RTTcpClientConnect;
925 pThis->VDITcpNetCallbacks.pfnClientClose = RTTcpClientClose;
926 pThis->VDITcpNetCallbacks.pfnSelectOne = RTTcpSelectOne;
927 pThis->VDITcpNetCallbacks.pfnRead = RTTcpRead;
928 pThis->VDITcpNetCallbacks.pfnWrite = RTTcpWrite;
929 pThis->VDITcpNetCallbacks.pfnFlush = RTTcpFlush;
930 }
931 else
932 {
933#ifndef VBOX_WITH_INIP
934 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
935 RT_SRC_POS, N_("DrvVD: Configuration error: TCP over Internal Networking not compiled in"));
936#else /* VBOX_WITH_INIP */
937 pThis->VDITcpNetCallbacks.cbSize = sizeof(VDINTERFACETCPNET);
938 pThis->VDITcpNetCallbacks.enmInterface = VDINTERFACETYPE_TCPNET;
939 pThis->VDITcpNetCallbacks.pfnClientConnect = drvvdINIPClientConnect;
940 pThis->VDITcpNetCallbacks.pfnClientClose = drvvdINIPClientClose;
941 pThis->VDITcpNetCallbacks.pfnSelectOne = drvvdINIPSelectOne;
942 pThis->VDITcpNetCallbacks.pfnRead = drvvdINIPRead;
943 pThis->VDITcpNetCallbacks.pfnWrite = drvvdINIPWrite;
944 pThis->VDITcpNetCallbacks.pfnFlush = drvvdINIPFlush;
945#endif /* VBOX_WITH_INIP */
946 }
947 if (RT_SUCCESS(rc))
948 {
949 rc = VDInterfaceAdd(&pThis->VDITcpNet, "DrvVD_INIP",
950 VDINTERFACETYPE_TCPNET,
951 &pThis->VDITcpNetCallbacks, NULL,
952 &pThis->pVDIfsDisk);
953 }
954 if (RT_SUCCESS(rc))
955 {
956 rc = VDCreate(pThis->pVDIfsDisk, &pThis->pDisk);
957 /* Error message is already set correctly. */
958 }
959 }
960
961 while (pCurNode && RT_SUCCESS(rc))
962 {
963 /* Allocate per-image data. */
964 PVBOXIMAGE pImage = drvvdNewImage(pThis);
965 if (!pImage)
966 {
967 rc = VERR_NO_MEMORY;
968 break;
969 }
970
971 /*
972 * Read the image configuration.
973 */
974 rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
975 if (RT_FAILURE(rc))
976 {
977 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
978 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
979 break;
980 }
981
982 rc = CFGMR3QueryStringAlloc(pCurNode, "Format", &pszFormat);
983 if (RT_FAILURE(rc))
984 {
985 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
986 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
987 break;
988 }
989
990 PCFGMNODE pCfg = CFGMR3GetChild(pCurNode, "VDConfig");
991 rc = VDInterfaceAdd(&pImage->VDIConfig, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
992 &pThis->VDIConfigCallbacks, pCfg, &pImage->pVDIfsImage);
993 AssertRC(rc);
994
995 /*
996 * Open the image.
997 */
998 unsigned uOpenFlags;
999 if (fReadOnly || iLevel != 0)
1000 uOpenFlags = VD_OPEN_FLAGS_READONLY;
1001 else
1002 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
1003 if (fHonorZeroWrites)
1004 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
1005 if (pThis->pDrvMediaAsyncPort)
1006 uOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
1007
1008 /* Try to open backend in asyc I/O mode first. */
1009 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
1010 if (rc == VERR_NOT_SUPPORTED)
1011 {
1012 /* Seems async I/O is not supported by the backend, open in normal mode. */
1013 uOpenFlags &= ~VD_OPEN_FLAGS_ASYNC_IO;
1014 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
1015 }
1016
1017 if (RT_SUCCESS(rc))
1018 {
1019 Log(("%s: %d - Opened '%s' in %s mode\n", __FUNCTION__,
1020 iLevel, pszName,
1021 VDIsReadOnly(pThis->pDisk) ? "read-only" : "read-write"));
1022 if ( VDIsReadOnly(pThis->pDisk)
1023 && !fReadOnly
1024 && iLevel == 0)
1025 {
1026 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_VD_IMAGE_READ_ONLY, RT_SRC_POS,
1027 N_("Failed to open image '%s' for writing due to wrong "
1028 "permissions"), pszName);
1029 break;
1030 }
1031 }
1032 else
1033 {
1034 rc = PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1035 N_("Failed to open image '%s' in %s mode rc=%Rrc"), pszName,
1036 (uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "readonly" : "read-write", rc);
1037 break;
1038 }
1039
1040
1041 MMR3HeapFree(pszName);
1042 pszName = NULL;
1043 MMR3HeapFree(pszFormat);
1044 pszFormat = NULL;
1045
1046 /* next */
1047 iLevel--;
1048 pCurNode = CFGMR3GetParent(pCurNode);
1049 }
1050
1051 if (RT_FAILURE(rc))
1052 {
1053 if (VALID_PTR(pThis->pDisk))
1054 {
1055 VDDestroy(pThis->pDisk);
1056 pThis->pDisk = NULL;
1057 }
1058 drvvdFreeImages(pThis);
1059 if (VALID_PTR(pszName))
1060 MMR3HeapFree(pszName);
1061 if (VALID_PTR(pszFormat))
1062 MMR3HeapFree(pszFormat);
1063
1064 return rc;
1065 }
1066 else
1067 {
1068 /*
1069 * Check if every opened image supports async I/O.
1070 * If not we revert to non async I/O.
1071 */
1072 if (pThis->fAsyncIOSupported)
1073 {
1074 for (unsigned i = 0; i < VDGetCount(pThis->pDisk); i++)
1075 {
1076 VDBACKENDINFO vdBackendInfo;
1077
1078 rc = VDBackendInfoSingle(pThis->pDisk, i, &vdBackendInfo);
1079 AssertRC(rc);
1080
1081 if (vdBackendInfo.uBackendCaps & VD_CAP_ASYNC)
1082 {
1083 /*
1084 * Backend indicates support for at least some files.
1085 * Check if current file is supported with async I/O)
1086 */
1087 rc = VDImageIsAsyncIOSupported(pThis->pDisk, i, &pThis->fAsyncIOSupported);
1088 AssertRC(rc);
1089
1090 /*
1091 * Check if current image is supported.
1092 * If not we can stop checking because
1093 * at least one does not support it.
1094 */
1095 if (!pThis->fAsyncIOSupported)
1096 break;
1097 }
1098 else
1099 {
1100 pThis->fAsyncIOSupported = false;
1101 break;
1102 }
1103 }
1104 }
1105
1106 /*
1107 * We know definitly if async I/O is supported now.
1108 * Create cache if it is supported.
1109 */
1110 if (pThis->fAsyncIOSupported)
1111 {
1112 rc = RTCacheCreate(&pThis->pCache, 0, sizeof(DRVVDASYNCTASK), RTOBJCACHE_PROTECT_INSERT);
1113 AssertMsgRC(rc, ("Failed to create cache rc=%Rrc\n", rc));
1114 }
1115
1116 /* Switch to runtime error facility. */
1117 pThis->fErrorUseRuntime = true;
1118 }
1119
1120 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
1121 return rc;
1122}
1123
1124/**
1125 * Destruct a driver instance.
1126 *
1127 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1128 * resources can be freed correctly.
1129 *
1130 * @param pDrvIns The driver instance data.
1131 */
1132static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
1133{
1134 int rc;
1135 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1136 LogFlow(("%s:\n", __FUNCTION__));
1137
1138 drvvdFreeImages(pThis);
1139 if (pThis->pCache)
1140 {
1141 rc = RTCacheDestroy(pThis->pCache);
1142 AssertRC(rc);
1143 }
1144}
1145
1146
1147/**
1148 * When the VM has been suspended we'll change the image mode to read-only
1149 * so that main and others can read the VDIs. This is important when
1150 * saving state and so forth.
1151 *
1152 * @param pDrvIns The driver instance data.
1153 */
1154static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
1155{
1156 LogFlow(("%s:\n", __FUNCTION__));
1157 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1158 if (!VDIsReadOnly(pThis->pDisk))
1159 {
1160 unsigned uOpenFlags;
1161 int rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
1162 AssertRC(rc);
1163 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
1164 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
1165 AssertRC(rc);
1166 pThis->fTempReadOnly = true;
1167 }
1168}
1169
1170/**
1171 * Before the VM resumes we'll have to undo the read-only mode change
1172 * done in drvvdSuspend.
1173 *
1174 * @param pDrvIns The driver instance data.
1175 */
1176static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
1177{
1178 LogFlow(("%s:\n", __FUNCTION__));
1179 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1180 if (pThis->fTempReadOnly)
1181 {
1182 unsigned uOpenFlags;
1183 int rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
1184 AssertRC(rc);
1185 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
1186 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
1187 AssertRC(rc);
1188 pThis->fTempReadOnly = false;
1189 }
1190}
1191
1192static DECLCALLBACK(void) drvvdPowerOff(PPDMDRVINS pDrvIns)
1193{
1194 LogFlow(("%s:\n", __FUNCTION__));
1195 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1196
1197 /*
1198 * We must close the disk here to ensure that
1199 * the backend closes all files before the
1200 * async transport driver is destructed.
1201 */
1202 int rc = VDCloseAll(pThis->pDisk);
1203 AssertRC(rc);
1204}
1205
1206/**
1207 * VBox disk container media driver registration record.
1208 */
1209const PDMDRVREG g_DrvVD =
1210{
1211 /* u32Version */
1212 PDM_DRVREG_VERSION,
1213 /* szDriverName */
1214 "VD",
1215 /* pszDescription */
1216 "Generic VBox disk media driver.",
1217 /* fFlags */
1218 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1219 /* fClass. */
1220 PDM_DRVREG_CLASS_MEDIA,
1221 /* cMaxInstances */
1222 ~0,
1223 /* cbInstance */
1224 sizeof(VBOXDISK),
1225 /* pfnConstruct */
1226 drvvdConstruct,
1227 /* pfnDestruct */
1228 drvvdDestruct,
1229 /* pfnIOCtl */
1230 NULL,
1231 /* pfnPowerOn */
1232 NULL,
1233 /* pfnReset */
1234 NULL,
1235 /* pfnSuspend */
1236 drvvdSuspend,
1237 /* pfnResume */
1238 drvvdResume,
1239 /* pfnDetach */
1240 NULL,
1241 /* pfnPowerOff */
1242 drvvdPowerOff
1243};
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