VirtualBox

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

Last change on this file since 25966 was 25966, checked in by vboxsync, 15 years ago

PDMIBASE refactoring; use UUID as interface IDs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.1 KB
Line 
1/* $Id: DrvVD.cpp 25966 2010-01-22 11:15:43Z 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.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DRV_VD
27#include <VBox/VBoxHDD.h>
28#include <VBox/pdmdrv.h>
29#include <VBox/pdmasynccompletion.h>
30#include <iprt/alloc.h>
31#include <iprt/assert.h>
32#include <iprt/uuid.h>
33#include <iprt/file.h>
34#include <iprt/string.h>
35#include <iprt/cache.h>
36#include <iprt/tcp.h>
37#include <iprt/semaphore.h>
38
39#ifdef VBOX_WITH_INIP
40/* All lwip header files are not C++ safe. So hack around this. */
41RT_C_DECLS_BEGIN
42#include <lwip/inet.h>
43#include <lwip/tcp.h>
44#include <lwip/sockets.h>
45RT_C_DECLS_END
46#endif /* VBOX_WITH_INIP */
47
48#include "Builtins.h"
49
50#ifdef VBOX_WITH_INIP
51/* Small hack to get at lwIP initialized status */
52extern bool DevINIPConfigured(void);
53#endif /* VBOX_WITH_INIP */
54
55
56/*******************************************************************************
57* Defined types, constants and macros *
58*******************************************************************************/
59
60/** Converts a pointer to VBOXDISK::IMedia to a PVBOXDISK. */
61#define PDMIMEDIA_2_VBOXDISK(pInterface) \
62 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMedia)) )
63
64/** Converts a pointer to PDMDRVINS::IBase to a PPDMDRVINS. */
65#define PDMIBASE_2_DRVINS(pInterface) \
66 ( (PPDMDRVINS)((uintptr_t)pInterface - RT_OFFSETOF(PDMDRVINS, IBase)) )
67
68/** Converts a pointer to PDMDRVINS::IBase to a PVBOXDISK. */
69#define PDMIBASE_2_VBOXDISK(pInterface) \
70 ( PDMINS_2_DATA(PDMIBASE_2_DRVINS(pInterface), PVBOXDISK) )
71
72/** Converts a pointer to VBOXDISK::IMediaAsync to a PVBOXDISK. */
73#define PDMIMEDIAASYNC_2_VBOXDISK(pInterface) \
74 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMediaAsync)) )
75
76/**
77 * VBox disk container, image information, private part.
78 */
79
80typedef struct VBOXIMAGE
81{
82 /** Pointer to next image. */
83 struct VBOXIMAGE *pNext;
84 /** Pointer to list of VD interfaces. Per-image. */
85 PVDINTERFACE pVDIfsImage;
86 /** Common structure for the configuration information interface. */
87 VDINTERFACE VDIConfig;
88} VBOXIMAGE, *PVBOXIMAGE;
89
90/**
91 * Storage backend data.
92 */
93typedef struct DRVVDSTORAGEBACKEND
94{
95 /** PDM async completion end point. */
96 PPDMASYNCCOMPLETIONENDPOINT pEndpoint;
97 /** The template. */
98 PPDMASYNCCOMPLETIONTEMPLATE pTemplate;
99 /** Event semaphore for synchronous operations. */
100 RTSEMEVENT EventSem;
101 /** Flag whether a synchronous operation is currently pending. */
102 volatile bool fSyncIoPending;
103 /** Callback routine */
104 PFNVDCOMPLETED pfnCompleted;
105} DRVVDSTORAGEBACKEND, *PDRVVDSTORAGEBACKEND;
106
107/**
108 * VBox disk container media main structure, private part.
109 *
110 * @implements PDMIMEDIA
111 * @implements PDMIMEDIAASYNC
112 * @implements VDINTERFACEERROR
113 * @implements VDINTERFACETCPNET
114 * @implements VDINTERFACEASYNCIO
115 * @implements VDINTERFACECONFIG
116 */
117typedef struct VBOXDISK
118{
119 /** The VBox disk container. */
120 PVBOXHDD pDisk;
121 /** The media interface. */
122 PDMIMEDIA IMedia;
123 /** Pointer to the driver instance. */
124 PPDMDRVINS pDrvIns;
125 /** Flag whether suspend has changed image open mode to read only. */
126 bool fTempReadOnly;
127 /** Flag whether to use the runtime (true) or startup error facility. */
128 bool fErrorUseRuntime;
129 /** Pointer to list of VD interfaces. Per-disk. */
130 PVDINTERFACE pVDIfsDisk;
131 /** Common structure for the supported error interface. */
132 VDINTERFACE VDIError;
133 /** Callback table for error interface. */
134 VDINTERFACEERROR VDIErrorCallbacks;
135 /** Common structure for the supported TCP network stack interface. */
136 VDINTERFACE VDITcpNet;
137 /** Callback table for TCP network stack interface. */
138 VDINTERFACETCPNET VDITcpNetCallbacks;
139 /** Common structure for the supported async I/O interface. */
140 VDINTERFACE VDIAsyncIO;
141 /** Callback table for async I/O interface. */
142 VDINTERFACEASYNCIO VDIAsyncIOCallbacks;
143 /** Callback table for the configuration information interface. */
144 VDINTERFACECONFIG VDIConfigCallbacks;
145 /** Flag whether opened disk suppports async I/O operations. */
146 bool fAsyncIOSupported;
147 /** The async media interface. */
148 PDMIMEDIAASYNC IMediaAsync;
149 /** The async media port interface above. */
150 PPDMIMEDIAASYNCPORT pDrvMediaAsyncPort;
151 /** Pointer to the list of data we need to keep per image. */
152 PVBOXIMAGE pImages;
153} VBOXDISK, *PVBOXDISK;
154
155
156/*******************************************************************************
157* Internal Functions *
158*******************************************************************************/
159
160/**
161 * Internal: allocate new image descriptor and put it in the list
162 */
163static PVBOXIMAGE drvvdNewImage(PVBOXDISK pThis)
164{
165 AssertPtr(pThis);
166 PVBOXIMAGE pImage = (PVBOXIMAGE)RTMemAllocZ(sizeof(VBOXIMAGE));
167 if (pImage)
168 {
169 pImage->pVDIfsImage = NULL;
170 PVBOXIMAGE *pp = &pThis->pImages;
171 while (*pp != NULL)
172 pp = &(*pp)->pNext;
173 *pp = pImage;
174 pImage->pNext = NULL;
175 }
176
177 return pImage;
178}
179
180/**
181 * Internal: free the list of images descriptors.
182 */
183static void drvvdFreeImages(PVBOXDISK pThis)
184{
185 while (pThis->pImages != NULL)
186 {
187 PVBOXIMAGE p = pThis->pImages;
188 pThis->pImages = pThis->pImages->pNext;
189 RTMemFree(p);
190 }
191}
192
193
194/**
195 * Undo the temporary read-only status of the image.
196 *
197 * @returns VBox status code.
198 * @param pThis The driver instance data.
199 */
200static int drvvdSetWritable(PVBOXDISK pThis)
201{
202 int rc = VINF_SUCCESS;
203 if (pThis->fTempReadOnly)
204 {
205 unsigned uOpenFlags;
206 rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
207 AssertRC(rc);
208 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
209 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
210 if (RT_SUCCESS(rc))
211 pThis->fTempReadOnly = false;
212 else
213 AssertRC(rc);
214 }
215 return rc;
216}
217
218
219/*******************************************************************************
220* Error reporting callback *
221*******************************************************************************/
222
223static void drvvdErrorCallback(void *pvUser, int rc, RT_SRC_POS_DECL,
224 const char *pszFormat, va_list va)
225{
226 PPDMDRVINS pDrvIns = (PPDMDRVINS)pvUser;
227 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
228 if (pThis->fErrorUseRuntime)
229 /* We must not pass VMSETRTERR_FLAGS_FATAL as it could lead to a
230 * deadlock: We are probably executed in a thread context != EMT
231 * and the EM thread would wait until every thread is suspended
232 * but we would wait for the EM thread ... */
233
234 PDMDrvHlpVMSetRuntimeErrorV(pDrvIns, /* fFlags=*/ 0, "DrvVD", pszFormat, va);
235 else
236 PDMDrvHlpVMSetErrorV(pDrvIns, rc, RT_SRC_POS_ARGS, pszFormat, va);
237}
238
239/*******************************************************************************
240* VD Async I/O interface implementation *
241*******************************************************************************/
242
243#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
244
245static DECLCALLBACK(void) drvvdAsyncTaskCompleted(PPDMDRVINS pDrvIns, void *pvTemplateUser, void *pvUser)
246{
247 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
248 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pvTemplateUser;
249
250 if (pStorageBackend->fSyncIoPending)
251 {
252 pStorageBackend->fSyncIoPending = false;
253 RTSemEventSignal(pStorageBackend->EventSem);
254 }
255 else
256 {
257 int rc = VINF_VD_ASYNC_IO_FINISHED;
258 void *pvCallerUser = NULL;
259
260 if (pStorageBackend->pfnCompleted)
261 rc = pStorageBackend->pfnCompleted(pvUser, &pvCallerUser);
262 else
263 pvCallerUser = pvUser;
264
265 if (rc == VINF_VD_ASYNC_IO_FINISHED)
266 {
267 rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort, pvCallerUser);
268 AssertRC(rc);
269 }
270 else
271 AssertMsg(rc == VERR_VD_ASYNC_IO_IN_PROGRESS, ("Invalid return code from disk backend rc=%Rrc\n", rc));
272 }
273}
274
275static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation, unsigned uOpenFlags,
276 PFNVDCOMPLETED pfnCompleted, void **ppStorage)
277{
278 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
279 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)RTMemAllocZ(sizeof(DRVVDSTORAGEBACKEND));
280 int rc = VINF_SUCCESS;
281
282 if (pStorageBackend)
283 {
284 pStorageBackend->fSyncIoPending = false;
285 pStorageBackend->pfnCompleted = pfnCompleted;
286
287 rc = RTSemEventCreate(&pStorageBackend->EventSem);
288 if (RT_SUCCESS(rc))
289 {
290 rc = PDMDrvHlpPDMAsyncCompletionTemplateCreate(pDrvVD->pDrvIns, &pStorageBackend->pTemplate,
291 drvvdAsyncTaskCompleted, pStorageBackend, "AsyncTaskCompleted");
292 if (RT_SUCCESS(rc))
293 {
294 rc = PDMR3AsyncCompletionEpCreateForFile(&pStorageBackend->pEndpoint, pszLocation,
295 uOpenFlags & VD_INTERFACEASYNCIO_OPEN_FLAGS_READONLY
296 ? PDMACEP_FILE_FLAGS_READ_ONLY | PDMACEP_FILE_FLAGS_CACHING
297 : PDMACEP_FILE_FLAGS_CACHING,
298 pStorageBackend->pTemplate);
299 if (RT_SUCCESS(rc))
300 {
301 *ppStorage = pStorageBackend;
302 return VINF_SUCCESS;
303 }
304
305 PDMR3AsyncCompletionTemplateDestroy(pStorageBackend->pTemplate);
306 }
307 RTSemEventDestroy(pStorageBackend->EventSem);
308 }
309 RTMemFree(pStorageBackend);
310 }
311 else
312 rc = VERR_NO_MEMORY;
313
314 return rc;
315}
316
317static DECLCALLBACK(int) drvvdAsyncIOClose(void *pvUser, void *pStorage)
318{
319 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
320 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
321
322 PDMR3AsyncCompletionEpClose(pStorageBackend->pEndpoint);
323 PDMR3AsyncCompletionTemplateDestroy(pStorageBackend->pTemplate);
324 RTSemEventDestroy(pStorageBackend->EventSem);
325 RTMemFree(pStorageBackend);
326
327 return VINF_SUCCESS;;
328}
329
330static DECLCALLBACK(int) drvvdAsyncIOGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
331{
332 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
333 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
334
335 return PDMR3AsyncCompletionEpGetSize(pStorageBackend->pEndpoint, pcbSize);
336}
337
338static DECLCALLBACK(int) drvvdAsyncIOSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
339{
340 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
341 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
342
343 return VERR_NOT_SUPPORTED;
344}
345
346static DECLCALLBACK(int) drvvdAsyncIOReadSync(void *pvUser, void *pStorage, uint64_t uOffset,
347 size_t cbRead, void *pvBuf, size_t *pcbRead)
348{
349 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
350 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
351 PDMDATASEG DataSeg;
352 PPDMASYNCCOMPLETIONTASK pTask;
353
354 Assert(!pStorageBackend->fSyncIoPending);
355 pStorageBackend->fSyncIoPending = true;
356 DataSeg.cbSeg = cbRead;
357 DataSeg.pvSeg = pvBuf;
358
359 int rc = PDMR3AsyncCompletionEpRead(pStorageBackend->pEndpoint, uOffset, &DataSeg, 1, cbRead, NULL, &pTask);
360 if (RT_FAILURE(rc))
361 return rc;
362
363 /* Wait */
364 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
365 AssertRC(rc);
366
367 if (pcbRead)
368 *pcbRead = cbRead;
369
370 return VINF_SUCCESS;
371}
372
373static DECLCALLBACK(int) drvvdAsyncIOWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
374 size_t cbWrite, const void *pvBuf, size_t *pcbWritten)
375{
376 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
377 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
378 PDMDATASEG DataSeg;
379 PPDMASYNCCOMPLETIONTASK pTask;
380
381 Assert(!pStorageBackend->fSyncIoPending);
382 pStorageBackend->fSyncIoPending = true;
383 DataSeg.cbSeg = cbWrite;
384 DataSeg.pvSeg = (void *)pvBuf;
385
386 int rc = PDMR3AsyncCompletionEpWrite(pStorageBackend->pEndpoint, uOffset, &DataSeg, 1, cbWrite, NULL, &pTask);
387 if (RT_FAILURE(rc))
388 return rc;
389
390 /* Wait */
391 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
392 AssertRC(rc);
393
394 if (pcbWritten)
395 *pcbWritten = cbWrite;
396
397 return VINF_SUCCESS;
398}
399
400static DECLCALLBACK(int) drvvdAsyncIOFlushSync(void *pvUser, void *pStorage)
401{
402 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
403 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
404 PPDMASYNCCOMPLETIONTASK pTask;
405
406 Assert(!pStorageBackend->fSyncIoPending);
407 pStorageBackend->fSyncIoPending = true;
408
409 int rc = PDMR3AsyncCompletionEpFlush(pStorageBackend->pEndpoint, NULL, &pTask);
410 if (RT_FAILURE(rc))
411 return rc;
412
413 /* Wait */
414 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
415 AssertRC(rc);
416
417 return VINF_SUCCESS;
418}
419
420static DECLCALLBACK(int) drvvdAsyncIOReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
421 PCPDMDATASEG paSegments, size_t cSegments,
422 size_t cbRead, void *pvCompletion,
423 void **ppTask)
424{
425 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
426 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
427
428 return PDMR3AsyncCompletionEpRead(pStorageBackend->pEndpoint, uOffset, paSegments, cSegments, cbRead,
429 pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask);
430}
431
432static DECLCALLBACK(int) drvvdAsyncIOWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
433 PCPDMDATASEG paSegments, size_t cSegments,
434 size_t cbWrite, void *pvCompletion,
435 void **ppTask)
436{
437 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
438 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
439
440 return PDMR3AsyncCompletionEpWrite(pStorageBackend->pEndpoint, uOffset, paSegments, cSegments, cbWrite,
441 pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask);
442}
443
444static DECLCALLBACK(int) drvvdAsyncIOFlushAsync(void *pvUser, void *pStorage,
445 void *pvCompletion, void **ppTask)
446{
447 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
448 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
449
450 return PDMR3AsyncCompletionEpFlush(pStorageBackend->pEndpoint, pvCompletion,
451 (PPPDMASYNCCOMPLETIONTASK)ppTask);
452}
453
454#endif /* VBOX_WITH_PDM_ASYNC_COMPLETION */
455
456
457/*******************************************************************************
458* VD Configuration interface implementation *
459*******************************************************************************/
460
461static bool drvvdCfgAreKeysValid(void *pvUser, const char *pszzValid)
462{
463 return CFGMR3AreValuesValid((PCFGMNODE)pvUser, pszzValid);
464}
465
466static int drvvdCfgQuerySize(void *pvUser, const char *pszName, size_t *pcb)
467{
468 return CFGMR3QuerySize((PCFGMNODE)pvUser, pszName, pcb);
469}
470
471static int drvvdCfgQuery(void *pvUser, const char *pszName, char *pszString, size_t cchString)
472{
473 return CFGMR3QueryString((PCFGMNODE)pvUser, pszName, pszString, cchString);
474}
475
476
477#ifdef VBOX_WITH_INIP
478/*******************************************************************************
479* VD TCP network stack interface implementation - INIP case *
480*******************************************************************************/
481
482/** @copydoc VDINTERFACETCPNET::pfnClientConnect */
483static DECLCALLBACK(int) drvvdINIPClientConnect(const char *pszAddress, uint32_t uPort, PRTSOCKET pSock)
484{
485 int rc = VINF_SUCCESS;
486 /* First check whether lwIP is set up in this VM instance. */
487 if (!DevINIPConfigured())
488 {
489 LogRelFunc(("no IP stack\n"));
490 return VERR_NET_HOST_UNREACHABLE;
491 }
492 /* Resolve hostname. As there is no standard resolver for lwIP yet,
493 * just accept numeric IP addresses for now. */
494 struct in_addr ip;
495 if (!lwip_inet_aton(pszAddress, &ip))
496 {
497 LogRelFunc(("cannot resolve IP %s\n", pszAddress));
498 return VERR_NET_HOST_UNREACHABLE;
499 }
500 /* Create socket and connect. */
501 RTSOCKET Sock = lwip_socket(PF_INET, SOCK_STREAM, 0);
502 if (Sock != -1)
503 {
504 struct sockaddr_in InAddr = {0};
505 InAddr.sin_family = AF_INET;
506 InAddr.sin_port = htons(uPort);
507 InAddr.sin_addr = ip;
508 if (!lwip_connect(Sock, (struct sockaddr *)&InAddr, sizeof(InAddr)))
509 {
510 *pSock = Sock;
511 return VINF_SUCCESS;
512 }
513 rc = VERR_NET_CONNECTION_REFUSED; /* @todo real solution needed */
514 lwip_close(Sock);
515 }
516 else
517 rc = VERR_NET_CONNECTION_REFUSED; /* @todo real solution needed */
518 return rc;
519}
520
521/** @copydoc VDINTERFACETCPNET::pfnClientClose */
522static DECLCALLBACK(int) drvvdINIPClientClose(RTSOCKET Sock)
523{
524 lwip_close(Sock);
525 return VINF_SUCCESS; /** @todo real solution needed */
526}
527
528/** @copydoc VDINTERFACETCPNET::pfnSelectOne */
529static DECLCALLBACK(int) drvvdINIPSelectOne(RTSOCKET Sock, RTMSINTERVAL cMillies)
530{
531 fd_set fdsetR;
532 FD_ZERO(&fdsetR);
533 FD_SET(Sock, &fdsetR);
534 fd_set fdsetE = fdsetR;
535
536 int rc;
537 if (cMillies == RT_INDEFINITE_WAIT)
538 rc = lwip_select(Sock + 1, &fdsetR, NULL, &fdsetE, NULL);
539 else
540 {
541 struct timeval timeout;
542 timeout.tv_sec = cMillies / 1000;
543 timeout.tv_usec = (cMillies % 1000) * 1000;
544 rc = lwip_select(Sock + 1, &fdsetR, NULL, &fdsetE, &timeout);
545 }
546 if (rc > 0)
547 return VINF_SUCCESS;
548 if (rc == 0)
549 return VERR_TIMEOUT;
550 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
551}
552
553/** @copydoc VDINTERFACETCPNET::pfnRead */
554static DECLCALLBACK(int) drvvdINIPRead(RTSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
555{
556 /* Do params checking */
557 if (!pvBuffer || !cbBuffer)
558 {
559 AssertMsgFailed(("Invalid params\n"));
560 return VERR_INVALID_PARAMETER;
561 }
562
563 /*
564 * Read loop.
565 * If pcbRead is NULL we have to fill the entire buffer!
566 */
567 size_t cbRead = 0;
568 size_t cbToRead = cbBuffer;
569 for (;;)
570 {
571 /** @todo this clipping here is just in case (the send function
572 * needed it, so I added it here, too). Didn't investigate if this
573 * really has issues. Better be safe than sorry. */
574 ssize_t cbBytesRead = lwip_recv(Sock, (char *)pvBuffer + cbRead,
575 RT_MIN(cbToRead, 32768), 0);
576 if (cbBytesRead < 0)
577 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution */
578 if (cbBytesRead == 0 && errno)
579 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution */
580 if (pcbRead)
581 {
582 /* return partial data */
583 *pcbRead = cbBytesRead;
584 break;
585 }
586
587 /* read more? */
588 cbRead += cbBytesRead;
589 if (cbRead == cbBuffer)
590 break;
591
592 /* next */
593 cbToRead = cbBuffer - cbRead;
594 }
595
596 return VINF_SUCCESS;
597}
598
599/** @copydoc VDINTERFACETCPNET::pfnWrite */
600static DECLCALLBACK(int) drvvdINIPWrite(RTSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
601{
602 do
603 {
604 /** @todo lwip send only supports up to 65535 bytes in a single
605 * send (stupid limitation buried in the code), so make sure we
606 * don't get any wraparounds. This should be moved to DevINIP
607 * stack interface once that's implemented. */
608 ssize_t cbWritten = lwip_send(Sock, (void *)pvBuffer,
609 RT_MIN(cbBuffer, 32768), 0);
610 if (cbWritten < 0)
611 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
612 AssertMsg(cbBuffer >= (size_t)cbWritten, ("Wrote more than we requested!!! cbWritten=%d cbBuffer=%d\n",
613 cbWritten, cbBuffer));
614 cbBuffer -= cbWritten;
615 pvBuffer = (const char *)pvBuffer + cbWritten;
616 } while (cbBuffer);
617
618 return VINF_SUCCESS;
619}
620
621/** @copydoc VDINTERFACETCPNET::pfnFlush */
622static DECLCALLBACK(int) drvvdINIPFlush(RTSOCKET Sock)
623{
624 int fFlag = 1;
625 lwip_setsockopt(Sock, IPPROTO_TCP, TCP_NODELAY,
626 (const char *)&fFlag, sizeof(fFlag));
627 fFlag = 0;
628 lwip_setsockopt(Sock, IPPROTO_TCP, TCP_NODELAY,
629 (const char *)&fFlag, sizeof(fFlag));
630 return VINF_SUCCESS;
631}
632#endif /* VBOX_WITH_INIP */
633
634
635/*******************************************************************************
636* Media interface methods *
637*******************************************************************************/
638
639/** @copydoc PDMIMEDIA::pfnRead */
640static DECLCALLBACK(int) drvvdRead(PPDMIMEDIA pInterface,
641 uint64_t off, void *pvBuf, size_t cbRead)
642{
643 LogFlow(("%s: off=%#llx pvBuf=%p cbRead=%d\n", __FUNCTION__,
644 off, pvBuf, cbRead));
645 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
646 int rc = VDRead(pThis->pDisk, off, pvBuf, cbRead);
647 if (RT_SUCCESS(rc))
648 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d %.*Rhxd\n", __FUNCTION__,
649 off, pvBuf, cbRead, cbRead, pvBuf));
650 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
651 return rc;
652}
653
654/** @copydoc PDMIMEDIA::pfnWrite */
655static DECLCALLBACK(int) drvvdWrite(PPDMIMEDIA pInterface,
656 uint64_t off, const void *pvBuf,
657 size_t cbWrite)
658{
659 LogFlow(("%s: off=%#llx pvBuf=%p cbWrite=%d\n", __FUNCTION__,
660 off, pvBuf, cbWrite));
661 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
662 Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d %.*Rhxd\n", __FUNCTION__,
663 off, pvBuf, cbWrite, cbWrite, pvBuf));
664 int rc = VDWrite(pThis->pDisk, off, pvBuf, cbWrite);
665 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
666 return rc;
667}
668
669/** @copydoc PDMIMEDIA::pfnFlush */
670static DECLCALLBACK(int) drvvdFlush(PPDMIMEDIA pInterface)
671{
672 LogFlow(("%s:\n", __FUNCTION__));
673 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
674 int rc = VDFlush(pThis->pDisk);
675 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
676 return rc;
677}
678
679/** @copydoc PDMIMEDIA::pfnGetSize */
680static DECLCALLBACK(uint64_t) drvvdGetSize(PPDMIMEDIA pInterface)
681{
682 LogFlow(("%s:\n", __FUNCTION__));
683 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
684 uint64_t cb = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
685 LogFlow(("%s: returns %#llx (%llu)\n", __FUNCTION__, cb, cb));
686 return cb;
687}
688
689/** @copydoc PDMIMEDIA::pfnIsReadOnly */
690static DECLCALLBACK(bool) drvvdIsReadOnly(PPDMIMEDIA pInterface)
691{
692 LogFlow(("%s:\n", __FUNCTION__));
693 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
694 bool f = VDIsReadOnly(pThis->pDisk);
695 LogFlow(("%s: returns %d\n", __FUNCTION__, f));
696 return f;
697}
698
699/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
700static DECLCALLBACK(int) drvvdBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
701 PPDMMEDIAGEOMETRY pPCHSGeometry)
702{
703 LogFlow(("%s:\n", __FUNCTION__));
704 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
705 int rc = VDGetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
706 if (RT_FAILURE(rc))
707 {
708 Log(("%s: geometry not available.\n", __FUNCTION__));
709 rc = VERR_PDM_GEOMETRY_NOT_SET;
710 }
711 LogFlow(("%s: returns %Rrc (CHS=%d/%d/%d)\n", __FUNCTION__,
712 rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
713 return rc;
714}
715
716/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
717static DECLCALLBACK(int) drvvdBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
718 PCPDMMEDIAGEOMETRY pPCHSGeometry)
719{
720 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
721 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
722 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
723 int rc = VDSetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
724 if (rc == VERR_VD_GEOMETRY_NOT_SET)
725 rc = VERR_PDM_GEOMETRY_NOT_SET;
726 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
727 return rc;
728}
729
730/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
731static DECLCALLBACK(int) drvvdBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
732 PPDMMEDIAGEOMETRY pLCHSGeometry)
733{
734 LogFlow(("%s:\n", __FUNCTION__));
735 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
736 int rc = VDGetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
737 if (RT_FAILURE(rc))
738 {
739 Log(("%s: geometry not available.\n", __FUNCTION__));
740 rc = VERR_PDM_GEOMETRY_NOT_SET;
741 }
742 LogFlow(("%s: returns %Rrc (CHS=%d/%d/%d)\n", __FUNCTION__,
743 rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
744 return rc;
745}
746
747/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
748static DECLCALLBACK(int) drvvdBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
749 PCPDMMEDIAGEOMETRY pLCHSGeometry)
750{
751 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
752 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
753 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
754 int rc = VDSetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
755 if (rc == VERR_VD_GEOMETRY_NOT_SET)
756 rc = VERR_PDM_GEOMETRY_NOT_SET;
757 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
758 return rc;
759}
760
761/** @copydoc PDMIMEDIA::pfnGetUuid */
762static DECLCALLBACK(int) drvvdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
763{
764 LogFlow(("%s:\n", __FUNCTION__));
765 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
766 int rc = VDGetUuid(pThis->pDisk, 0, pUuid);
767 LogFlow(("%s: returns %Rrc ({%RTuuid})\n", __FUNCTION__, rc, pUuid));
768 return rc;
769}
770
771/*******************************************************************************
772* Async Media interface methods *
773*******************************************************************************/
774
775static DECLCALLBACK(int) drvvdStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
776 PPDMDATASEG paSeg, unsigned cSeg,
777 size_t cbRead, void *pvUser)
778{
779 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d\n pvUser=%#p", __FUNCTION__,
780 uOffset, paSeg, cSeg, cbRead, pvUser));
781 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
782 int rc = VDAsyncRead(pThis->pDisk, uOffset, cbRead, paSeg, cSeg, pvUser);
783 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
784 return rc;
785}
786
787static DECLCALLBACK(int) drvvdStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
788 PPDMDATASEG paSeg, unsigned cSeg,
789 size_t cbWrite, void *pvUser)
790{
791 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d\n pvUser=%#p", __FUNCTION__,
792 uOffset, paSeg, cSeg, cbWrite, pvUser));
793 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
794 int rc = VDAsyncWrite(pThis->pDisk, uOffset, cbWrite, paSeg, cSeg, pvUser);
795 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
796 return rc;
797}
798
799/*******************************************************************************
800* Async transport port interface methods *
801*******************************************************************************/
802
803static DECLCALLBACK(int) drvvdTasksCompleteNotify(PPDMDRVINS pDrvIns, void *pvUser)
804{
805 return VERR_NOT_IMPLEMENTED;
806}
807
808
809/*******************************************************************************
810* Base interface methods *
811*******************************************************************************/
812
813/**
814 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
815 */
816static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface, const char *pszIID)
817{
818 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
819 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
820
821 if (RTUuidCompare2Strs(pszIID, PDMIBASE_IID) == 0)
822 return &pDrvIns->IBase;
823 if (RTUuidCompare2Strs(pszIID, PDMINTERFACE_MEDIA) == 0)
824 return &pThis->IMedia;
825 if (RTUuidCompare2Strs(pszIID, PDMINTERFACE_MEDIA_ASYNC) == 0)
826 return pThis->fAsyncIOSupported ? &pThis->IMediaAsync : NULL;
827 return NULL;
828}
829
830
831/*******************************************************************************
832* Saved state notification methods *
833*******************************************************************************/
834
835/**
836 * Load done callback for re-opening the image writable during teleportation.
837 *
838 * This is called both for successful and failed load runs, we only care about
839 * successfull ones.
840 *
841 * @returns VBox status code.
842 * @param pDrvIns The driver instance.
843 * @param pSSM The saved state handle.
844 */
845static DECLCALLBACK(int) drvvdLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
846{
847 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
848 Assert(!pThis->fErrorUseRuntime);
849
850 /* Drop out if we don't have any work to do or if it's a failed load. */
851 if ( !pThis->fTempReadOnly
852 || RT_FAILURE(SSMR3HandleGetStatus(pSSM)))
853 return VINF_SUCCESS;
854
855 int rc = drvvdSetWritable(pThis);
856 if (RT_FAILURE(rc)) /** @todo does the bugger set any errors? */
857 return SSMR3SetLoadError(pSSM, rc, RT_SRC_POS,
858 N_("Failed to write lock the images"));
859 return VINF_SUCCESS;
860}
861
862
863/*******************************************************************************
864* Driver methods *
865*******************************************************************************/
866
867static DECLCALLBACK(void) drvvdPowerOff(PPDMDRVINS pDrvIns)
868{
869 LogFlow(("%s:\n", __FUNCTION__));
870 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
871
872 /*
873 * We must close the disk here to ensure that
874 * the backend closes all files before the
875 * async transport driver is destructed.
876 */
877 int rc = VDCloseAll(pThis->pDisk);
878 AssertRC(rc);
879}
880
881/**
882 * VM resume notification that we use to undo what the temporary read-only image
883 * mode set by drvvdSuspend.
884 *
885 * Also switch to runtime error mode if we're resuming after a state load
886 * without having been powered on first.
887 *
888 * @param pDrvIns The driver instance data.
889 *
890 * @todo The VMSetError vs VMSetRuntimeError mess must be fixed elsewhere,
891 * we're making assumptions about Main behavior here!
892 */
893static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
894{
895 LogFlow(("%s:\n", __FUNCTION__));
896 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
897 drvvdSetWritable(pThis);
898 pThis->fErrorUseRuntime = true;
899}
900
901/**
902 * The VM is being suspended, temporarily change to read-only image mode.
903 *
904 * This is important for several reasons:
905 * -# It makes sure that there are no pending writes to the image. Most
906 * backends implements this by closing and reopening the image in read-only
907 * mode.
908 * -# It allows Main to read the images during snapshotting without having
909 * to account for concurrent writes.
910 * -# This is essential for making teleportation targets sharing images work
911 * right. Both with regards to caching and with regards to file sharing
912 * locks (RTFILE_O_DENY_*). (See also drvvdLoadDone.)
913 *
914 * @param pDrvIns The driver instance data.
915 */
916static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
917{
918 LogFlow(("%s:\n", __FUNCTION__));
919 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
920 if (!VDIsReadOnly(pThis->pDisk))
921 {
922 unsigned uOpenFlags;
923 int rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
924 AssertRC(rc);
925 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
926 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
927 AssertRC(rc);
928 pThis->fTempReadOnly = true;
929 }
930}
931
932/**
933 * VM PowerOn notification for undoing the TempReadOnly config option and
934 * changing to runtime error mode.
935 *
936 * @param pDrvIns The driver instance data.
937 *
938 * @todo The VMSetError vs VMSetRuntimeError mess must be fixed elsewhere,
939 * we're making assumptions about Main behavior here!
940 */
941static DECLCALLBACK(void) drvvdPowerOn(PPDMDRVINS pDrvIns)
942{
943 LogFlow(("%s:\n", __FUNCTION__));
944 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
945 drvvdSetWritable(pThis);
946 pThis->fErrorUseRuntime = true;
947}
948
949/**
950 * @copydoc FNPDMDRVDESTRUCT
951 */
952static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
953{
954 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
955 LogFlow(("%s:\n", __FUNCTION__));
956
957 if (VALID_PTR(pThis->pDisk))
958 {
959 VDDestroy(pThis->pDisk);
960 pThis->pDisk = NULL;
961 }
962 drvvdFreeImages(pThis);
963}
964
965/**
966 * Construct a VBox disk media driver instance.
967 *
968 * @copydoc FNPDMDRVCONSTRUCT
969 */
970static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
971 PCFGMNODE pCfgHandle,
972 uint32_t fFlags)
973{
974 LogFlow(("%s:\n", __FUNCTION__));
975 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
976 int rc = VINF_SUCCESS;
977 char *pszName = NULL; /**< The path of the disk image file. */
978 char *pszFormat = NULL; /**< The format backed to use for this image. */
979 bool fReadOnly; /**< True if the media is read-only. */
980 bool fHonorZeroWrites; /**< True if zero blocks should be written. */
981
982 /*
983 * Init the static parts.
984 */
985 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
986 pThis->pDrvIns = pDrvIns;
987 pThis->fTempReadOnly = false;
988 pThis->pDisk = NULL;
989 pThis->fAsyncIOSupported = false;
990
991 /* IMedia */
992 pThis->IMedia.pfnRead = drvvdRead;
993 pThis->IMedia.pfnWrite = drvvdWrite;
994 pThis->IMedia.pfnFlush = drvvdFlush;
995 pThis->IMedia.pfnGetSize = drvvdGetSize;
996 pThis->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
997 pThis->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
998 pThis->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
999 pThis->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
1000 pThis->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
1001 pThis->IMedia.pfnGetUuid = drvvdGetUuid;
1002
1003 /* IMediaAsync */
1004 pThis->IMediaAsync.pfnStartRead = drvvdStartRead;
1005 pThis->IMediaAsync.pfnStartWrite = drvvdStartWrite;
1006
1007 /* Initialize supported VD interfaces. */
1008 pThis->pVDIfsDisk = NULL;
1009
1010 pThis->VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
1011 pThis->VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
1012 pThis->VDIErrorCallbacks.pfnError = drvvdErrorCallback;
1013 pThis->VDIErrorCallbacks.pfnMessage = NULL;
1014
1015 rc = VDInterfaceAdd(&pThis->VDIError, "DrvVD_VDIError", VDINTERFACETYPE_ERROR,
1016 &pThis->VDIErrorCallbacks, pDrvIns, &pThis->pVDIfsDisk);
1017 AssertRC(rc);
1018
1019 /* This is just prepared here, the actual interface is per-image, so it's
1020 * added later. No need to have separate callback tables. */
1021 pThis->VDIConfigCallbacks.cbSize = sizeof(VDINTERFACECONFIG);
1022 pThis->VDIConfigCallbacks.enmInterface = VDINTERFACETYPE_CONFIG;
1023 pThis->VDIConfigCallbacks.pfnAreKeysValid = drvvdCfgAreKeysValid;
1024 pThis->VDIConfigCallbacks.pfnQuerySize = drvvdCfgQuerySize;
1025 pThis->VDIConfigCallbacks.pfnQuery = drvvdCfgQuery;
1026
1027 /* List of images is empty now. */
1028 pThis->pImages = NULL;
1029
1030 /* Try to attach async media port interface above.*/
1031 pThis->pDrvMediaAsyncPort = (PPDMIMEDIAASYNCPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_MEDIA_ASYNC_PORT);
1032
1033 /*
1034 * Validate configuration and find all parent images.
1035 * It's sort of up side down from the image dependency tree.
1036 */
1037 bool fHostIP = false;
1038 bool fUseNewIo = false;
1039 unsigned iLevel = 0;
1040 PCFGMNODE pCurNode = pCfgHandle;
1041
1042 for (;;)
1043 {
1044 bool fValid;
1045
1046 if (pCurNode == pCfgHandle)
1047 {
1048 /* Toplevel configuration additionally contains the global image
1049 * open flags. Some might be converted to per-image flags later. */
1050 fValid = CFGMR3AreValuesValid(pCurNode,
1051 "Format\0Path\0"
1052 "ReadOnly\0TempReadOnly\0HonorZeroWrites\0"
1053 "HostIPStack\0UseNewIo\0");
1054 }
1055 else
1056 {
1057 /* All other image configurations only contain image name and
1058 * the format information. */
1059 fValid = CFGMR3AreValuesValid(pCurNode, "Format\0Path\0");
1060 }
1061 if (!fValid)
1062 {
1063 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
1064 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
1065 break;
1066 }
1067
1068 if (pCurNode == pCfgHandle)
1069 {
1070 rc = CFGMR3QueryBoolDef(pCurNode, "HostIPStack", &fHostIP, true);
1071 if (RT_FAILURE(rc))
1072 {
1073 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1074 N_("DrvVD: Configuration error: Querying \"HostIPStack\" as boolean failed"));
1075 break;
1076 }
1077
1078 rc = CFGMR3QueryBoolDef(pCurNode, "HonorZeroWrites", &fHonorZeroWrites, false);
1079 if (RT_FAILURE(rc))
1080 {
1081 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1082 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
1083 break;
1084 }
1085
1086 rc = CFGMR3QueryBoolDef(pCurNode, "ReadOnly", &fReadOnly, false);
1087 if (RT_FAILURE(rc))
1088 {
1089 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1090 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
1091 break;
1092 }
1093
1094 rc = CFGMR3QueryBoolDef(pCurNode, "TempReadOnly", &pThis->fTempReadOnly, false);
1095 if (RT_FAILURE(rc))
1096 {
1097 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1098 N_("DrvVD: Configuration error: Querying \"TempReadOnly\" as boolean failed"));
1099 break;
1100 }
1101 if (fReadOnly && pThis->fTempReadOnly)
1102 {
1103 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1104 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"TempReadOnly\" are set"));
1105 break;
1106 }
1107 rc = CFGMR3QueryBoolDef(pCurNode, "UseNewIo", &fUseNewIo, false);
1108 if (RT_FAILURE(rc))
1109 {
1110 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1111 N_("DrvVD: Configuration error: Querying \"UseNewIo\" as boolean failed"));
1112 break;
1113 }
1114 }
1115
1116 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
1117 if (!pParent)
1118 break;
1119 pCurNode = pParent;
1120 iLevel++;
1121 }
1122
1123 /*
1124 * Open the images.
1125 */
1126 if (RT_SUCCESS(rc))
1127 {
1128 /* First of all figure out what kind of TCP networking stack interface
1129 * to use. This is done unconditionally, as backends which don't need
1130 * it will just ignore it. */
1131 if (fHostIP)
1132 {
1133 pThis->VDITcpNetCallbacks.cbSize = sizeof(VDINTERFACETCPNET);
1134 pThis->VDITcpNetCallbacks.enmInterface = VDINTERFACETYPE_TCPNET;
1135 pThis->VDITcpNetCallbacks.pfnClientConnect = RTTcpClientConnect;
1136 pThis->VDITcpNetCallbacks.pfnClientClose = RTTcpClientClose;
1137 pThis->VDITcpNetCallbacks.pfnSelectOne = RTTcpSelectOne;
1138 pThis->VDITcpNetCallbacks.pfnRead = RTTcpRead;
1139 pThis->VDITcpNetCallbacks.pfnWrite = RTTcpWrite;
1140 pThis->VDITcpNetCallbacks.pfnFlush = RTTcpFlush;
1141 }
1142 else
1143 {
1144#ifndef VBOX_WITH_INIP
1145 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
1146 RT_SRC_POS, N_("DrvVD: Configuration error: TCP over Internal Networking not compiled in"));
1147#else /* VBOX_WITH_INIP */
1148 pThis->VDITcpNetCallbacks.cbSize = sizeof(VDINTERFACETCPNET);
1149 pThis->VDITcpNetCallbacks.enmInterface = VDINTERFACETYPE_TCPNET;
1150 pThis->VDITcpNetCallbacks.pfnClientConnect = drvvdINIPClientConnect;
1151 pThis->VDITcpNetCallbacks.pfnClientClose = drvvdINIPClientClose;
1152 pThis->VDITcpNetCallbacks.pfnSelectOne = drvvdINIPSelectOne;
1153 pThis->VDITcpNetCallbacks.pfnRead = drvvdINIPRead;
1154 pThis->VDITcpNetCallbacks.pfnWrite = drvvdINIPWrite;
1155 pThis->VDITcpNetCallbacks.pfnFlush = drvvdINIPFlush;
1156#endif /* VBOX_WITH_INIP */
1157 }
1158 if (RT_SUCCESS(rc))
1159 {
1160 rc = VDInterfaceAdd(&pThis->VDITcpNet, "DrvVD_INIP",
1161 VDINTERFACETYPE_TCPNET,
1162 &pThis->VDITcpNetCallbacks, NULL,
1163 &pThis->pVDIfsDisk);
1164 }
1165
1166 if (RT_SUCCESS(rc) && fUseNewIo)
1167 {
1168#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
1169 pThis->VDIAsyncIOCallbacks.cbSize = sizeof(VDINTERFACEASYNCIO);
1170 pThis->VDIAsyncIOCallbacks.enmInterface = VDINTERFACETYPE_ASYNCIO;
1171 pThis->VDIAsyncIOCallbacks.pfnOpen = drvvdAsyncIOOpen;
1172 pThis->VDIAsyncIOCallbacks.pfnClose = drvvdAsyncIOClose;
1173 pThis->VDIAsyncIOCallbacks.pfnGetSize = drvvdAsyncIOGetSize;
1174 pThis->VDIAsyncIOCallbacks.pfnSetSize = drvvdAsyncIOSetSize;
1175 pThis->VDIAsyncIOCallbacks.pfnReadSync = drvvdAsyncIOReadSync;
1176 pThis->VDIAsyncIOCallbacks.pfnWriteSync = drvvdAsyncIOWriteSync;
1177 pThis->VDIAsyncIOCallbacks.pfnFlushSync = drvvdAsyncIOFlushSync;
1178 pThis->VDIAsyncIOCallbacks.pfnReadAsync = drvvdAsyncIOReadAsync;
1179 pThis->VDIAsyncIOCallbacks.pfnWriteAsync = drvvdAsyncIOWriteAsync;
1180 pThis->VDIAsyncIOCallbacks.pfnFlushAsync = drvvdAsyncIOFlushAsync;
1181
1182 rc = VDInterfaceAdd(&pThis->VDIAsyncIO, "DrvVD_AsyncIO", VDINTERFACETYPE_ASYNCIO,
1183 &pThis->VDIAsyncIOCallbacks, pThis, &pThis->pVDIfsDisk);
1184#else /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
1185 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
1186 RT_SRC_POS, N_("DrvVD: Configuration error: Async Completion Framework not compiled in"));
1187#endif /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
1188 }
1189
1190 if (RT_SUCCESS(rc))
1191 {
1192 rc = VDCreate(pThis->pVDIfsDisk, &pThis->pDisk);
1193 /* Error message is already set correctly. */
1194 }
1195 }
1196
1197 if (pThis->pDrvMediaAsyncPort)
1198 pThis->fAsyncIOSupported = true;
1199
1200 while (pCurNode && RT_SUCCESS(rc))
1201 {
1202 /* Allocate per-image data. */
1203 PVBOXIMAGE pImage = drvvdNewImage(pThis);
1204 if (!pImage)
1205 {
1206 rc = VERR_NO_MEMORY;
1207 break;
1208 }
1209
1210 /*
1211 * Read the image configuration.
1212 */
1213 rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
1214 if (RT_FAILURE(rc))
1215 {
1216 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1217 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
1218 break;
1219 }
1220
1221 rc = CFGMR3QueryStringAlloc(pCurNode, "Format", &pszFormat);
1222 if (RT_FAILURE(rc))
1223 {
1224 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1225 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
1226 break;
1227 }
1228
1229 PCFGMNODE pCfg = CFGMR3GetChild(pCurNode, "VDConfig");
1230 rc = VDInterfaceAdd(&pImage->VDIConfig, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
1231 &pThis->VDIConfigCallbacks, pCfg, &pImage->pVDIfsImage);
1232 AssertRC(rc);
1233
1234 /*
1235 * Open the image.
1236 */
1237 unsigned uOpenFlags;
1238 if (fReadOnly || pThis->fTempReadOnly || iLevel != 0)
1239 uOpenFlags = VD_OPEN_FLAGS_READONLY;
1240 else
1241 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
1242 if (fHonorZeroWrites)
1243 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
1244 if (pThis->fAsyncIOSupported)
1245 uOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
1246
1247 /* Try to open backend in async I/O mode first. */
1248 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
1249 if (rc == VERR_NOT_SUPPORTED)
1250 {
1251 /* Seems async I/O is not supported by the backend, open in normal mode. */
1252 pThis->fAsyncIOSupported = false;
1253 uOpenFlags &= ~VD_OPEN_FLAGS_ASYNC_IO;
1254 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
1255 }
1256
1257 if (RT_SUCCESS(rc))
1258 {
1259 Log(("%s: %d - Opened '%s' in %s mode\n", __FUNCTION__,
1260 iLevel, pszName,
1261 VDIsReadOnly(pThis->pDisk) ? "read-only" : "read-write"));
1262 if ( VDIsReadOnly(pThis->pDisk)
1263 && !fReadOnly
1264 && !pThis->fTempReadOnly
1265 && iLevel == 0)
1266 {
1267 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_VD_IMAGE_READ_ONLY, RT_SRC_POS,
1268 N_("Failed to open image '%s' for writing due to wrong permissions"),
1269 pszName);
1270 break;
1271 }
1272 }
1273 else
1274 {
1275 rc = PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1276 N_("Failed to open image '%s' in %s mode rc=%Rrc"), pszName,
1277 (uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "read-only" : "read-write", rc);
1278 break;
1279 }
1280
1281
1282 MMR3HeapFree(pszName);
1283 pszName = NULL;
1284 MMR3HeapFree(pszFormat);
1285 pszFormat = NULL;
1286
1287 /* next */
1288 iLevel--;
1289 pCurNode = CFGMR3GetParent(pCurNode);
1290 }
1291
1292 /*
1293 * Register a load-done callback so we can undo TempReadOnly config before
1294 * we get to drvvdResume. Autoamtically deregistered upon destruction.
1295 */
1296 if (RT_SUCCESS(rc))
1297 rc = PDMDrvHlpSSMRegisterEx(pDrvIns, 0 /* version */, 0 /* cbGuess */,
1298 NULL /*pfnLivePrep*/, NULL /*pfnLiveExec*/, NULL /*pfnLiveVote*/,
1299 NULL /*pfnSavePrep*/, NULL /*pfnSaveExec*/, NULL /*pfnSaveDone*/,
1300 NULL /*pfnDonePrep*/, NULL /*pfnLoadExec*/, drvvdLoadDone);
1301
1302
1303 if (RT_FAILURE(rc))
1304 {
1305 if (VALID_PTR(pszName))
1306 MMR3HeapFree(pszName);
1307 if (VALID_PTR(pszFormat))
1308 MMR3HeapFree(pszFormat);
1309 /* drvvdDestruct does the rest. */
1310 }
1311
1312 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
1313 return rc;
1314}
1315
1316/**
1317 * VBox disk container media driver registration record.
1318 */
1319const PDMDRVREG g_DrvVD =
1320{
1321 /* u32Version */
1322 PDM_DRVREG_VERSION,
1323 /* szDriverName */
1324 "VD",
1325 /* szRCMod */
1326 "",
1327 /* szR0Mod */
1328 "",
1329 /* pszDescription */
1330 "Generic VBox disk media driver.",
1331 /* fFlags */
1332 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1333 /* fClass. */
1334 PDM_DRVREG_CLASS_MEDIA,
1335 /* cMaxInstances */
1336 ~0,
1337 /* cbInstance */
1338 sizeof(VBOXDISK),
1339 /* pfnConstruct */
1340 drvvdConstruct,
1341 /* pfnDestruct */
1342 drvvdDestruct,
1343 /* pfnRelocate */
1344 NULL,
1345 /* pfnIOCtl */
1346 NULL,
1347 /* pfnPowerOn */
1348 drvvdPowerOn,
1349 /* pfnReset */
1350 NULL,
1351 /* pfnSuspend */
1352 drvvdSuspend,
1353 /* pfnResume */
1354 drvvdResume,
1355 /* pfnAttach */
1356 NULL,
1357 /* pfnDetach */
1358 NULL,
1359 /* pfnPowerOff */
1360 drvvdPowerOff,
1361 /* pfnSoftReset */
1362 NULL,
1363 /* u32EndVersion */
1364 PDM_DRVREG_VERSION
1365};
1366
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette