VirtualBox

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

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

Storage: Eradicated the last bits using the old VDI only backend, keeping only the testcases for now (no longer built).

Completely removed old iSCSI driver.

Added intnet option to addiscsidisk and adjusted documentation.

Made backend name comparisons case-insensitive.

Detect VMDK files not according to VMDK 1.0 and reject with clear error message.

Changed format probing logic to not fall through to the "unsupported" case if it's a known format, i.e. has valid header.

VBoxManage converthd generic format converter made official.

Added format flag to VBoxManage createhd, allows creating VMDK files.

VBoxManage convertdd reimplemented based on new framework, supporting any image format.

VBoxManage internalcommands sethduuid reimplemented based on new framework, supporting any image format.

Cleaned up error codes.

  • 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 15366 2008-12-12 13:50:32Z 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_VD_GEOMETRY_NOT_SET)
555 rc = VERR_PDM_GEOMETRY_NOT_SET;
556 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
557 return rc;
558}
559
560/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
561static DECLCALLBACK(int) drvvdBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
562 PPDMMEDIAGEOMETRY pLCHSGeometry)
563{
564 LogFlow(("%s:\n", __FUNCTION__));
565 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
566 int rc = VDGetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
567 if (RT_FAILURE(rc))
568 {
569 Log(("%s: geometry not available.\n", __FUNCTION__));
570 rc = VERR_PDM_GEOMETRY_NOT_SET;
571 }
572 LogFlow(("%s: returns %Rrc (CHS=%d/%d/%d)\n", __FUNCTION__,
573 rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
574 return rc;
575}
576
577/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
578static DECLCALLBACK(int) drvvdBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
579 PCPDMMEDIAGEOMETRY pLCHSGeometry)
580{
581 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
582 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
583 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
584 int rc = VDSetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
585 if (rc == VERR_VD_GEOMETRY_NOT_SET)
586 rc = VERR_PDM_GEOMETRY_NOT_SET;
587 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
588 return rc;
589}
590
591/** @copydoc PDMIMEDIA::pfnGetUuid */
592static DECLCALLBACK(int) drvvdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
593{
594 LogFlow(("%s:\n", __FUNCTION__));
595 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
596 int rc = VDGetUuid(pThis->pDisk, 0, pUuid);
597 LogFlow(("%s: returns %Rrc ({%RTuuid})\n", __FUNCTION__, rc, pUuid));
598 return rc;
599}
600
601/*******************************************************************************
602* Async Media interface methods *
603*******************************************************************************/
604
605static DECLCALLBACK(int) drvvdStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
606 PPDMDATASEG paSeg, unsigned cSeg,
607 size_t cbRead, void *pvUser)
608{
609 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d\n pvUser=%#p", __FUNCTION__,
610 uOffset, paSeg, cSeg, cbRead, pvUser));
611 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
612 int rc = VDAsyncRead(pThis->pDisk, uOffset, cbRead, paSeg, cSeg, pvUser);
613 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
614 return rc;
615}
616
617static DECLCALLBACK(int) drvvdStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
618 PPDMDATASEG paSeg, unsigned cSeg,
619 size_t cbWrite, void *pvUser)
620{
621 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d\n pvUser=%#p", __FUNCTION__,
622 uOffset, paSeg, cSeg, cbWrite, pvUser));
623 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
624 int rc = VDAsyncWrite(pThis->pDisk, uOffset, cbWrite, paSeg, cSeg, pvUser);
625 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
626 return rc;
627}
628
629/*******************************************************************************
630* Async transport port interface methods *
631*******************************************************************************/
632
633static DECLCALLBACK(int) drvvdTasksCompleteNotify(PPDMITRANSPORTASYNCPORT pInterface, void *pvUser)
634{
635 PVBOXDISK pThis = PDMITRANSPORTASYNCPORT_2_VBOXDISK(pInterface);
636 PDRVVDASYNCTASK pDrvVDAsyncTask = (PDRVVDASYNCTASK)pvUser;
637 int rc = VINF_VD_ASYNC_IO_FINISHED;
638
639 /* Having a completion callback for a task is not mandatory. */
640 if (pDrvVDAsyncTask->pfnCompleted)
641 rc = pDrvVDAsyncTask->pfnCompleted(pDrvVDAsyncTask->pvUser);
642
643 /* Check if the request is finished. */
644 if (rc == VINF_VD_ASYNC_IO_FINISHED)
645 {
646 rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort, pDrvVDAsyncTask->pvUserCaller);
647 }
648 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
649 rc = VINF_SUCCESS;
650
651 rc = RTCacheInsert(pThis->pCache, pDrvVDAsyncTask);
652 AssertRC(rc);
653
654 return rc;
655}
656
657
658/*******************************************************************************
659* Base interface methods *
660*******************************************************************************/
661
662/** @copydoc PDMIBASE::pfnQueryInterface */
663static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface,
664 PDMINTERFACE enmInterface)
665{
666 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
667 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
668 switch (enmInterface)
669 {
670 case PDMINTERFACE_BASE:
671 return &pDrvIns->IBase;
672 case PDMINTERFACE_MEDIA:
673 return &pThis->IMedia;
674 case PDMINTERFACE_MEDIA_ASYNC:
675 return pThis->fAsyncIOSupported ? &pThis->IMediaAsync : NULL;
676 case PDMINTERFACE_TRANSPORT_ASYNC_PORT:
677 return &pThis->ITransportAsyncPort;
678 default:
679 return NULL;
680 }
681}
682
683
684/*******************************************************************************
685* Driver methods *
686*******************************************************************************/
687
688
689/**
690 * Construct a VBox disk media driver instance.
691 *
692 * @returns VBox status.
693 * @param pDrvIns The driver instance data.
694 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
695 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
696 * of the driver instance. It's also found in pDrvIns->pCfgHandle as it's expected
697 * to be used frequently in this function.
698 */
699static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
700 PCFGMNODE pCfgHandle)
701{
702 LogFlow(("%s:\n", __FUNCTION__));
703 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
704 int rc = VINF_SUCCESS;
705 char *pszName = NULL; /**< The path of the disk image file. */
706 char *pszFormat = NULL; /**< The format backed to use for this image. */
707 bool fReadOnly; /**< True if the media is readonly. */
708 bool fHonorZeroWrites; /**< True if zero blocks should be written. */
709
710 /*
711 * Init the static parts.
712 */
713 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
714 pThis->pDrvIns = pDrvIns;
715 pThis->fTempReadOnly = false;
716 pThis->pDisk = NULL;
717 pThis->fAsyncIOSupported = false;
718
719 /* IMedia */
720 pThis->IMedia.pfnRead = drvvdRead;
721 pThis->IMedia.pfnWrite = drvvdWrite;
722 pThis->IMedia.pfnFlush = drvvdFlush;
723 pThis->IMedia.pfnGetSize = drvvdGetSize;
724 pThis->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
725 pThis->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
726 pThis->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
727 pThis->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
728 pThis->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
729 pThis->IMedia.pfnGetUuid = drvvdGetUuid;
730
731 /* IMediaAsync */
732 pThis->IMediaAsync.pfnStartRead = drvvdStartRead;
733 pThis->IMediaAsync.pfnStartWrite = drvvdStartWrite;
734
735 /* ITransportAsyncPort */
736 pThis->ITransportAsyncPort.pfnTaskCompleteNotify = drvvdTasksCompleteNotify;
737
738 /* Initialize supported VD interfaces. */
739 pThis->pVDIfsDisk = NULL;
740
741 pThis->VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
742 pThis->VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
743 pThis->VDIErrorCallbacks.pfnError = drvvdErrorCallback;
744
745 rc = VDInterfaceAdd(&pThis->VDIError, "DrvVD_VDIError", VDINTERFACETYPE_ERROR,
746 &pThis->VDIErrorCallbacks, pDrvIns, &pThis->pVDIfsDisk);
747 AssertRC(rc);
748
749 pThis->VDIAsyncIOCallbacks.cbSize = sizeof(VDINTERFACEASYNCIO);
750 pThis->VDIAsyncIOCallbacks.enmInterface = VDINTERFACETYPE_ASYNCIO;
751 pThis->VDIAsyncIOCallbacks.pfnOpen = drvvdAsyncIOOpen;
752 pThis->VDIAsyncIOCallbacks.pfnClose = drvvdAsyncIOClose;
753 pThis->VDIAsyncIOCallbacks.pfnRead = drvvdAsyncIORead;
754 pThis->VDIAsyncIOCallbacks.pfnWrite = drvvdAsyncIOWrite;
755 pThis->VDIAsyncIOCallbacks.pfnFlush = drvvdAsyncIOFlush;
756 pThis->VDIAsyncIOCallbacks.pfnPrepareRead = drvvdAsyncIOPrepareRead;
757 pThis->VDIAsyncIOCallbacks.pfnPrepareWrite = drvvdAsyncIOPrepareWrite;
758 pThis->VDIAsyncIOCallbacks.pfnTasksSubmit = drvvdAsyncIOTasksSubmit;
759
760 rc = VDInterfaceAdd(&pThis->VDIAsyncIO, "DrvVD_AsyncIO", VDINTERFACETYPE_ASYNCIO,
761 &pThis->VDIAsyncIOCallbacks, pThis, &pThis->pVDIfsDisk);
762 AssertRC(rc);
763
764 /* This is just prepared here, the actual interface is per-image, so it's
765 * added later. No need to have separate callback tables. */
766 pThis->VDIConfigCallbacks.cbSize = sizeof(VDINTERFACECONFIG);
767 pThis->VDIConfigCallbacks.enmInterface = VDINTERFACETYPE_CONFIG;
768 pThis->VDIConfigCallbacks.pfnAreKeysValid = drvvdCfgAreKeysValid;
769 pThis->VDIConfigCallbacks.pfnQuerySize = drvvdCfgQuerySize;
770 pThis->VDIConfigCallbacks.pfnQuery = drvvdCfgQuery;
771
772 /* List of images is empty now. */
773 pThis->pImages = NULL;
774
775 /* Try to attach async media port interface above.*/
776 pThis->pDrvMediaAsyncPort = (PPDMIMEDIAASYNCPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_MEDIA_ASYNC_PORT);
777
778 /*
779 * Attach the async transport driver below if the device above us implements the
780 * async interface.
781 */
782 if (pThis->pDrvMediaAsyncPort)
783 {
784 /* Try to attach the driver. */
785 PPDMIBASE pBase;
786
787 rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
788 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
789 {
790 /*
791 * Though the device supports async I/O there is no transport driver
792 * which processes async requests.
793 * Revert to non async I/O.
794 */
795 rc = VINF_SUCCESS;
796 pThis->pDrvMediaAsyncPort = NULL;
797 pThis->fAsyncIOSupported = false;
798 }
799 else if (RT_FAILURE(rc))
800 {
801 AssertMsgFailed(("Failed to attach async transport driver below rc=%Rrc\n", rc));
802 }
803 else
804 {
805 /*
806 * The device supports async I/O and we successfully attached the transport driver.
807 * Indicate that async I/O is supported for now as we check if the image backend supports
808 * it later.
809 */
810 pThis->fAsyncIOSupported = true;
811
812 /* Success query the async transport interface. */
813 pThis->pDrvTransportAsync = (PPDMITRANSPORTASYNC)pBase->pfnQueryInterface(pBase, PDMINTERFACE_TRANSPORT_ASYNC);
814 if (!pThis->pDrvTransportAsync)
815 {
816 /* An attached driver without an async transport interface - impossible. */
817 AssertMsgFailed(("Configuration error: No async transport interface below!\n"));
818 return VERR_PDM_MISSING_INTERFACE_ABOVE;
819 }
820 }
821 }
822
823 /*
824 * Validate configuration and find all parent images.
825 * It's sort of up side down from the image dependency tree.
826 */
827 bool fHostIP = false;
828 unsigned iLevel = 0;
829 PCFGMNODE pCurNode = pCfgHandle;
830
831 for (;;)
832 {
833 bool fValid;
834
835 if (pCurNode == pCfgHandle)
836 {
837 /* Toplevel configuration additionally contains the global image
838 * open flags. Some might be converted to per-image flags later. */
839 fValid = CFGMR3AreValuesValid(pCurNode,
840 "Format\0Path\0"
841 "ReadOnly\0HonorZeroWrites\0"
842 "HostIPStack\0");
843
844 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