VirtualBox

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

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

Storage/DrvVD: fix status codes to match the function specs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.9 KB
Line 
1/* $Id: DrvVD.cpp 15156 2008-12-09 11:04:53Z 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 if (rc == VERR_VDI_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_VDI_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_VDI_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_VDI_ASYNC_IO_FINISHED)
645 {
646 rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort, pDrvVDAsyncTask->pvUserCaller);
647 }
648 else if (rc == VERR_VDI_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 rc = CFGMR3QueryBool(pCfgHandle, "HostIPStack", &fHostIP);
845 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
846 {
847 fHostIP = true;
848 rc = VINF_SUCCESS;
849 }
850 else if (RT_FAILURE(rc))
851 {
852 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
853 N_("DrvVD: Configuration error: Querying \"HostIPStack\" as boolean failed"));
854 break;
855 }
856 }
857 else
858 {
859 /* All other image configurations only contain image name and
860 * the format information. */
861 fValid = CFGMR3AreValuesValid(pCurNode, "Format\0Path\0");
862 }
863 if (!fValid)
864 {
865 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
866 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
867 break;
868 }
869
870 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
871 if (!pParent)
872 break;
873 pCurNode = pParent;
874 iLevel++;
875 }
876
877 /*
878 * Open the images.
879 */
880 if (RT_SUCCESS(rc))
881 {
882 /* First of all figure out what kind of TCP networking stack interface
883 * to use. This is done unconditionally, as backends which don't need
884 * it will just ignore it. */
885 if (fHostIP)
886 {
887 pThis->VDITcpNetCallbacks.cbSize = sizeof(VDINTERFACETCPNET);
888 pThis->VDITcpNetCallbacks.enmInterface = VDINTERFACETYPE_TCPNET;
889 pThis->VDITcpNetCallbacks.pfnClientConnect = RTTcpClientConnect;
890 pThis->VDITcpNetCallbacks.pfnClientClose = RTTcpClientClose;
891 pThis->VDITcpNetCallbacks.pfnSelectOne = RTTcpSelectOne;
892 pThis->VDITcpNetCallbacks.pfnRead = RTTcpRead;
893 pThis->VDITcpNetCallbacks.pfnWrite = RTTcpWrite;
894 pThis->VDITcpNetCallbacks.pfnFlush = RTTcpFlush;
895 }
896 else
897 {
898#ifdef VBOX_OSE
899 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
900 RT_SRC_POS, N_("DrvVD: Configuration error: TCP over Internal Networking not supported in VirtualBox OSE"));
901#else /* !VBOX_OSE */
902 pThis->VDITcpNetCallbacks.cbSize = sizeof(VDINTERFACETCPNET);
903 pThis->VDITcpNetCallbacks.enmInterface = VDINTERFACETYPE_TCPNET;
904 pThis->VDITcpNetCallbacks.pfnClientConnect = drvvdINIPClientConnect;
905 pThis->VDITcpNetCallbacks.pfnClientClose = drvvdINIPClientClose;
906 pThis->VDITcpNetCallbacks.pfnSelectOne = drvvdINIPSelectOne;
907 pThis->VDITcpNetCallbacks.pfnRead = drvvdINIPRead;
908 pThis->VDITcpNetCallbacks.pfnWrite = drvvdINIPWrite;
909 pThis->VDITcpNetCallbacks.pfnFlush = drvvdINIPFlush;
910#endif /* !VBOX_OSE */
911 }
912 if (RT_SUCCESS(rc))
913 {
914 rc = VDInterfaceAdd(&pThis->VDITcpNet, "DrvVD_INIP",
915 VDINTERFACETYPE_TCPNET,
916 &pThis->VDITcpNetCallbacks, NULL,
917 &pThis->pVDIfsDisk);
918 }
919 if (RT_SUCCESS(rc))
920 {
921 rc = VDCreate(pThis->pVDIfsDisk, &pThis->pDisk);
922 /* Error message is already set correctly. */
923 }
924 }
925
926 while (pCurNode && RT_SUCCESS(rc))
927 {
928 /* Allocate per-image data. */
929 PVBOXIMAGE pImage = drvvdNewImage(pThis);
930 if (!pImage)
931 {
932 rc = VERR_NO_MEMORY;
933 break;
934 }
935
936 /*
937 * Read the image configuration.
938 */
939 rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
940 if (RT_FAILURE(rc))
941 {
942 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
943 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
944 break;
945 }
946
947 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Format", &pszFormat);
948 if (RT_FAILURE(rc))
949 {
950 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
951 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
952 break;
953 }
954
955 if (iLevel == 0)
956 {
957 rc = CFGMR3QueryBool(pCurNode, "ReadOnly", &fReadOnly);
958 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
959 fReadOnly = false;
960 else if (RT_FAILURE(rc))
961 {
962 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
963 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
964 break;
965 }
966
967 rc = CFGMR3QueryBool(pCfgHandle, "HonorZeroWrites", &fHonorZeroWrites);
968 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
969 fHonorZeroWrites = false;
970 else if (RT_FAILURE(rc))
971 {
972 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
973 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
974 break;
975 }
976 }
977 else
978 {
979 fReadOnly = true;
980 fHonorZeroWrites = false;
981 }
982
983 PCFGMNODE pCfg = CFGMR3GetChild(pCurNode, "VDConfig");
984 rc = VDInterfaceAdd(&pImage->VDIConfig, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
985 &pThis->VDIConfigCallbacks, pCfg, &pImage->pVDIfsImage);
986 AssertRC(rc);
987
988 /*
989 * Open the image.
990 */
991 unsigned uOpenFlags;
992 if (fReadOnly)
993 uOpenFlags = VD_OPEN_FLAGS_READONLY;
994 else
995 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
996 if (fHonorZeroWrites)
997 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
998 if (pThis->pDrvMediaAsyncPort)
999 uOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
1000
1001 /** Try to open backend in asyc I/O mode first. */
1002 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
1003 if (rc == VERR_NOT_SUPPORTED)
1004 {
1005 /* Seems async I/O is not supported by the backend, open in normal mode. */
1006 uOpenFlags &= ~VD_OPEN_FLAGS_ASYNC_IO;
1007 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
1008 }
1009
1010 if (RT_SUCCESS(rc))
1011 Log(("%s: %d - Opened '%s' in %s mode\n", __FUNCTION__,
1012 iLevel, pszName,
1013 VDIsReadOnly(pThis->pDisk) ? "read-only" : "read-write"));
1014 else
1015 {
1016 rc = PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1017 N_("Failed to open image '%s' in %s mode rc=%Rrc\n"), pszName,
1018 (uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "readonly" : "read-write", rc);
1019 break;
1020 }
1021
1022
1023 MMR3HeapFree(pszName);
1024 pszName = NULL;
1025 MMR3HeapFree(pszFormat);
1026 pszFormat = NULL;
1027
1028 /* next */
1029 iLevel--;
1030 pCurNode = CFGMR3GetParent(pCurNode);
1031 }
1032
1033 if (RT_FAILURE(rc))
1034 {
1035 if (VALID_PTR(pThis->pDisk))
1036 {
1037 VDDestroy(pThis->pDisk);
1038 pThis->pDisk = NULL;
1039 }
1040 drvvdFreeImages(pThis);
1041 if (VALID_PTR(pszName))
1042 MMR3HeapFree(pszName);
1043 if (VALID_PTR(pszFormat))
1044 MMR3HeapFree(pszFormat);
1045
1046 return rc;
1047 }
1048 else
1049 {
1050 /*
1051 * Check if every opened image supports async I/O.
1052 * If not we revert to non async I/O.
1053 */
1054 if (pThis->fAsyncIOSupported)
1055 {
1056 for (unsigned i = 0; i < VDGetCount(pThis->pDisk); i++)
1057 {
1058 VDBACKENDINFO vdBackendInfo;
1059
1060 rc = VDBackendInfoSingle(pThis->pDisk, i, &vdBackendInfo);
1061 AssertRC(rc);
1062
1063 if (vdBackendInfo.uBackendCaps & VD_CAP_ASYNC)
1064 {
1065 /*
1066 * Backend indicates support for at least some files.
1067 * Check if current file is supported with async I/O)
1068 */
1069 rc = VDImageIsAsyncIOSupported(pThis->pDisk, i, &pThis->fAsyncIOSupported);
1070 AssertRC(rc);
1071
1072 /*
1073 * Check if current image is supported.
1074 * If not we can stop checking because
1075 * at least one does not support it.
1076 */
1077 if (!pThis->fAsyncIOSupported)
1078 break;
1079 }
1080 else
1081 {
1082 pThis->fAsyncIOSupported = false;
1083 break;
1084 }
1085 }
1086 }
1087
1088 /*
1089 * We know definitly if async I/O is supported now.
1090 * Create cache if it is supported.
1091 */
1092 if (pThis->fAsyncIOSupported)
1093 {
1094 rc = RTCacheCreate(&pThis->pCache, 0, sizeof(DRVVDASYNCTASK), RTOBJCACHE_PROTECT_INSERT);
1095 AssertMsgRC(rc, ("Failed to create cache rc=%Rrc\n", rc));
1096 }
1097 }
1098
1099 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
1100 return rc;
1101}
1102
1103/**
1104 * Destruct a driver instance.
1105 *
1106 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1107 * resources can be freed correctly.
1108 *
1109 * @param pDrvIns The driver instance data.
1110 */
1111static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
1112{
1113 int rc;
1114 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1115 LogFlow(("%s:\n", __FUNCTION__));
1116
1117 drvvdFreeImages(pThis);
1118 if (pThis->pCache)
1119 {
1120 rc = RTCacheDestroy(pThis->pCache);
1121 AssertRC(rc);
1122 }
1123}
1124
1125
1126/**
1127 * When the VM has been suspended we'll change the image mode to read-only
1128 * so that main and others can read the VDIs. This is important when
1129 * saving state and so forth.
1130 *
1131 * @param pDrvIns The driver instance data.
1132 */
1133static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
1134{
1135 LogFlow(("%s:\n", __FUNCTION__));
1136 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1137 if (!VDIsReadOnly(pThis->pDisk))
1138 {
1139 unsigned uOpenFlags;
1140 int rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
1141 AssertRC(rc);
1142 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
1143 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
1144 AssertRC(rc);
1145 pThis->fTempReadOnly = true;
1146 }
1147}
1148
1149/**
1150 * Before the VM resumes we'll have to undo the read-only mode change
1151 * done in drvvdSuspend.
1152 *
1153 * @param pDrvIns The driver instance data.
1154 */
1155static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
1156{
1157 LogFlow(("%s:\n", __FUNCTION__));
1158 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1159 if (pThis->fTempReadOnly)
1160 {
1161 unsigned uOpenFlags;
1162 int rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
1163 AssertRC(rc);
1164 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
1165 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
1166 AssertRC(rc);
1167 pThis->fTempReadOnly = false;
1168 }
1169}
1170
1171static DECLCALLBACK(void) drvvdPowerOff(PPDMDRVINS pDrvIns)
1172{
1173 LogFlow(("%s:\n", __FUNCTION__));
1174 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1175
1176 /*
1177 * We must close the disk here to ensure that
1178 * the backend closes all files before the
1179 * async transport driver is destructed.
1180 */
1181 int rc = VDCloseAll(pThis->pDisk);
1182 AssertRC(rc);
1183}
1184
1185/**
1186 * VBox disk container media driver registration record.
1187 */
1188const PDMDRVREG g_DrvVD =
1189{
1190 /* u32Version */
1191 PDM_DRVREG_VERSION,
1192 /* szDriverName */
1193 "VD",
1194 /* pszDescription */
1195 "Generic VBox disk media driver.",
1196 /* fFlags */
1197 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1198 /* fClass. */
1199 PDM_DRVREG_CLASS_MEDIA,
1200 /* cMaxInstances */
1201 ~0,
1202 /* cbInstance */
1203 sizeof(VBOXDISK),
1204 /* pfnConstruct */
1205 drvvdConstruct,
1206 /* pfnDestruct */
1207 drvvdDestruct,
1208 /* pfnIOCtl */
1209 NULL,
1210 /* pfnPowerOn */
1211 NULL,
1212 /* pfnReset */
1213 NULL,
1214 /* pfnSuspend */
1215 drvvdSuspend,
1216 /* pfnResume */
1217 drvvdResume,
1218 /* pfnDetach */
1219 NULL,
1220 /* pfnPowerOff */
1221 drvvdPowerOff
1222};
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