VirtualBox

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

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

VBoxHDD: simplify config handling. Treat everything as a string, and do the conversion in the common code. Makes the job of implementing the VBox API (iSCSI in particular) easier.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.8 KB
Line 
1/* $Id: DrvVD.cpp 14780 2008-11-28 14:36:22Z 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-new.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#ifndef VBOX_OSE
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_OSE */
45
46#include "Builtins.h"
47
48#ifndef VBOX_OSE
49/* Small hack to get at lwIP initialized status */
50extern bool DevINIPConfigured(void);
51#endif /* !VBOX_OSE */
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#ifndef VBOX_OSE
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_OSE */
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 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
555 return rc;
556}
557
558/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
559static DECLCALLBACK(int) drvvdBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
560 PPDMMEDIAGEOMETRY pLCHSGeometry)
561{
562 LogFlow(("%s:\n", __FUNCTION__));
563 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
564 int rc = VDGetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
565 if (RT_FAILURE(rc))
566 {
567 Log(("%s: geometry not available.\n", __FUNCTION__));
568 rc = VERR_PDM_GEOMETRY_NOT_SET;
569 }
570 LogFlow(("%s: returns %Rrc (CHS=%d/%d/%d)\n", __FUNCTION__,
571 rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
572 return rc;
573}
574
575/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
576static DECLCALLBACK(int) drvvdBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
577 PCPDMMEDIAGEOMETRY pLCHSGeometry)
578{
579 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
580 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
581 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
582 int rc = VDSetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
583 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
584 return rc;
585}
586
587/** @copydoc PDMIMEDIA::pfnGetUuid */
588static DECLCALLBACK(int) drvvdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
589{
590 LogFlow(("%s:\n", __FUNCTION__));
591 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
592 int rc = VDGetUuid(pThis->pDisk, 0, pUuid);
593 LogFlow(("%s: returns %Rrc ({%RTuuid})\n", __FUNCTION__, rc, pUuid));
594 return rc;
595}
596
597/*******************************************************************************
598* Async Media interface methods *
599*******************************************************************************/
600
601static DECLCALLBACK(int) drvvdStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
602 PPDMDATASEG paSeg, unsigned cSeg,
603 size_t cbRead, void *pvUser)
604{
605 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d\n pvUser=%#p", __FUNCTION__,
606 uOffset, paSeg, cSeg, cbRead, pvUser));
607 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
608 int rc = VDAsyncRead(pThis->pDisk, uOffset, cbRead, paSeg, cSeg, pvUser);
609 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
610 return rc;
611}
612
613static DECLCALLBACK(int) drvvdStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
614 PPDMDATASEG paSeg, unsigned cSeg,
615 size_t cbWrite, void *pvUser)
616{
617 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d\n pvUser=%#p", __FUNCTION__,
618 uOffset, paSeg, cSeg, cbWrite, pvUser));
619 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
620 int rc = VDAsyncWrite(pThis->pDisk, uOffset, cbWrite, paSeg, cSeg, pvUser);
621 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
622 return rc;
623}
624
625/*******************************************************************************
626* Async transport port interface methods *
627*******************************************************************************/
628
629static DECLCALLBACK(int) drvvdTasksCompleteNotify(PPDMITRANSPORTASYNCPORT pInterface, void *pvUser)
630{
631 PVBOXDISK pThis = PDMITRANSPORTASYNCPORT_2_VBOXDISK(pInterface);
632 PDRVVDASYNCTASK pDrvVDAsyncTask = (PDRVVDASYNCTASK)pvUser;
633 int rc = VINF_VDI_ASYNC_IO_FINISHED;
634
635 /* Having a completion callback for a task is not mandatory. */
636 if (pDrvVDAsyncTask->pfnCompleted)
637 rc = pDrvVDAsyncTask->pfnCompleted(pDrvVDAsyncTask->pvUser);
638
639 /* Check if the request is finished. */
640 if (rc == VINF_VDI_ASYNC_IO_FINISHED)
641 {
642 rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort, pDrvVDAsyncTask->pvUserCaller);
643 }
644 else if (rc == VERR_VDI_ASYNC_IO_IN_PROGRESS)
645 rc = VINF_SUCCESS;
646
647 rc = RTCacheInsert(pThis->pCache, pDrvVDAsyncTask);
648 AssertRC(rc);
649
650 return rc;
651}
652
653
654/*******************************************************************************
655* Base interface methods *
656*******************************************************************************/
657
658/** @copydoc PDMIBASE::pfnQueryInterface */
659static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface,
660 PDMINTERFACE enmInterface)
661{
662 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
663 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
664 switch (enmInterface)
665 {
666 case PDMINTERFACE_BASE:
667 return &pDrvIns->IBase;
668 case PDMINTERFACE_MEDIA:
669 return &pThis->IMedia;
670 case PDMINTERFACE_MEDIA_ASYNC:
671 return pThis->fAsyncIOSupported ? &pThis->IMediaAsync : NULL;
672 case PDMINTERFACE_TRANSPORT_ASYNC_PORT:
673 return &pThis->ITransportAsyncPort;
674 default:
675 return NULL;
676 }
677}
678
679
680/*******************************************************************************
681* Driver methods *
682*******************************************************************************/
683
684
685/**
686 * Construct a VBox disk media driver instance.
687 *
688 * @returns VBox status.
689 * @param pDrvIns The driver instance data.
690 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
691 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
692 * of the driver instance. It's also found in pDrvIns->pCfgHandle as it's expected
693 * to be used frequently in this function.
694 */
695static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
696 PCFGMNODE pCfgHandle)
697{
698 LogFlow(("%s:\n", __FUNCTION__));
699 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
700 int rc = VINF_SUCCESS;
701 char *pszName = NULL; /**< The path of the disk image file. */
702 char *pszFormat = NULL; /**< The format backed to use for this image. */
703 bool fReadOnly; /**< True if the media is readonly. */
704 bool fHonorZeroWrites; /**< True if zero blocks should be written. */
705
706 /*
707 * Init the static parts.
708 */
709 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
710 pThis->pDrvIns = pDrvIns;
711 pThis->fTempReadOnly = false;
712 pThis->pDisk = NULL;
713 pThis->fAsyncIOSupported = false;
714
715 /* IMedia */
716 pThis->IMedia.pfnRead = drvvdRead;
717 pThis->IMedia.pfnWrite = drvvdWrite;
718 pThis->IMedia.pfnFlush = drvvdFlush;
719 pThis->IMedia.pfnGetSize = drvvdGetSize;
720 pThis->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
721 pThis->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
722 pThis->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
723 pThis->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
724 pThis->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
725 pThis->IMedia.pfnGetUuid = drvvdGetUuid;
726
727 /* IMediaAsync */
728 pThis->IMediaAsync.pfnStartRead = drvvdStartRead;
729 pThis->IMediaAsync.pfnStartWrite = drvvdStartWrite;
730
731 /* ITransportAsyncPort */
732 pThis->ITransportAsyncPort.pfnTaskCompleteNotify = drvvdTasksCompleteNotify;
733
734 /* Initialize supported VD interfaces. */
735 pThis->pVDIfsDisk = NULL;
736
737 pThis->VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
738 pThis->VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
739 pThis->VDIErrorCallbacks.pfnError = drvvdErrorCallback;
740
741 rc = VDInterfaceAdd(&pThis->VDIError, "DrvVD_VDIError", VDINTERFACETYPE_ERROR,
742 &pThis->VDIErrorCallbacks, pDrvIns, &pThis->pVDIfsDisk);
743 AssertRC(rc);
744
745 pThis->VDIAsyncIOCallbacks.cbSize = sizeof(VDINTERFACEASYNCIO);
746 pThis->VDIAsyncIOCallbacks.enmInterface = VDINTERFACETYPE_ASYNCIO;
747 pThis->VDIAsyncIOCallbacks.pfnOpen = drvvdAsyncIOOpen;
748 pThis->VDIAsyncIOCallbacks.pfnClose = drvvdAsyncIOClose;
749 pThis->VDIAsyncIOCallbacks.pfnRead = drvvdAsyncIORead;
750 pThis->VDIAsyncIOCallbacks.pfnWrite = drvvdAsyncIOWrite;
751 pThis->VDIAsyncIOCallbacks.pfnFlush = drvvdAsyncIOFlush;
752 pThis->VDIAsyncIOCallbacks.pfnPrepareRead = drvvdAsyncIOPrepareRead;
753 pThis->VDIAsyncIOCallbacks.pfnPrepareWrite = drvvdAsyncIOPrepareWrite;
754 pThis->VDIAsyncIOCallbacks.pfnTasksSubmit = drvvdAsyncIOTasksSubmit;
755
756 rc = VDInterfaceAdd(&pThis->VDIAsyncIO, "DrvVD_AsyncIO", VDINTERFACETYPE_ASYNCIO,
757 &pThis->VDIAsyncIOCallbacks, pThis, &pThis->pVDIfsDisk);
758 AssertRC(rc);
759
760 /* This is just prepared here, the actual interface is per-image, so it's
761 * added later. No need to have separate callback tables. */
762 pThis->VDIConfigCallbacks.cbSize = sizeof(VDINTERFACECONFIG);
763 pThis->VDIConfigCallbacks.enmInterface = VDINTERFACETYPE_CONFIG;
764 pThis->VDIConfigCallbacks.pfnAreKeysValid = drvvdCfgAreKeysValid;
765 pThis->VDIConfigCallbacks.pfnQuerySize = drvvdCfgQuerySize;
766 pThis->VDIConfigCallbacks.pfnQuery = drvvdCfgQuery;
767
768 /* List of images is empty now. */
769 pThis->pImages = NULL;
770
771 /* Try to attach async media port interface above.*/
772 pThis->pDrvMediaAsyncPort = (PPDMIMEDIAASYNCPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_MEDIA_ASYNC_PORT);
773
774 /*
775 * Attach the async transport driver below if the device above us implements the
776 * async interface.
777 */
778 if (pThis->pDrvMediaAsyncPort)
779 {
780 /* Try to attach the driver. */
781 PPDMIBASE pBase;
782
783 rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
784 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
785 {
786 /*
787 * Though the device supports async I/O there is no transport driver
788 * which processes async requests.
789 * Revert to non async I/O.
790 */
791 rc = VINF_SUCCESS;
792 pThis->pDrvMediaAsyncPort = NULL;
793 pThis->fAsyncIOSupported = false;
794 }
795 else if (RT_FAILURE(rc))
796 {
797 AssertMsgFailed(("Failed to attach async transport driver below rc=%Rrc\n", rc));
798 }
799 else
800 {
801 /*
802 * The device supports async I/O and we successfully attached the transport driver.
803 * Indicate that async I/O is supported for now as we check if the image backend supports
804 * it later.
805 */
806 pThis->fAsyncIOSupported = true;
807
808 /* Success query the async transport interface. */
809 pThis->pDrvTransportAsync = (PPDMITRANSPORTASYNC)pBase->pfnQueryInterface(pBase, PDMINTERFACE_TRANSPORT_ASYNC);
810 if (!pThis->pDrvTransportAsync)
811 {
812 /* An attached driver without an async transport interface - impossible. */
813 AssertMsgFailed(("Configuration error: No async transport interface below!\n"));
814 return VERR_PDM_MISSING_INTERFACE_ABOVE;
815 }
816 }
817 }
818
819 /*
820 * Validate configuration and find all parent images.
821 * It's sort of up side down from the image dependency tree.
822 */
823 bool fHostIP = false;
824 unsigned iLevel = 0;
825 PCFGMNODE pCurNode = pCfgHandle;
826
827 for (;;)
828 {
829 bool fValid;
830
831 if (pCurNode == pCfgHandle)
832 {
833 /* Toplevel configuration additionally contains the global image
834 * open flags. Some might be converted to per-image flags later. */
835 fValid = CFGMR3AreValuesValid(pCurNode,
836 "Format\0Path\0"
837 "ReadOnly\0HonorZeroWrites\0"
838 "HostIPStack\0");
839
840 rc = CFGMR3QueryBool(pCfgHandle, "HostIPStack", &fHostIP);
841 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
842 {
843 fHostIP = true;
844 rc = VINF_SUCCESS;
845 }
846 else if (RT_FAILURE(rc))
847 {
848 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
849 N_("DrvVD: Configuration error: Querying \"HostIPStack\" as boolean failed"));
850 break;
851 }
852 }
853 else
854 {
855 /* All other image configurations only contain image name and
856 * the format information. */
857 fValid = CFGMR3AreValuesValid(pCurNode, "Format\0Path\0");
858 }
859 if (!fValid)
860 {
861 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
862 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
863 break;
864 }
865
866 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
867 if (!pParent)
868 break;
869 pCurNode = pParent;
870 iLevel++;
871 }
872
873 /*
874 * Open the images.
875 */
876 if (RT_SUCCESS(rc))
877 {
878 /* First of all figure out what kind of TCP networking stack interface
879 * to use. This is done unconditionally, as backends which don't need
880 * it will just ignore it. */
881 if (fHostIP)
882 {
883 pThis->VDITcpNetCallbacks.cbSize = sizeof(VDINTERFACETCPNET);
884 pThis->VDITcpNetCallbacks.enmInterface = VDINTERFACETYPE_TCPNET;
885 pThis->VDITcpNetCallbacks.pfnClientConnect = RTTcpClientConnect;
886 pThis->VDITcpNetCallbacks.pfnClientClose = RTTcpClientClose;
887 pThis->VDITcpNetCallbacks.pfnSelectOne = RTTcpSelectOne;
888 pThis->VDITcpNetCallbacks.pfnRead = RTTcpRead;
889 pThis->VDITcpNetCallbacks.pfnWrite = RTTcpWrite;
890 pThis->VDITcpNetCallbacks.pfnFlush = RTTcpFlush;
891 }
892 else
893 {
894#ifdef VBOX_OSE
895 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
896 RT_SRC_POS, N_("DrvVD: Configuration error: TCP over Internal Networking not supported in VirtualBox OSE"));
897#else /* !VBOX_OSE */
898 pThis->VDITcpNetCallbacks.cbSize = sizeof(VDINTERFACETCPNET);
899 pThis->VDITcpNetCallbacks.enmInterface = VDINTERFACETYPE_TCPNET;
900 pThis->VDITcpNetCallbacks.pfnClientConnect = drvvdINIPClientConnect;
901 pThis->VDITcpNetCallbacks.pfnClientClose = drvvdINIPClientClose;
902 pThis->VDITcpNetCallbacks.pfnSelectOne = drvvdINIPSelectOne;
903 pThis->VDITcpNetCallbacks.pfnRead = drvvdINIPRead;
904 pThis->VDITcpNetCallbacks.pfnWrite = drvvdINIPWrite;
905 pThis->VDITcpNetCallbacks.pfnFlush = drvvdINIPFlush;
906#endif /* !VBOX_OSE */
907 }
908 if (RT_SUCCESS(rc))
909 {
910 rc = VDInterfaceAdd(&pThis->VDITcpNet, "DrvVD_INIP",
911 VDINTERFACETYPE_TCPNET,
912 &pThis->VDITcpNetCallbacks, NULL,
913 &pThis->pVDIfsDisk);
914 }
915 if (RT_SUCCESS(rc))
916 {
917 rc = VDCreate(pThis->pVDIfsDisk, &pThis->pDisk);
918 /* Error message is already set correctly. */
919 }
920 }
921
922 while (pCurNode && RT_SUCCESS(rc))
923 {
924 /* Allocate per-image data. */
925 PVBOXIMAGE pImage = drvvdNewImage(pThis);
926 if (!pImage)
927 {
928 rc = VERR_NO_MEMORY;
929 break;
930 }
931
932 /*
933 * Read the image configuration.
934 */
935 rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
936 if (RT_FAILURE(rc))
937 {
938 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
939 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
940 break;
941 }
942
943 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Format", &pszFormat);
944 if (RT_FAILURE(rc))
945 {
946 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
947 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
948 break;
949 }
950
951 if (iLevel == 0)
952 {
953 rc = CFGMR3QueryBool(pCurNode, "ReadOnly", &fReadOnly);
954 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
955 fReadOnly = false;
956 else if (RT_FAILURE(rc))
957 {
958 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
959 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
960 break;
961 }
962
963 rc = CFGMR3QueryBool(pCfgHandle, "HonorZeroWrites", &fHonorZeroWrites);
964 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
965 fHonorZeroWrites = false;
966 else if (RT_FAILURE(rc))
967 {
968 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
969 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
970 break;
971 }
972 }
973 else
974 {
975 fReadOnly = true;
976 fHonorZeroWrites = false;
977 }
978
979 PCFGMNODE pCfg = CFGMR3GetChild(pCurNode, "VDConfig");
980 rc = VDInterfaceAdd(&pImage->VDIConfig, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
981 &pThis->VDIConfigCallbacks, pCfg, &pImage->pVDIfsImage);
982 AssertRC(rc);
983
984 /*
985 * Open the image.
986 */
987 unsigned uOpenFlags;
988 if (fReadOnly)
989 uOpenFlags = VD_OPEN_FLAGS_READONLY;
990 else
991 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
992 if (fHonorZeroWrites)
993 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
994 if (pThis->pDrvMediaAsyncPort)
995 uOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
996
997 /** Try to open backend in asyc I/O mode first. */
998 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
999 if (rc == VERR_NOT_SUPPORTED)
1000 {
1001 /* Seems async I/O is not supported by the backend, open in normal mode. */
1002 uOpenFlags &= ~VD_OPEN_FLAGS_ASYNC_IO;
1003 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
1004 }
1005
1006 if (RT_SUCCESS(rc))
1007 Log(("%s: %d - Opened '%s' in %s mode\n", __FUNCTION__,
1008 iLevel, pszName,
1009 VDIsReadOnly(pThis->pDisk) ? "read-only" : "read-write"));
1010 else
1011 {
1012 rc = PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1013 N_("Failed to open image '%s' in %s mode rc=%Rrc\n"), pszName,
1014 (uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "readonly" : "read-write", rc);
1015 break;
1016 }
1017
1018
1019 MMR3HeapFree(pszName);
1020 pszName = NULL;
1021 MMR3HeapFree(pszFormat);
1022 pszFormat = NULL;
1023
1024 /* next */
1025 iLevel--;
1026 pCurNode = CFGMR3GetParent(pCurNode);
1027 }
1028
1029 if (RT_FAILURE(rc))
1030 {
1031 if (VALID_PTR(pThis->pDisk))
1032 {
1033 VDDestroy(pThis->pDisk);
1034 pThis->pDisk = NULL;
1035 }
1036 drvvdFreeImages(pThis);
1037 if (VALID_PTR(pszName))
1038 MMR3HeapFree(pszName);
1039 if (VALID_PTR(pszFormat))
1040 MMR3HeapFree(pszFormat);
1041
1042 return rc;
1043 }
1044 else
1045 {
1046 /*
1047 * Check if every opened image supports async I/O.
1048 * If not we revert to non async I/O.
1049 */
1050 if (pThis->fAsyncIOSupported)
1051 {
1052 for (unsigned i = 0; i < VDGetCount(pThis->pDisk); i++)
1053 {
1054 VDBACKENDINFO vdBackendInfo;
1055
1056 rc = VDBackendInfoSingle(pThis->pDisk, i, &vdBackendInfo);
1057 AssertRC(rc);
1058
1059 if (vdBackendInfo.uBackendCaps & VD_CAP_ASYNC)
1060 {
1061 /*
1062 * Backend indicates support for at least some files.
1063 * Check if current file is supported with async I/O)
1064 */
1065 rc = VDImageIsAsyncIOSupported(pThis->pDisk, i, &pThis->fAsyncIOSupported);
1066 AssertRC(rc);
1067
1068 /*
1069 * Check if current image is supported.
1070 * If not we can stop checking because
1071 * at least one does not support it.
1072 */
1073 if (!pThis->fAsyncIOSupported)
1074 break;
1075 }
1076 else
1077 {
1078 pThis->fAsyncIOSupported = false;
1079 break;
1080 }
1081 }
1082 }
1083
1084 /*
1085 * We know definitly if async I/O is supported now.
1086 * Create cache if it is supported.
1087 */
1088 if (pThis->fAsyncIOSupported)
1089 {
1090 rc = RTCacheCreate(&pThis->pCache, 0, sizeof(DRVVDASYNCTASK), RTOBJCACHE_PROTECT_INSERT);
1091 AssertMsgRC(rc, ("Failed to create cache rc=%Rrc\n", rc));
1092 }
1093 }
1094
1095 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
1096 return rc;
1097}
1098
1099/**
1100 * Destruct a driver instance.
1101 *
1102 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1103 * resources can be freed correctly.
1104 *
1105 * @param pDrvIns The driver instance data.
1106 */
1107static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
1108{
1109 int rc;
1110 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1111 LogFlow(("%s:\n", __FUNCTION__));
1112
1113 drvvdFreeImages(pThis);
1114 if (pThis->pCache)
1115 {
1116 rc = RTCacheDestroy(pThis->pCache);
1117 AssertRC(rc);
1118 }
1119}
1120
1121
1122/**
1123 * When the VM has been suspended we'll change the image mode to read-only
1124 * so that main and others can read the VDIs. This is important when
1125 * saving state and so forth.
1126 *
1127 * @param pDrvIns The driver instance data.
1128 */
1129static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
1130{
1131 LogFlow(("%s:\n", __FUNCTION__));
1132 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1133 if (!VDIsReadOnly(pThis->pDisk))
1134 {
1135 unsigned uOpenFlags;
1136 int rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
1137 AssertRC(rc);
1138 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
1139 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
1140 AssertRC(rc);
1141 pThis->fTempReadOnly = true;
1142 }
1143}
1144
1145/**
1146 * Before the VM resumes we'll have to undo the read-only mode change
1147 * done in drvvdSuspend.
1148 *
1149 * @param pDrvIns The driver instance data.
1150 */
1151static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
1152{
1153 LogFlow(("%s:\n", __FUNCTION__));
1154 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1155 if (pThis->fTempReadOnly)
1156 {
1157 unsigned uOpenFlags;
1158 int rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
1159 AssertRC(rc);
1160 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
1161 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
1162 AssertRC(rc);
1163 pThis->fTempReadOnly = false;
1164 }
1165}
1166
1167static DECLCALLBACK(void) drvvdPowerOff(PPDMDRVINS pDrvIns)
1168{
1169 LogFlow(("%s:\n", __FUNCTION__));
1170 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1171
1172 /*
1173 * We must close the disk here to ensure that
1174 * the backend closes all files before the
1175 * async transport driver is destructed.
1176 */
1177 int rc = VDCloseAll(pThis->pDisk);
1178 AssertRC(rc);
1179}
1180
1181/**
1182 * VBox disk container media driver registration record.
1183 */
1184const PDMDRVREG g_DrvVD =
1185{
1186 /* u32Version */
1187 PDM_DRVREG_VERSION,
1188 /* szDriverName */
1189 "VD",
1190 /* pszDescription */
1191 "Generic VBox disk media driver.",
1192 /* fFlags */
1193 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1194 /* fClass. */
1195 PDM_DRVREG_CLASS_MEDIA,
1196 /* cMaxInstances */
1197 ~0,
1198 /* cbInstance */
1199 sizeof(VBOXDISK),
1200 /* pfnConstruct */
1201 drvvdConstruct,
1202 /* pfnDestruct */
1203 drvvdDestruct,
1204 /* pfnIOCtl */
1205 NULL,
1206 /* pfnPowerOn */
1207 NULL,
1208 /* pfnReset */
1209 NULL,
1210 /* pfnSuspend */
1211 drvvdSuspend,
1212 /* pfnResume */
1213 drvvdResume,
1214 /* pfnDetach */
1215 NULL,
1216 /* pfnPowerOff */
1217 drvvdPowerOff
1218};
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