VirtualBox

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

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

OSE: export iSCSI initiator, plus minor ifdef cleanup

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