VirtualBox

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

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

Devices/Storage: iSCSI stuff, including bringing IntNet support back to life. Should build, but otherwise untested.

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