VirtualBox

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

Last change on this file since 37596 was 36022, checked in by vboxsync, 14 years ago

DrvVD: Fix crash for the internal network stack case

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 97.4 KB
Line 
1/* $Id: DrvVD.cpp 36022 2011-02-18 15:00:20Z vboxsync $ */
2/** @file
3 * DrvVD - Generic VBox disk media driver.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
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
18
19/*******************************************************************************
20* Header files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_VD
23#include <VBox/vd.h>
24#include <VBox/vmm/pdmdrv.h>
25#include <VBox/vmm/pdmasynccompletion.h>
26#include <VBox/vmm/pdmblkcache.h>
27#include <iprt/asm.h>
28#include <iprt/alloc.h>
29#include <iprt/assert.h>
30#include <iprt/uuid.h>
31#include <iprt/file.h>
32#include <iprt/string.h>
33#include <iprt/tcp.h>
34#include <iprt/semaphore.h>
35#include <iprt/sg.h>
36#include <iprt/poll.h>
37#include <iprt/pipe.h>
38#include <iprt/system.h>
39
40#ifdef VBOX_WITH_INIP
41/* All lwip header files are not C++ safe. So hack around this. */
42RT_C_DECLS_BEGIN
43#include <lwip/inet.h>
44#include <lwip/tcp.h>
45#include <lwip/sockets.h>
46RT_C_DECLS_END
47#endif /* VBOX_WITH_INIP */
48
49#include "VBoxDD.h"
50
51#ifdef VBOX_WITH_INIP
52/* Small hack to get at lwIP initialized status */
53extern bool DevINIPConfigured(void);
54#endif /* VBOX_WITH_INIP */
55
56
57/*******************************************************************************
58* Defined types, constants and macros *
59*******************************************************************************/
60
61/** Converts a pointer to VBOXDISK::IMedia to a PVBOXDISK. */
62#define PDMIMEDIA_2_VBOXDISK(pInterface) \
63 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMedia)) )
64
65/** Converts a pointer to PDMDRVINS::IBase to a PPDMDRVINS. */
66#define PDMIBASE_2_DRVINS(pInterface) \
67 ( (PPDMDRVINS)((uintptr_t)pInterface - RT_OFFSETOF(PDMDRVINS, IBase)) )
68
69/** Converts a pointer to PDMDRVINS::IBase to a PVBOXDISK. */
70#define PDMIBASE_2_VBOXDISK(pInterface) \
71 ( PDMINS_2_DATA(PDMIBASE_2_DRVINS(pInterface), PVBOXDISK) )
72
73/** Converts a pointer to VBOXDISK::IMediaAsync to a PVBOXDISK. */
74#define PDMIMEDIAASYNC_2_VBOXDISK(pInterface) \
75 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMediaAsync)) )
76
77/**
78 * VBox disk container, image information, private part.
79 */
80
81typedef struct VBOXIMAGE
82{
83 /** Pointer to next image. */
84 struct VBOXIMAGE *pNext;
85 /** Pointer to list of VD interfaces. Per-image. */
86 PVDINTERFACE pVDIfsImage;
87 /** Common structure for the configuration information interface. */
88 VDINTERFACE VDIConfig;
89 /** Common structure for the supported TCP network stack interface. */
90 VDINTERFACE VDITcpNet;
91 /** Common structure for the supported I/O interface. */
92 VDINTERFACE VDIIO;
93} VBOXIMAGE, *PVBOXIMAGE;
94
95/**
96 * Storage backend data.
97 */
98typedef struct DRVVDSTORAGEBACKEND
99{
100 /** PDM async completion end point. */
101 PPDMASYNCCOMPLETIONENDPOINT pEndpoint;
102 /** The template. */
103 PPDMASYNCCOMPLETIONTEMPLATE pTemplate;
104 /** Event semaphore for synchronous operations. */
105 RTSEMEVENT EventSem;
106 /** Flag whether a synchronous operation is currently pending. */
107 volatile bool fSyncIoPending;
108 /** Return code of the last completed request. */
109 int rcReqLast;
110 /** Callback routine */
111 PFNVDCOMPLETED pfnCompleted;
112} DRVVDSTORAGEBACKEND, *PDRVVDSTORAGEBACKEND;
113
114/**
115 * VBox disk container media main structure, private part.
116 *
117 * @implements PDMIMEDIA
118 * @implements PDMIMEDIAASYNC
119 * @implements VDINTERFACEERROR
120 * @implements VDINTERFACETCPNET
121 * @implements VDINTERFACEASYNCIO
122 * @implements VDINTERFACECONFIG
123 */
124typedef struct VBOXDISK
125{
126 /** The VBox disk container. */
127 PVBOXHDD pDisk;
128 /** The media interface. */
129 PDMIMEDIA IMedia;
130 /** Media port. */
131 PPDMIMEDIAPORT pDrvMediaPort;
132 /** Pointer to the driver instance. */
133 PPDMDRVINS pDrvIns;
134 /** Flag whether suspend has changed image open mode to read only. */
135 bool fTempReadOnly;
136 /** Flag whether to use the runtime (true) or startup error facility. */
137 bool fErrorUseRuntime;
138 /** Pointer to list of VD interfaces. Per-disk. */
139 PVDINTERFACE pVDIfsDisk;
140 /** Common structure for the supported error interface. */
141 VDINTERFACE VDIError;
142 /** Callback table for error interface. */
143 VDINTERFACEERROR VDIErrorCallbacks;
144 /** Common structure for the supported thread synchronization interface. */
145 VDINTERFACE VDIThreadSync;
146 /** Callback table for thread synchronization interface. */
147 VDINTERFACETHREADSYNC VDIThreadSyncCallbacks;
148
149 /** Callback table for the configuration information interface. */
150 VDINTERFACECONFIG VDIConfigCallbacks;
151 /** Callback table for TCP network stack interface. */
152 VDINTERFACETCPNET VDITcpNetCallbacks;
153 /** Callback table for I/O interface. */
154 VDINTERFACEIO VDIIOCallbacks;
155
156 /** Flag whether opened disk supports async I/O operations. */
157 bool fAsyncIOSupported;
158 /** The async media interface. */
159 PDMIMEDIAASYNC IMediaAsync;
160 /** The async media port interface above. */
161 PPDMIMEDIAASYNCPORT pDrvMediaAsyncPort;
162 /** Pointer to the list of data we need to keep per image. */
163 PVBOXIMAGE pImages;
164 /** Flag whether the media should allow concurrent open for writing. */
165 bool fShareable;
166 /** Flag whether a merge operation has been set up. */
167 bool fMergePending;
168 /** Synchronization to prevent destruction before merge finishes. */
169 RTSEMFASTMUTEX MergeCompleteMutex;
170 /** Synchronization between merge and other image accesses. */
171 RTSEMRW MergeLock;
172 /** Source image index for merging. */
173 unsigned uMergeSource;
174 /** Target image index for merging. */
175 unsigned uMergeTarget;
176
177 /** Flag whether boot acceleration is enabled. */
178 bool fBootAccelEnabled;
179 /** Flag whether boot acceleration is currently active. */
180 bool fBootAccelActive;
181 /** Size of the disk, used for read truncation. */
182 size_t cbDisk;
183 /** Size of the configured buffer. */
184 size_t cbBootAccelBuffer;
185 /** Start offset for which the buffer holds data. */
186 uint64_t offDisk;
187 /** Number of valid bytes in the buffer. */
188 size_t cbDataValid;
189 /** The disk buffer. */
190 uint8_t *pbData;
191 /** Bandwidth group the disk is assigned to. */
192 char *pszBwGroup;
193 /** Flag whether async I/O using the host cache is enabled. */
194 bool fAsyncIoWithHostCache;
195
196 /** I/O interface for a cache image. */
197 VDINTERFACE VDIIOCache;
198 /** Interface list for the cache image. */
199 PVDINTERFACE pVDIfsCache;
200
201 /** The block cache handle if configured. */
202 PPDMBLKCACHE pBlkCache;
203} VBOXDISK, *PVBOXDISK;
204
205
206/*******************************************************************************
207* Internal Functions *
208*******************************************************************************/
209
210/**
211 * Internal: allocate new image descriptor and put it in the list
212 */
213static PVBOXIMAGE drvvdNewImage(PVBOXDISK pThis)
214{
215 AssertPtr(pThis);
216 PVBOXIMAGE pImage = (PVBOXIMAGE)RTMemAllocZ(sizeof(VBOXIMAGE));
217 if (pImage)
218 {
219 pImage->pVDIfsImage = NULL;
220 PVBOXIMAGE *pp = &pThis->pImages;
221 while (*pp != NULL)
222 pp = &(*pp)->pNext;
223 *pp = pImage;
224 pImage->pNext = NULL;
225 }
226
227 return pImage;
228}
229
230/**
231 * Internal: free the list of images descriptors.
232 */
233static void drvvdFreeImages(PVBOXDISK pThis)
234{
235 while (pThis->pImages != NULL)
236 {
237 PVBOXIMAGE p = pThis->pImages;
238 pThis->pImages = pThis->pImages->pNext;
239 RTMemFree(p);
240 }
241}
242
243
244/**
245 * Make the image temporarily read-only.
246 *
247 * @returns VBox status code.
248 * @param pThis The driver instance data.
249 */
250static int drvvdSetReadonly(PVBOXDISK pThis)
251{
252 int rc = VINF_SUCCESS;
253 if (!VDIsReadOnly(pThis->pDisk))
254 {
255 unsigned uOpenFlags;
256 rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
257 AssertRC(rc);
258 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
259 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
260 AssertRC(rc);
261 pThis->fTempReadOnly = true;
262 }
263 return rc;
264}
265
266
267/**
268 * Undo the temporary read-only status of the image.
269 *
270 * @returns VBox status code.
271 * @param pThis The driver instance data.
272 */
273static int drvvdSetWritable(PVBOXDISK pThis)
274{
275 int rc = VINF_SUCCESS;
276 if (pThis->fTempReadOnly)
277 {
278 unsigned uOpenFlags;
279 rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
280 AssertRC(rc);
281 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
282 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
283 if (RT_SUCCESS(rc))
284 pThis->fTempReadOnly = false;
285 else
286 AssertRC(rc);
287 }
288 return rc;
289}
290
291
292/*******************************************************************************
293* Error reporting callback *
294*******************************************************************************/
295
296static void drvvdErrorCallback(void *pvUser, int rc, RT_SRC_POS_DECL,
297 const char *pszFormat, va_list va)
298{
299 PPDMDRVINS pDrvIns = (PPDMDRVINS)pvUser;
300 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
301 if (pThis->fErrorUseRuntime)
302 /* We must not pass VMSETRTERR_FLAGS_FATAL as it could lead to a
303 * deadlock: We are probably executed in a thread context != EMT
304 * and the EM thread would wait until every thread is suspended
305 * but we would wait for the EM thread ... */
306
307 PDMDrvHlpVMSetRuntimeErrorV(pDrvIns, /* fFlags=*/ 0, "DrvVD", pszFormat, va);
308 else
309 PDMDrvHlpVMSetErrorV(pDrvIns, rc, RT_SRC_POS_ARGS, pszFormat, va);
310}
311
312/*******************************************************************************
313* VD Async I/O interface implementation *
314*******************************************************************************/
315
316#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
317
318static DECLCALLBACK(void) drvvdAsyncTaskCompleted(PPDMDRVINS pDrvIns, void *pvTemplateUser, void *pvUser, int rcReq)
319{
320 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
321 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pvTemplateUser;
322
323 LogFlowFunc(("pDrvIns=%#p pvTemplateUser=%#p pvUser=%#p rcReq\n",
324 pDrvIns, pvTemplateUser, pvUser, rcReq));
325
326 if (pStorageBackend->fSyncIoPending)
327 {
328 Assert(!pvUser);
329 pStorageBackend->rcReqLast = rcReq;
330 pStorageBackend->fSyncIoPending = false;
331 RTSemEventSignal(pStorageBackend->EventSem);
332 }
333 else
334 {
335 int rc;
336
337 AssertPtr(pvUser);
338
339 AssertPtr(pStorageBackend->pfnCompleted);
340 rc = pStorageBackend->pfnCompleted(pvUser, rcReq);
341 AssertRC(rc);
342 }
343}
344
345static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation,
346 uint32_t fOpen,
347 PFNVDCOMPLETED pfnCompleted,
348 void **ppStorage)
349{
350 PVBOXDISK pThis = (PVBOXDISK)pvUser;
351 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)RTMemAllocZ(sizeof(DRVVDSTORAGEBACKEND));
352 int rc = VINF_SUCCESS;
353
354 if (pStorageBackend)
355 {
356 pStorageBackend->fSyncIoPending = false;
357 pStorageBackend->rcReqLast = VINF_SUCCESS;
358 pStorageBackend->pfnCompleted = pfnCompleted;
359
360 rc = RTSemEventCreate(&pStorageBackend->EventSem);
361 if (RT_SUCCESS(rc))
362 {
363 rc = PDMDrvHlpAsyncCompletionTemplateCreate(pThis->pDrvIns, &pStorageBackend->pTemplate,
364 drvvdAsyncTaskCompleted, pStorageBackend, "AsyncTaskCompleted");
365 if (RT_SUCCESS(rc))
366 {
367 uint32_t fFlags = (fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ
368 ? PDMACEP_FILE_FLAGS_READ_ONLY
369 : 0;
370 if (pThis->fShareable)
371 {
372 Assert((fOpen & RTFILE_O_DENY_MASK) == RTFILE_O_DENY_NONE);
373
374 fFlags |= PDMACEP_FILE_FLAGS_DONT_LOCK;
375 }
376 if (pThis->fAsyncIoWithHostCache)
377 fFlags |= PDMACEP_FILE_FLAGS_HOST_CACHE_ENABLED;
378
379 rc = PDMR3AsyncCompletionEpCreateForFile(&pStorageBackend->pEndpoint,
380 pszLocation, fFlags,
381 pStorageBackend->pTemplate);
382
383 if (RT_SUCCESS(rc))
384 {
385 if (pThis->pszBwGroup)
386 rc = PDMR3AsyncCompletionEpSetBwMgr(pStorageBackend->pEndpoint, pThis->pszBwGroup);
387
388 if (RT_SUCCESS(rc))
389 {
390 *ppStorage = pStorageBackend;
391 return VINF_SUCCESS;
392 }
393
394 PDMR3AsyncCompletionEpClose(pStorageBackend->pEndpoint);
395 }
396
397 PDMR3AsyncCompletionTemplateDestroy(pStorageBackend->pTemplate);
398 }
399 RTSemEventDestroy(pStorageBackend->EventSem);
400 }
401 RTMemFree(pStorageBackend);
402 }
403 else
404 rc = VERR_NO_MEMORY;
405
406 return rc;
407}
408
409static DECLCALLBACK(int) drvvdAsyncIOClose(void *pvUser, void *pStorage)
410{
411 PVBOXDISK pThis = (PVBOXDISK)pvUser;
412 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
413
414 PDMR3AsyncCompletionEpClose(pStorageBackend->pEndpoint);
415 PDMR3AsyncCompletionTemplateDestroy(pStorageBackend->pTemplate);
416 RTSemEventDestroy(pStorageBackend->EventSem);
417 RTMemFree(pStorageBackend);
418
419 return VINF_SUCCESS;;
420}
421
422static DECLCALLBACK(int) drvvdAsyncIOReadSync(void *pvUser, void *pStorage, uint64_t uOffset,
423 void *pvBuf, size_t cbRead, size_t *pcbRead)
424{
425 PVBOXDISK pThis = (PVBOXDISK)pvUser;
426 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
427 RTSGSEG DataSeg;
428 PPDMASYNCCOMPLETIONTASK pTask;
429
430 Assert(!pStorageBackend->fSyncIoPending);
431 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
432 DataSeg.cbSeg = cbRead;
433 DataSeg.pvSeg = pvBuf;
434
435 int rc = PDMR3AsyncCompletionEpRead(pStorageBackend->pEndpoint, uOffset, &DataSeg, 1, cbRead, NULL, &pTask);
436 if (RT_FAILURE(rc))
437 return rc;
438
439 if (rc == VINF_AIO_TASK_PENDING)
440 {
441 /* Wait */
442 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
443 AssertRC(rc);
444 }
445 else
446 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
447
448 if (pcbRead)
449 *pcbRead = cbRead;
450
451 return pStorageBackend->rcReqLast;
452}
453
454static DECLCALLBACK(int) drvvdAsyncIOWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
455 const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
456{
457 PVBOXDISK pThis = (PVBOXDISK)pvUser;
458 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
459 RTSGSEG DataSeg;
460 PPDMASYNCCOMPLETIONTASK pTask;
461
462 Assert(!pStorageBackend->fSyncIoPending);
463 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
464 DataSeg.cbSeg = cbWrite;
465 DataSeg.pvSeg = (void *)pvBuf;
466
467 int rc = PDMR3AsyncCompletionEpWrite(pStorageBackend->pEndpoint, uOffset, &DataSeg, 1, cbWrite, NULL, &pTask);
468 if (RT_FAILURE(rc))
469 return rc;
470
471 if (rc == VINF_AIO_TASK_PENDING)
472 {
473 /* Wait */
474 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
475 AssertRC(rc);
476 }
477 else
478 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
479
480 if (pcbWritten)
481 *pcbWritten = cbWrite;
482
483 return pStorageBackend->rcReqLast;
484}
485
486static DECLCALLBACK(int) drvvdAsyncIOFlushSync(void *pvUser, void *pStorage)
487{
488 PVBOXDISK pThis = (PVBOXDISK)pvUser;
489 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
490 PPDMASYNCCOMPLETIONTASK pTask;
491
492 LogFlowFunc(("pvUser=%#p pStorage=%#p\n", pvUser, pStorage));
493
494 Assert(!pStorageBackend->fSyncIoPending);
495 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
496
497 int rc = PDMR3AsyncCompletionEpFlush(pStorageBackend->pEndpoint, NULL, &pTask);
498 if (RT_FAILURE(rc))
499 return rc;
500
501 if (rc == VINF_AIO_TASK_PENDING)
502 {
503 /* Wait */
504 LogFlowFunc(("Waiting for flush to complete\n"));
505 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
506 AssertRC(rc);
507 }
508 else
509 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
510
511 return pStorageBackend->rcReqLast;
512}
513
514static DECLCALLBACK(int) drvvdAsyncIOReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
515 PCRTSGSEG paSegments, size_t cSegments,
516 size_t cbRead, void *pvCompletion,
517 void **ppTask)
518{
519 PVBOXDISK pThis = (PVBOXDISK)pvUser;
520 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
521
522 int rc = PDMR3AsyncCompletionEpRead(pStorageBackend->pEndpoint, uOffset, paSegments, cSegments, cbRead,
523 pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask);
524 if (rc == VINF_AIO_TASK_PENDING)
525 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
526
527 return rc;
528}
529
530static DECLCALLBACK(int) drvvdAsyncIOWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
531 PCRTSGSEG paSegments, size_t cSegments,
532 size_t cbWrite, void *pvCompletion,
533 void **ppTask)
534{
535 PVBOXDISK pThis = (PVBOXDISK)pvUser;
536 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
537
538 int rc = PDMR3AsyncCompletionEpWrite(pStorageBackend->pEndpoint, uOffset, paSegments, cSegments, cbWrite,
539 pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask);
540 if (rc == VINF_AIO_TASK_PENDING)
541 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
542
543 return rc;
544}
545
546static DECLCALLBACK(int) drvvdAsyncIOFlushAsync(void *pvUser, void *pStorage,
547 void *pvCompletion, void **ppTask)
548{
549 PVBOXDISK pThis = (PVBOXDISK)pvUser;
550 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
551
552 int rc = PDMR3AsyncCompletionEpFlush(pStorageBackend->pEndpoint, pvCompletion,
553 (PPPDMASYNCCOMPLETIONTASK)ppTask);
554 if (rc == VINF_AIO_TASK_PENDING)
555 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
556
557 return rc;
558}
559
560static DECLCALLBACK(int) drvvdAsyncIOGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
561{
562 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
563 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
564
565 return PDMR3AsyncCompletionEpGetSize(pStorageBackend->pEndpoint, pcbSize);
566}
567
568static DECLCALLBACK(int) drvvdAsyncIOSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
569{
570 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
571 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
572
573 int rc = drvvdAsyncIOFlushSync(pvUser, pStorage);
574 if (RT_SUCCESS(rc))
575 rc = PDMR3AsyncCompletionEpSetSize(pStorageBackend->pEndpoint, cbSize);
576
577 return rc;
578}
579
580#endif /* VBOX_WITH_PDM_ASYNC_COMPLETION */
581
582
583/*******************************************************************************
584* VD Thread Synchronization interface implementation *
585*******************************************************************************/
586
587static DECLCALLBACK(int) drvvdThreadStartRead(void *pvUser)
588{
589 PVBOXDISK pThis = (PVBOXDISK)pvUser;
590
591 return RTSemRWRequestRead(pThis->MergeLock, RT_INDEFINITE_WAIT);
592}
593
594static DECLCALLBACK(int) drvvdThreadFinishRead(void *pvUser)
595{
596 PVBOXDISK pThis = (PVBOXDISK)pvUser;
597
598 return RTSemRWReleaseRead(pThis->MergeLock);
599}
600
601static DECLCALLBACK(int) drvvdThreadStartWrite(void *pvUser)
602{
603 PVBOXDISK pThis = (PVBOXDISK)pvUser;
604
605 return RTSemRWRequestWrite(pThis->MergeLock, RT_INDEFINITE_WAIT);
606}
607
608static DECLCALLBACK(int) drvvdThreadFinishWrite(void *pvUser)
609{
610 PVBOXDISK pThis = (PVBOXDISK)pvUser;
611
612 return RTSemRWReleaseWrite(pThis->MergeLock);
613}
614
615
616/*******************************************************************************
617* VD Configuration interface implementation *
618*******************************************************************************/
619
620static bool drvvdCfgAreKeysValid(void *pvUser, const char *pszzValid)
621{
622 return CFGMR3AreValuesValid((PCFGMNODE)pvUser, pszzValid);
623}
624
625static int drvvdCfgQuerySize(void *pvUser, const char *pszName, size_t *pcb)
626{
627 return CFGMR3QuerySize((PCFGMNODE)pvUser, pszName, pcb);
628}
629
630static int drvvdCfgQuery(void *pvUser, const char *pszName, char *pszString, size_t cchString)
631{
632 return CFGMR3QueryString((PCFGMNODE)pvUser, pszName, pszString, cchString);
633}
634
635
636#ifdef VBOX_WITH_INIP
637/*******************************************************************************
638* VD TCP network stack interface implementation - INIP case *
639*******************************************************************************/
640
641typedef union INIPSOCKADDRUNION
642{
643 struct sockaddr Addr;
644 struct sockaddr_in Ipv4;
645} INIPSOCKADDRUNION;
646
647typedef struct INIPSOCKET
648{
649 int hSock;
650} INIPSOCKET, *PINIPSOCKET;
651
652static DECLCALLBACK(int) drvvdINIPFlush(VDSOCKET Sock);
653
654/** @copydoc VDINTERFACETCPNET::pfnSocketCreate */
655static DECLCALLBACK(int) drvvdINIPSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
656{
657 PINIPSOCKET pSocketInt = NULL;
658
659 /*
660 * The extended select method is not supported because it is impossible to wakeup
661 * the thread.
662 */
663 if (fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT)
664 return VERR_NOT_SUPPORTED;
665
666 pSocketInt = (PINIPSOCKET)RTMemAllocZ(sizeof(INIPSOCKET));
667 if (pSocketInt)
668 {
669 pSocketInt->hSock = INT32_MAX;
670 *pSock = (VDSOCKET)pSocketInt;
671 return VINF_SUCCESS;
672 }
673
674 return VERR_NO_MEMORY;
675}
676
677/** @copydoc VDINTERFACETCPNET::pfnSocketCreate */
678static DECLCALLBACK(int) drvvdINIPSocketDestroy(VDSOCKET Sock)
679{
680 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
681
682 RTMemFree(pSocketInt);
683 return VINF_SUCCESS;
684}
685
686/** @copydoc VDINTERFACETCPNET::pfnClientConnect */
687static DECLCALLBACK(int) drvvdINIPClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort)
688{
689 int rc = VINF_SUCCESS;
690 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
691
692 /* Check whether lwIP is set up in this VM instance. */
693 if (!DevINIPConfigured())
694 {
695 LogRelFunc(("no IP stack\n"));
696 return VERR_NET_HOST_UNREACHABLE;
697 }
698 /* Resolve hostname. As there is no standard resolver for lwIP yet,
699 * just accept numeric IP addresses for now. */
700 struct in_addr ip;
701 if (!lwip_inet_aton(pszAddress, &ip))
702 {
703 LogRelFunc(("cannot resolve IP %s\n", pszAddress));
704 return VERR_NET_HOST_UNREACHABLE;
705 }
706 /* Create socket and connect. */
707 int iSock = lwip_socket(PF_INET, SOCK_STREAM, 0);
708 if (iSock != -1)
709 {
710 struct sockaddr_in InAddr = {0};
711 InAddr.sin_family = AF_INET;
712 InAddr.sin_port = htons(uPort);
713 InAddr.sin_addr = ip;
714 if (!lwip_connect(iSock, (struct sockaddr *)&InAddr, sizeof(InAddr)))
715 {
716 pSocketInt->hSock = iSock;
717 return VINF_SUCCESS;
718 }
719 rc = VERR_NET_CONNECTION_REFUSED; /* @todo real solution needed */
720 lwip_close(iSock);
721 }
722 else
723 rc = VERR_NET_CONNECTION_REFUSED; /* @todo real solution needed */
724 return rc;
725}
726
727/** @copydoc VDINTERFACETCPNET::pfnClientClose */
728static DECLCALLBACK(int) drvvdINIPClientClose(VDSOCKET Sock)
729{
730 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
731
732 lwip_close(pSocketInt->hSock);
733 pSocketInt->hSock = INT32_MAX;
734 return VINF_SUCCESS; /** @todo real solution needed */
735}
736
737/** @copydoc VDINTERFACETCPNET::pfnIsClientConnected */
738static DECLCALLBACK(bool) drvvdINIPIsClientConnected(VDSOCKET Sock)
739{
740 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
741
742 return pSocketInt->hSock != INT32_MAX;
743}
744
745/** @copydoc VDINTERFACETCPNET::pfnSelectOne */
746static DECLCALLBACK(int) drvvdINIPSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
747{
748 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
749 fd_set fdsetR;
750 FD_ZERO(&fdsetR);
751 FD_SET((uintptr_t)pSocketInt->hSock, &fdsetR);
752 fd_set fdsetE = fdsetR;
753
754 int rc;
755 if (cMillies == RT_INDEFINITE_WAIT)
756 rc = lwip_select(pSocketInt->hSock + 1, &fdsetR, NULL, &fdsetE, NULL);
757 else
758 {
759 struct timeval timeout;
760 timeout.tv_sec = cMillies / 1000;
761 timeout.tv_usec = (cMillies % 1000) * 1000;
762 rc = lwip_select(pSocketInt->hSock + 1, &fdsetR, NULL, &fdsetE, &timeout);
763 }
764 if (rc > 0)
765 return VINF_SUCCESS;
766 if (rc == 0)
767 return VERR_TIMEOUT;
768 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
769}
770
771/** @copydoc VDINTERFACETCPNET::pfnRead */
772static DECLCALLBACK(int) drvvdINIPRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
773{
774 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
775
776 /* Do params checking */
777 if (!pvBuffer || !cbBuffer)
778 {
779 AssertMsgFailed(("Invalid params\n"));
780 return VERR_INVALID_PARAMETER;
781 }
782
783 /*
784 * Read loop.
785 * If pcbRead is NULL we have to fill the entire buffer!
786 */
787 size_t cbRead = 0;
788 size_t cbToRead = cbBuffer;
789 for (;;)
790 {
791 /** @todo this clipping here is just in case (the send function
792 * needed it, so I added it here, too). Didn't investigate if this
793 * really has issues. Better be safe than sorry. */
794 ssize_t cbBytesRead = lwip_recv(pSocketInt->hSock, (char *)pvBuffer + cbRead,
795 RT_MIN(cbToRead, 32768), 0);
796 if (cbBytesRead < 0)
797 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution */
798 if (cbBytesRead == 0 && errno) /** @todo r=bird: lwip_recv will not touch errno on Windows. This may apply to other hosts as well */
799 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution */
800 if (pcbRead)
801 {
802 /* return partial data */
803 *pcbRead = cbBytesRead;
804 break;
805 }
806
807 /* read more? */
808 cbRead += cbBytesRead;
809 if (cbRead == cbBuffer)
810 break;
811
812 /* next */
813 cbToRead = cbBuffer - cbRead;
814 }
815
816 return VINF_SUCCESS;
817}
818
819/** @copydoc VDINTERFACETCPNET::pfnWrite */
820static DECLCALLBACK(int) drvvdINIPWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
821{
822 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
823
824 do
825 {
826 /** @todo lwip send only supports up to 65535 bytes in a single
827 * send (stupid limitation buried in the code), so make sure we
828 * don't get any wraparounds. This should be moved to DevINIP
829 * stack interface once that's implemented. */
830 ssize_t cbWritten = lwip_send(pSocketInt->hSock, (void *)pvBuffer,
831 RT_MIN(cbBuffer, 32768), 0);
832 if (cbWritten < 0)
833 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
834 AssertMsg(cbBuffer >= (size_t)cbWritten, ("Wrote more than we requested!!! cbWritten=%d cbBuffer=%d\n",
835 cbWritten, cbBuffer));
836 cbBuffer -= cbWritten;
837 pvBuffer = (const char *)pvBuffer + cbWritten;
838 } while (cbBuffer);
839
840 return VINF_SUCCESS;
841}
842
843/** @copydoc VDINTERFACETCPNET::pfnSgWrite */
844static DECLCALLBACK(int) drvvdINIPSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
845{
846 int rc = VINF_SUCCESS;
847
848 /* This is an extremely crude emulation, however it's good enough
849 * for our iSCSI code. INIP has no sendmsg(). */
850 for (unsigned i = 0; i < pSgBuf->cSegs; i++)
851 {
852 rc = drvvdINIPWrite(Sock, pSgBuf->paSegs[i].pvSeg,
853 pSgBuf->paSegs[i].cbSeg);
854 if (RT_FAILURE(rc))
855 break;
856 }
857 if (RT_SUCCESS(rc))
858 drvvdINIPFlush(Sock);
859
860 return rc;
861}
862
863/** @copydoc VDINTERFACETCPNET::pfnFlush */
864static DECLCALLBACK(int) drvvdINIPFlush(VDSOCKET Sock)
865{
866 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
867
868 int fFlag = 1;
869 lwip_setsockopt(pSocketInt->hSock, IPPROTO_TCP, TCP_NODELAY,
870 (const char *)&fFlag, sizeof(fFlag));
871 fFlag = 0;
872 lwip_setsockopt(pSocketInt->hSock, IPPROTO_TCP, TCP_NODELAY,
873 (const char *)&fFlag, sizeof(fFlag));
874 return VINF_SUCCESS;
875}
876
877/** @copydoc VDINTERFACETCPNET::pfnSetSendCoalescing */
878static DECLCALLBACK(int) drvvdINIPSetSendCoalescing(VDSOCKET Sock, bool fEnable)
879{
880 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
881
882 int fFlag = fEnable ? 0 : 1;
883 lwip_setsockopt(pSocketInt->hSock, IPPROTO_TCP, TCP_NODELAY,
884 (const char *)&fFlag, sizeof(fFlag));
885 return VINF_SUCCESS;
886}
887
888/** @copydoc VDINTERFACETCPNET::pfnGetLocalAddress */
889static DECLCALLBACK(int) drvvdINIPGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
890{
891 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
892 INIPSOCKADDRUNION u;
893 socklen_t cbAddr = sizeof(u);
894 RT_ZERO(u);
895 if (!lwip_getsockname(pSocketInt->hSock, &u.Addr, &cbAddr))
896 {
897 /*
898 * Convert the address.
899 */
900 if ( cbAddr == sizeof(struct sockaddr_in)
901 && u.Addr.sa_family == AF_INET)
902 {
903 RT_ZERO(*pAddr);
904 pAddr->enmType = RTNETADDRTYPE_IPV4;
905 pAddr->uPort = RT_N2H_U16(u.Ipv4.sin_port);
906 pAddr->uAddr.IPv4.u = u.Ipv4.sin_addr.s_addr;
907 }
908 else
909 return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
910 return VINF_SUCCESS;
911 }
912 return VERR_NET_OPERATION_NOT_SUPPORTED;
913}
914
915/** @copydoc VDINTERFACETCPNET::pfnGetPeerAddress */
916static DECLCALLBACK(int) drvvdINIPGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
917{
918 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
919 INIPSOCKADDRUNION u;
920 socklen_t cbAddr = sizeof(u);
921 RT_ZERO(u);
922 if (!lwip_getpeername(pSocketInt->hSock, &u.Addr, &cbAddr))
923 {
924 /*
925 * Convert the address.
926 */
927 if ( cbAddr == sizeof(struct sockaddr_in)
928 && u.Addr.sa_family == AF_INET)
929 {
930 RT_ZERO(*pAddr);
931 pAddr->enmType = RTNETADDRTYPE_IPV4;
932 pAddr->uPort = RT_N2H_U16(u.Ipv4.sin_port);
933 pAddr->uAddr.IPv4.u = u.Ipv4.sin_addr.s_addr;
934 }
935 else
936 return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
937 return VINF_SUCCESS;
938 }
939 return VERR_NET_OPERATION_NOT_SUPPORTED;
940}
941
942/** @copydoc VDINTERFACETCPNET::pfnSelectOneEx */
943static DECLCALLBACK(int) drvvdINIPSelectOneEx(VDSOCKET Sock, uint32_t fEvents, uint32_t *pfEvents, RTMSINTERVAL cMillies)
944{
945 AssertMsgFailed(("Not supported!\n"));
946 return VERR_NOT_SUPPORTED;
947}
948
949/** @copydoc VDINTERFACETCPNET::pfnPoke */
950static DECLCALLBACK(int) drvvdINIPPoke(VDSOCKET Sock)
951{
952 AssertMsgFailed(("Not supported!\n"));
953 return VERR_NOT_SUPPORTED;
954}
955
956#endif /* VBOX_WITH_INIP */
957
958
959/*******************************************************************************
960* VD TCP network stack interface implementation - Host TCP case *
961*******************************************************************************/
962
963/**
964 * Socket data.
965 */
966typedef struct VDSOCKETINT
967{
968 /** IPRT socket handle. */
969 RTSOCKET hSocket;
970 /** Pollset with the wakeup pipe and socket. */
971 RTPOLLSET hPollSet;
972 /** Pipe endpoint - read (in the pollset). */
973 RTPIPE hPipeR;
974 /** Pipe endpoint - write. */
975 RTPIPE hPipeW;
976 /** Flag whether the thread was woken up. */
977 volatile bool fWokenUp;
978 /** Flag whether the thread is waiting in the select call. */
979 volatile bool fWaiting;
980 /** Old event mask. */
981 uint32_t fEventsOld;
982} VDSOCKETINT, *PVDSOCKETINT;
983
984/** Pollset id of the socket. */
985#define VDSOCKET_POLL_ID_SOCKET 0
986/** Pollset id of the pipe. */
987#define VDSOCKET_POLL_ID_PIPE 1
988
989/** @copydoc VDINTERFACETCPNET::pfnSocketCreate */
990static DECLCALLBACK(int) drvvdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
991{
992 int rc = VINF_SUCCESS;
993 int rc2 = VINF_SUCCESS;
994 PVDSOCKETINT pSockInt = NULL;
995
996 pSockInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
997 if (!pSockInt)
998 return VERR_NO_MEMORY;
999
1000 pSockInt->hSocket = NIL_RTSOCKET;
1001 pSockInt->hPollSet = NIL_RTPOLLSET;
1002 pSockInt->hPipeR = NIL_RTPIPE;
1003 pSockInt->hPipeW = NIL_RTPIPE;
1004 pSockInt->fWokenUp = false;
1005 pSockInt->fWaiting = false;
1006
1007 if (fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT)
1008 {
1009 /* Init pipe and pollset. */
1010 rc = RTPipeCreate(&pSockInt->hPipeR, &pSockInt->hPipeW, 0);
1011 if (RT_SUCCESS(rc))
1012 {
1013 rc = RTPollSetCreate(&pSockInt->hPollSet);
1014 if (RT_SUCCESS(rc))
1015 {
1016 rc = RTPollSetAddPipe(pSockInt->hPollSet, pSockInt->hPipeR,
1017 RTPOLL_EVT_READ, VDSOCKET_POLL_ID_PIPE);
1018 if (RT_SUCCESS(rc))
1019 {
1020 *pSock = pSockInt;
1021 return VINF_SUCCESS;
1022 }
1023
1024 RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_PIPE);
1025 rc2 = RTPollSetDestroy(pSockInt->hPollSet);
1026 AssertRC(rc2);
1027 }
1028
1029 rc2 = RTPipeClose(pSockInt->hPipeR);
1030 AssertRC(rc2);
1031 rc2 = RTPipeClose(pSockInt->hPipeW);
1032 AssertRC(rc2);
1033 }
1034 }
1035 else
1036 {
1037 *pSock = pSockInt;
1038 return VINF_SUCCESS;
1039 }
1040
1041 RTMemFree(pSockInt);
1042
1043 return rc;
1044}
1045
1046/** @copydoc VDINTERFACETCPNET::pfnSocketDestroy */
1047static DECLCALLBACK(int) drvvdTcpSocketDestroy(VDSOCKET Sock)
1048{
1049 int rc = VINF_SUCCESS;
1050 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1051
1052 /* Destroy the pipe and pollset if necessary. */
1053 if (pSockInt->hPollSet != NIL_RTPOLLSET)
1054 {
1055 if (pSockInt->hSocket != NIL_RTSOCKET)
1056 {
1057 rc = RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_SOCKET);
1058 Assert(RT_SUCCESS(rc) || rc == VERR_POLL_HANDLE_ID_NOT_FOUND);
1059 }
1060 rc = RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_PIPE);
1061 AssertRC(rc);
1062 rc = RTPollSetDestroy(pSockInt->hPollSet);
1063 AssertRC(rc);
1064 rc = RTPipeClose(pSockInt->hPipeR);
1065 AssertRC(rc);
1066 rc = RTPipeClose(pSockInt->hPipeW);
1067 AssertRC(rc);
1068 }
1069
1070 if (pSockInt->hSocket != NIL_RTSOCKET)
1071 rc = RTTcpClientCloseEx(pSockInt->hSocket, false /*fGracefulShutdown*/);
1072
1073 RTMemFree(pSockInt);
1074
1075 return rc;
1076}
1077
1078/** @copydoc VDINTERFACETCPNET::pfnClientConnect */
1079static DECLCALLBACK(int) drvvdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort)
1080{
1081 int rc = VINF_SUCCESS;
1082 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1083
1084 rc = RTTcpClientConnect(pszAddress, uPort, &pSockInt->hSocket);
1085 if (RT_SUCCESS(rc))
1086 {
1087 /* Add to the pollset if required. */
1088 if (pSockInt->hPollSet != NIL_RTPOLLSET)
1089 {
1090 pSockInt->fEventsOld = RTPOLL_EVT_READ | RTPOLL_EVT_WRITE | RTPOLL_EVT_ERROR;
1091
1092 rc = RTPollSetAddSocket(pSockInt->hPollSet, pSockInt->hSocket,
1093 pSockInt->fEventsOld, VDSOCKET_POLL_ID_SOCKET);
1094 }
1095
1096 if (RT_SUCCESS(rc))
1097 return VINF_SUCCESS;
1098
1099 rc = RTTcpClientCloseEx(pSockInt->hSocket, false /*fGracefulShutdown*/);
1100 }
1101
1102 return rc;
1103}
1104
1105/** @copydoc VDINTERFACETCPNET::pfnClientClose */
1106static DECLCALLBACK(int) drvvdTcpClientClose(VDSOCKET Sock)
1107{
1108 int rc = VINF_SUCCESS;
1109 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1110
1111 if (pSockInt->hPollSet != NIL_RTPOLLSET)
1112 {
1113 rc = RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_SOCKET);
1114 AssertRC(rc);
1115 }
1116
1117 rc = RTTcpClientCloseEx(pSockInt->hSocket, false /*fGracefulShutdown*/);
1118 pSockInt->hSocket = NIL_RTSOCKET;
1119
1120 return rc;
1121}
1122
1123/** @copydoc VDINTERFACETCPNET::pfnIsClientConnected */
1124static DECLCALLBACK(bool) drvvdTcpIsClientConnected(VDSOCKET Sock)
1125{
1126 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1127
1128 return pSockInt->hSocket != NIL_RTSOCKET;
1129}
1130
1131/** @copydoc VDINTERFACETCPNET::pfnSelectOne */
1132static DECLCALLBACK(int) drvvdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
1133{
1134 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1135
1136 return RTTcpSelectOne(pSockInt->hSocket, cMillies);
1137}
1138
1139/** @copydoc VDINTERFACETCPNET::pfnRead */
1140static DECLCALLBACK(int) drvvdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
1141{
1142 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1143
1144 return RTTcpRead(pSockInt->hSocket, pvBuffer, cbBuffer, pcbRead);
1145}
1146
1147/** @copydoc VDINTERFACETCPNET::pfnWrite */
1148static DECLCALLBACK(int) drvvdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
1149{
1150 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1151
1152 return RTTcpWrite(pSockInt->hSocket, pvBuffer, cbBuffer);
1153}
1154
1155/** @copydoc VDINTERFACETCPNET::pfnSgWrite */
1156static DECLCALLBACK(int) drvvdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
1157{
1158 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1159
1160 return RTTcpSgWrite(pSockInt->hSocket, pSgBuf);
1161}
1162
1163/** @copydoc VDINTERFACETCPNET::pfnReadNB */
1164static DECLCALLBACK(int) drvvdTcpReadNB(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
1165{
1166 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1167
1168 return RTTcpReadNB(pSockInt->hSocket, pvBuffer, cbBuffer, pcbRead);
1169}
1170
1171/** @copydoc VDINTERFACETCPNET::pfnWriteNB */
1172static DECLCALLBACK(int) drvvdTcpWriteNB(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten)
1173{
1174 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1175
1176 return RTTcpWriteNB(pSockInt->hSocket, pvBuffer, cbBuffer, pcbWritten);
1177}
1178
1179/** @copydoc VDINTERFACETCPNET::pfnSgWriteNB */
1180static DECLCALLBACK(int) drvvdTcpSgWriteNB(VDSOCKET Sock, PRTSGBUF pSgBuf, size_t *pcbWritten)
1181{
1182 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1183
1184 return RTTcpSgWriteNB(pSockInt->hSocket, pSgBuf, pcbWritten);
1185}
1186
1187/** @copydoc VDINTERFACETCPNET::pfnFlush */
1188static DECLCALLBACK(int) drvvdTcpFlush(VDSOCKET Sock)
1189{
1190 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1191
1192 return RTTcpFlush(pSockInt->hSocket);
1193}
1194
1195/** @copydoc VDINTERFACETCPNET::pfnSetSendCoalescing */
1196static DECLCALLBACK(int) drvvdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
1197{
1198 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1199
1200 return RTTcpSetSendCoalescing(pSockInt->hSocket, fEnable);
1201}
1202
1203/** @copydoc VDINTERFACETCPNET::pfnGetLocalAddress */
1204static DECLCALLBACK(int) drvvdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
1205{
1206 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1207
1208 return RTTcpGetLocalAddress(pSockInt->hSocket, pAddr);
1209}
1210
1211/** @copydoc VDINTERFACETCPNET::pfnGetPeerAddress */
1212static DECLCALLBACK(int) drvvdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
1213{
1214 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1215
1216 return RTTcpGetPeerAddress(pSockInt->hSocket, pAddr);
1217}
1218
1219static int drvvdTcpSelectOneExPoll(VDSOCKET Sock, uint32_t fEvents,
1220 uint32_t *pfEvents, RTMSINTERVAL cMillies)
1221{
1222 int rc = VINF_SUCCESS;
1223 uint32_t id = 0;
1224 uint32_t fEventsRecv = 0;
1225 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1226
1227 *pfEvents = 0;
1228
1229 if ( pSockInt->fEventsOld != fEvents
1230 && pSockInt->hSocket != NIL_RTSOCKET)
1231 {
1232 uint32_t fPollEvents = 0;
1233
1234 if (fEvents & VD_INTERFACETCPNET_EVT_READ)
1235 fPollEvents |= RTPOLL_EVT_READ;
1236 if (fEvents & VD_INTERFACETCPNET_EVT_WRITE)
1237 fPollEvents |= RTPOLL_EVT_WRITE;
1238 if (fEvents & VD_INTERFACETCPNET_EVT_ERROR)
1239 fPollEvents |= RTPOLL_EVT_ERROR;
1240
1241 rc = RTPollSetEventsChange(pSockInt->hPollSet, VDSOCKET_POLL_ID_SOCKET, fPollEvents);
1242 if (RT_FAILURE(rc))
1243 return rc;
1244
1245 pSockInt->fEventsOld = fEvents;
1246 }
1247
1248 ASMAtomicXchgBool(&pSockInt->fWaiting, true);
1249 if (ASMAtomicXchgBool(&pSockInt->fWokenUp, false))
1250 {
1251 ASMAtomicXchgBool(&pSockInt->fWaiting, false);
1252 return VERR_INTERRUPTED;
1253 }
1254
1255 rc = RTPoll(pSockInt->hPollSet, cMillies, &fEventsRecv, &id);
1256 Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT);
1257
1258 ASMAtomicXchgBool(&pSockInt->fWaiting, false);
1259
1260 if (RT_SUCCESS(rc))
1261 {
1262 if (id == VDSOCKET_POLL_ID_SOCKET)
1263 {
1264 fEventsRecv &= RTPOLL_EVT_VALID_MASK;
1265
1266 if (fEventsRecv & RTPOLL_EVT_READ)
1267 *pfEvents |= VD_INTERFACETCPNET_EVT_READ;
1268 if (fEventsRecv & RTPOLL_EVT_WRITE)
1269 *pfEvents |= VD_INTERFACETCPNET_EVT_WRITE;
1270 if (fEventsRecv & RTPOLL_EVT_ERROR)
1271 *pfEvents |= VD_INTERFACETCPNET_EVT_ERROR;
1272 }
1273 else
1274 {
1275 size_t cbRead = 0;
1276 uint8_t abBuf[10];
1277 Assert(id == VDSOCKET_POLL_ID_PIPE);
1278 Assert((fEventsRecv & RTPOLL_EVT_VALID_MASK) == RTPOLL_EVT_READ);
1279
1280 /* We got interrupted, drain the pipe. */
1281 rc = RTPipeRead(pSockInt->hPipeR, abBuf, sizeof(abBuf), &cbRead);
1282 AssertRC(rc);
1283
1284 ASMAtomicXchgBool(&pSockInt->fWokenUp, false);
1285
1286 rc = VERR_INTERRUPTED;
1287 }
1288 }
1289
1290 return rc;
1291}
1292
1293/** @copydoc VDINTERFACETCPNET::pfnSelectOneEx */
1294static DECLCALLBACK(int) drvvdTcpSelectOneExNoPoll(VDSOCKET Sock, uint32_t fEvents,
1295 uint32_t *pfEvents, RTMSINTERVAL cMillies)
1296{
1297 int rc = VINF_SUCCESS;
1298 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1299
1300 *pfEvents = 0;
1301
1302 ASMAtomicXchgBool(&pSockInt->fWaiting, true);
1303 if (ASMAtomicXchgBool(&pSockInt->fWokenUp, false))
1304 {
1305 ASMAtomicXchgBool(&pSockInt->fWaiting, false);
1306 return VERR_INTERRUPTED;
1307 }
1308
1309 if ( pSockInt->hSocket == NIL_RTSOCKET
1310 || !fEvents)
1311 {
1312 /*
1313 * Only the pipe is configured or the caller doesn't wait for a socket event,
1314 * wait until there is something to read from the pipe.
1315 */
1316 size_t cbRead = 0;
1317 char ch = 0;
1318 rc = RTPipeReadBlocking(pSockInt->hPipeR, &ch, 1, &cbRead);
1319 if (RT_SUCCESS(rc))
1320 {
1321 Assert(cbRead == 1);
1322 rc = VERR_INTERRUPTED;
1323 ASMAtomicXchgBool(&pSockInt->fWokenUp, false);
1324 }
1325 }
1326 else
1327 {
1328 uint32_t fSelectEvents = 0;
1329
1330 if (fEvents & VD_INTERFACETCPNET_EVT_READ)
1331 fSelectEvents |= RTSOCKET_EVT_READ;
1332 if (fEvents & VD_INTERFACETCPNET_EVT_WRITE)
1333 fSelectEvents |= RTSOCKET_EVT_WRITE;
1334 if (fEvents & VD_INTERFACETCPNET_EVT_ERROR)
1335 fSelectEvents |= RTSOCKET_EVT_ERROR;
1336
1337 if (fEvents & VD_INTERFACETCPNET_HINT_INTERRUPT)
1338 {
1339 uint32_t fEventsRecv = 0;
1340
1341 /* Make sure the socket is not in the pollset. */
1342 rc = RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_SOCKET);
1343 Assert(RT_SUCCESS(rc) || rc == VERR_POLL_HANDLE_ID_NOT_FOUND);
1344
1345 for (;;)
1346 {
1347 uint32_t id = 0;
1348 rc = RTPoll(pSockInt->hPollSet, 5, &fEvents, &id);
1349 if (rc == VERR_TIMEOUT)
1350 {
1351 /* Check the socket. */
1352 rc = RTTcpSelectOneEx(pSockInt->hSocket, fSelectEvents, &fEventsRecv, 0);
1353 if (RT_SUCCESS(rc))
1354 {
1355 if (fEventsRecv & RTSOCKET_EVT_READ)
1356 *pfEvents |= VD_INTERFACETCPNET_EVT_READ;
1357 if (fEventsRecv & RTSOCKET_EVT_WRITE)
1358 *pfEvents |= VD_INTERFACETCPNET_EVT_WRITE;
1359 if (fEventsRecv & RTSOCKET_EVT_ERROR)
1360 *pfEvents |= VD_INTERFACETCPNET_EVT_ERROR;
1361 break; /* Quit */
1362 }
1363 else if (rc != VERR_TIMEOUT)
1364 break;
1365 }
1366 else if (RT_SUCCESS(rc))
1367 {
1368 size_t cbRead = 0;
1369 uint8_t abBuf[10];
1370 Assert(id == VDSOCKET_POLL_ID_PIPE);
1371 Assert((fEventsRecv & RTPOLL_EVT_VALID_MASK) == RTPOLL_EVT_READ);
1372
1373 /* We got interrupted, drain the pipe. */
1374 rc = RTPipeRead(pSockInt->hPipeR, abBuf, sizeof(abBuf), &cbRead);
1375 AssertRC(rc);
1376
1377 ASMAtomicXchgBool(&pSockInt->fWokenUp, false);
1378
1379 rc = VERR_INTERRUPTED;
1380 break;
1381 }
1382 else
1383 break;
1384 }
1385 }
1386 else /* The caller waits for a socket event. */
1387 {
1388 uint32_t fEventsRecv = 0;
1389
1390 /* Loop until we got woken up or a socket event occurred. */
1391 for (;;)
1392 {
1393 /** @todo find an adaptive wait algorithm based on the
1394 * number of wakeups in the past. */
1395 rc = RTTcpSelectOneEx(pSockInt->hSocket, fSelectEvents, &fEventsRecv, 5);
1396 if (rc == VERR_TIMEOUT)
1397 {
1398 /* Check if there is an event pending. */
1399 size_t cbRead = 0;
1400 char ch = 0;
1401 rc = RTPipeRead(pSockInt->hPipeR, &ch, 1, &cbRead);
1402 if (RT_SUCCESS(rc) && rc != VINF_TRY_AGAIN)
1403 {
1404 Assert(cbRead == 1);
1405 rc = VERR_INTERRUPTED;
1406 ASMAtomicXchgBool(&pSockInt->fWokenUp, false);
1407 break; /* Quit */
1408 }
1409 else
1410 Assert(rc == VINF_TRY_AGAIN);
1411 }
1412 else if (RT_SUCCESS(rc))
1413 {
1414 if (fEventsRecv & RTSOCKET_EVT_READ)
1415 *pfEvents |= VD_INTERFACETCPNET_EVT_READ;
1416 if (fEventsRecv & RTSOCKET_EVT_WRITE)
1417 *pfEvents |= VD_INTERFACETCPNET_EVT_WRITE;
1418 if (fEventsRecv & RTSOCKET_EVT_ERROR)
1419 *pfEvents |= VD_INTERFACETCPNET_EVT_ERROR;
1420 break; /* Quit */
1421 }
1422 else
1423 break;
1424 }
1425 }
1426 }
1427
1428 ASMAtomicXchgBool(&pSockInt->fWaiting, false);
1429
1430 return rc;
1431}
1432
1433/** @copydoc VDINTERFACETCPNET::pfnPoke */
1434static DECLCALLBACK(int) drvvdTcpPoke(VDSOCKET Sock)
1435{
1436 int rc = VINF_SUCCESS;
1437 size_t cbWritten = 0;
1438 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1439
1440 ASMAtomicXchgBool(&pSockInt->fWokenUp, true);
1441
1442 if (ASMAtomicReadBool(&pSockInt->fWaiting))
1443 {
1444 rc = RTPipeWrite(pSockInt->hPipeW, "", 1, &cbWritten);
1445 Assert(RT_SUCCESS(rc) || cbWritten == 0);
1446 }
1447
1448 return VINF_SUCCESS;
1449}
1450
1451
1452/*******************************************************************************
1453* Media interface methods *
1454*******************************************************************************/
1455
1456/** @copydoc PDMIMEDIA::pfnRead */
1457static DECLCALLBACK(int) drvvdRead(PPDMIMEDIA pInterface,
1458 uint64_t off, void *pvBuf, size_t cbRead)
1459{
1460 int rc = VINF_SUCCESS;
1461
1462 LogFlowFunc(("off=%#llx pvBuf=%p cbRead=%d\n", off, pvBuf, cbRead));
1463 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1464
1465 if (!pThis->fBootAccelActive)
1466 rc = VDRead(pThis->pDisk, off, pvBuf, cbRead);
1467 else
1468 {
1469 /* Can we serve the request from the buffer? */
1470 if ( off >= pThis->offDisk
1471 && off - pThis->offDisk < pThis->cbDataValid)
1472 {
1473 size_t cbToCopy = RT_MIN(cbRead, pThis->offDisk + pThis->cbDataValid - off);
1474
1475 memcpy(pvBuf, pThis->pbData + (off - pThis->offDisk), cbToCopy);
1476 cbRead -= cbToCopy;
1477 off += cbToCopy;
1478 pvBuf = (char *)pvBuf + cbToCopy;
1479 }
1480
1481 if ( cbRead > 0
1482 && cbRead < pThis->cbBootAccelBuffer)
1483 {
1484 /* Increase request to the buffer size and read. */
1485 pThis->cbDataValid = RT_MIN(pThis->cbDisk - off, pThis->cbBootAccelBuffer);
1486 pThis->offDisk = off;
1487 rc = VDRead(pThis->pDisk, off, pThis->pbData, pThis->cbDataValid);
1488 if (RT_FAILURE(rc))
1489 pThis->cbDataValid = 0;
1490 else
1491 memcpy(pvBuf, pThis->pbData, cbRead);
1492 }
1493 else if (cbRead >= pThis->cbBootAccelBuffer)
1494 {
1495 pThis->fBootAccelActive = false; /* Deactiviate */
1496 }
1497 }
1498
1499 if (RT_SUCCESS(rc))
1500 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d %.*Rhxd\n", __FUNCTION__,
1501 off, pvBuf, cbRead, cbRead, pvBuf));
1502 LogFlowFunc(("returns %Rrc\n", rc));
1503 return rc;
1504}
1505
1506/** @copydoc PDMIMEDIA::pfnWrite */
1507static DECLCALLBACK(int) drvvdWrite(PPDMIMEDIA pInterface,
1508 uint64_t off, const void *pvBuf,
1509 size_t cbWrite)
1510{
1511 LogFlowFunc(("off=%#llx pvBuf=%p cbWrite=%d\n", off, pvBuf, cbWrite));
1512 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1513 Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d %.*Rhxd\n", __FUNCTION__,
1514 off, pvBuf, cbWrite, cbWrite, pvBuf));
1515
1516 /* Invalidate any buffer if boot acceleration is enabled. */
1517 if (pThis->fBootAccelActive)
1518 {
1519 pThis->cbDataValid = 0;
1520 pThis->offDisk = 0;
1521 }
1522
1523 int rc = VDWrite(pThis->pDisk, off, pvBuf, cbWrite);
1524 LogFlowFunc(("returns %Rrc\n", rc));
1525 return rc;
1526}
1527
1528/** @copydoc PDMIMEDIA::pfnFlush */
1529static DECLCALLBACK(int) drvvdFlush(PPDMIMEDIA pInterface)
1530{
1531 LogFlowFunc(("\n"));
1532 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1533 int rc = VDFlush(pThis->pDisk);
1534 LogFlowFunc(("returns %Rrc\n", rc));
1535 return rc;
1536}
1537
1538/** @copydoc PDMIMEDIA::pfnMerge */
1539static DECLCALLBACK(int) drvvdMerge(PPDMIMEDIA pInterface,
1540 PFNSIMPLEPROGRESS pfnProgress,
1541 void *pvUser)
1542{
1543 LogFlowFunc(("\n"));
1544 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1545 int rc = VINF_SUCCESS;
1546
1547 /* Note: There is an unavoidable race between destruction and another
1548 * thread invoking this function. This is handled safely and gracefully by
1549 * atomically invalidating the lock handle in drvvdDestruct. */
1550 int rc2 = RTSemFastMutexRequest(pThis->MergeCompleteMutex);
1551 AssertRC(rc2);
1552 if (RT_SUCCESS(rc2) && pThis->fMergePending)
1553 {
1554 /* Take shortcut: PFNSIMPLEPROGRESS is exactly the same type as
1555 * PFNVDPROGRESS, so there's no need for a conversion function. */
1556 /** @todo maybe introduce a conversion which limits update frequency. */
1557 PVDINTERFACE pVDIfsOperation = NULL;
1558 VDINTERFACE VDIProgress;
1559 VDINTERFACEPROGRESS VDIProgressCallbacks;
1560 VDIProgressCallbacks.cbSize = sizeof(VDINTERFACEPROGRESS);
1561 VDIProgressCallbacks.enmInterface = VDINTERFACETYPE_PROGRESS;
1562 VDIProgressCallbacks.pfnProgress = pfnProgress;
1563 rc2 = VDInterfaceAdd(&VDIProgress, "DrvVD_VDIProgress", VDINTERFACETYPE_PROGRESS,
1564 &VDIProgressCallbacks, pvUser, &pVDIfsOperation);
1565 AssertRC(rc2);
1566 pThis->fMergePending = false;
1567 rc = VDMerge(pThis->pDisk, pThis->uMergeSource,
1568 pThis->uMergeTarget, pVDIfsOperation);
1569 }
1570 rc2 = RTSemFastMutexRelease(pThis->MergeCompleteMutex);
1571 AssertRC(rc2);
1572 LogFlowFunc(("returns %Rrc\n", rc));
1573 return rc;
1574}
1575
1576/** @copydoc PDMIMEDIA::pfnGetSize */
1577static DECLCALLBACK(uint64_t) drvvdGetSize(PPDMIMEDIA pInterface)
1578{
1579 LogFlowFunc(("\n"));
1580 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1581 uint64_t cb = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
1582 LogFlowFunc(("returns %#llx (%llu)\n", cb, cb));
1583 return cb;
1584}
1585
1586/** @copydoc PDMIMEDIA::pfnIsReadOnly */
1587static DECLCALLBACK(bool) drvvdIsReadOnly(PPDMIMEDIA pInterface)
1588{
1589 LogFlowFunc(("\n"));
1590 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1591 bool f = VDIsReadOnly(pThis->pDisk);
1592 LogFlowFunc(("returns %d\n", f));
1593 return f;
1594}
1595
1596/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
1597static DECLCALLBACK(int) drvvdBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
1598 PPDMMEDIAGEOMETRY pPCHSGeometry)
1599{
1600 LogFlowFunc(("\n"));
1601 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1602 VDGEOMETRY geo;
1603 int rc = VDGetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
1604 if (RT_SUCCESS(rc))
1605 {
1606 pPCHSGeometry->cCylinders = geo.cCylinders;
1607 pPCHSGeometry->cHeads = geo.cHeads;
1608 pPCHSGeometry->cSectors = geo.cSectors;
1609 }
1610 else
1611 {
1612 LogFunc(("geometry not available.\n"));
1613 rc = VERR_PDM_GEOMETRY_NOT_SET;
1614 }
1615 LogFlowFunc(("returns %Rrc (CHS=%d/%d/%d)\n",
1616 rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1617 return rc;
1618}
1619
1620/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
1621static DECLCALLBACK(int) drvvdBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
1622 PCPDMMEDIAGEOMETRY pPCHSGeometry)
1623{
1624 LogFlowFunc(("CHS=%d/%d/%d\n",
1625 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1626 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1627 VDGEOMETRY geo;
1628 geo.cCylinders = pPCHSGeometry->cCylinders;
1629 geo.cHeads = pPCHSGeometry->cHeads;
1630 geo.cSectors = pPCHSGeometry->cSectors;
1631 int rc = VDSetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
1632 if (rc == VERR_VD_GEOMETRY_NOT_SET)
1633 rc = VERR_PDM_GEOMETRY_NOT_SET;
1634 LogFlowFunc(("returns %Rrc\n", rc));
1635 return rc;
1636}
1637
1638/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
1639static DECLCALLBACK(int) drvvdBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
1640 PPDMMEDIAGEOMETRY pLCHSGeometry)
1641{
1642 LogFlowFunc(("\n"));
1643 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1644 VDGEOMETRY geo;
1645 int rc = VDGetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
1646 if (RT_SUCCESS(rc))
1647 {
1648 pLCHSGeometry->cCylinders = geo.cCylinders;
1649 pLCHSGeometry->cHeads = geo.cHeads;
1650 pLCHSGeometry->cSectors = geo.cSectors;
1651 }
1652 else
1653 {
1654 LogFunc(("geometry not available.\n"));
1655 rc = VERR_PDM_GEOMETRY_NOT_SET;
1656 }
1657 LogFlowFunc(("returns %Rrc (CHS=%d/%d/%d)\n",
1658 rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1659 return rc;
1660}
1661
1662/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
1663static DECLCALLBACK(int) drvvdBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
1664 PCPDMMEDIAGEOMETRY pLCHSGeometry)
1665{
1666 LogFlowFunc(("CHS=%d/%d/%d\n",
1667 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1668 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1669 VDGEOMETRY geo;
1670 geo.cCylinders = pLCHSGeometry->cCylinders;
1671 geo.cHeads = pLCHSGeometry->cHeads;
1672 geo.cSectors = pLCHSGeometry->cSectors;
1673 int rc = VDSetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
1674 if (rc == VERR_VD_GEOMETRY_NOT_SET)
1675 rc = VERR_PDM_GEOMETRY_NOT_SET;
1676 LogFlowFunc(("returns %Rrc\n", rc));
1677 return rc;
1678}
1679
1680/** @copydoc PDMIMEDIA::pfnGetUuid */
1681static DECLCALLBACK(int) drvvdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
1682{
1683 LogFlowFunc(("\n"));
1684 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1685 int rc = VDGetUuid(pThis->pDisk, 0, pUuid);
1686 LogFlowFunc(("returns %Rrc ({%RTuuid})\n", rc, pUuid));
1687 return rc;
1688}
1689
1690/*******************************************************************************
1691* Async Media interface methods *
1692*******************************************************************************/
1693
1694static void drvvdAsyncReqComplete(void *pvUser1, void *pvUser2, int rcReq)
1695{
1696 PVBOXDISK pThis = (PVBOXDISK)pvUser1;
1697
1698 if (!pThis->pBlkCache)
1699 {
1700 int rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort,
1701 pvUser2, rcReq);
1702 AssertRC(rc);
1703 }
1704 else
1705 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, (PPDMBLKCACHEIOXFER)pvUser2, rcReq);
1706}
1707
1708static DECLCALLBACK(int) drvvdStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
1709 PCRTSGSEG paSeg, unsigned cSeg,
1710 size_t cbRead, void *pvUser)
1711{
1712 LogFlowFunc(("uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d pvUser=%#p\n",
1713 uOffset, paSeg, cSeg, cbRead, pvUser));
1714 int rc = VINF_SUCCESS;
1715 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
1716
1717 pThis->fBootAccelActive = false;
1718
1719 RTSGBUF SgBuf;
1720 RTSgBufInit(&SgBuf, paSeg, cSeg);
1721 if (!pThis->pBlkCache)
1722 rc = VDAsyncRead(pThis->pDisk, uOffset, cbRead, &SgBuf,
1723 drvvdAsyncReqComplete, pThis, pvUser);
1724 else
1725 {
1726 rc = PDMR3BlkCacheRead(pThis->pBlkCache, uOffset, &SgBuf, cbRead, pvUser);
1727 if (rc == VINF_AIO_TASK_PENDING)
1728 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1729 else if (rc == VINF_SUCCESS)
1730 rc = VINF_VD_ASYNC_IO_FINISHED;
1731 }
1732
1733 LogFlowFunc(("returns %Rrc\n", rc));
1734 return rc;
1735}
1736
1737static DECLCALLBACK(int) drvvdStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
1738 PCRTSGSEG paSeg, unsigned cSeg,
1739 size_t cbWrite, void *pvUser)
1740{
1741 LogFlowFunc(("uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d pvUser=%#p\n",
1742 uOffset, paSeg, cSeg, cbWrite, pvUser));
1743 int rc = VINF_SUCCESS;
1744 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
1745
1746 pThis->fBootAccelActive = false;
1747
1748 RTSGBUF SgBuf;
1749 RTSgBufInit(&SgBuf, paSeg, cSeg);
1750
1751 if (!pThis->pBlkCache)
1752 rc = VDAsyncWrite(pThis->pDisk, uOffset, cbWrite, &SgBuf,
1753 drvvdAsyncReqComplete, pThis, pvUser);
1754 else
1755 {
1756 rc = PDMR3BlkCacheWrite(pThis->pBlkCache, uOffset, &SgBuf, cbWrite, pvUser);
1757 if (rc == VINF_AIO_TASK_PENDING)
1758 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1759 else if (rc == VINF_SUCCESS)
1760 rc = VINF_VD_ASYNC_IO_FINISHED;
1761 }
1762
1763 LogFlowFunc(("returns %Rrc\n", rc));
1764 return rc;
1765}
1766
1767static DECLCALLBACK(int) drvvdStartFlush(PPDMIMEDIAASYNC pInterface, void *pvUser)
1768{
1769 LogFlowFunc(("pvUser=%#p\n", pvUser));
1770 int rc = VINF_SUCCESS;
1771 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
1772
1773 if (!pThis->pBlkCache)
1774 rc = VDAsyncFlush(pThis->pDisk, drvvdAsyncReqComplete, pThis, pvUser);
1775 else
1776 {
1777 rc = PDMR3BlkCacheFlush(pThis->pBlkCache, pvUser);
1778 if (rc == VINF_AIO_TASK_PENDING)
1779 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1780 else if (rc == VINF_SUCCESS)
1781 rc = VINF_VD_ASYNC_IO_FINISHED;
1782 }
1783 LogFlowFunc(("returns %Rrc\n", rc));
1784 return rc;
1785}
1786
1787/** @copydoc FNPDMBLKCACHEXFERCOMPLETEDRV */
1788static void drvvdBlkCacheXferComplete(PPDMDRVINS pDrvIns, void *pvUser, int rcReq)
1789{
1790 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1791
1792 int rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort,
1793 pvUser, rcReq);
1794 AssertRC(rc);
1795}
1796
1797/** @copydoc FNPDMBLKCACHEXFERENQUEUEDRV */
1798static int drvvdBlkCacheXferEnqueue(PPDMDRVINS pDrvIns,
1799 PDMBLKCACHEXFERDIR enmXferDir,
1800 uint64_t off, size_t cbXfer,
1801 PCRTSGBUF pcSgBuf, PPDMBLKCACHEIOXFER hIoXfer)
1802{
1803 int rc = VINF_SUCCESS;
1804 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1805
1806 switch (enmXferDir)
1807 {
1808 case PDMBLKCACHEXFERDIR_READ:
1809 rc = VDAsyncRead(pThis->pDisk, off, cbXfer, pcSgBuf, drvvdAsyncReqComplete,
1810 pThis, hIoXfer);
1811 break;
1812 case PDMBLKCACHEXFERDIR_WRITE:
1813 rc = VDAsyncWrite(pThis->pDisk, off, cbXfer, pcSgBuf, drvvdAsyncReqComplete,
1814 pThis, hIoXfer);
1815 break;
1816 case PDMBLKCACHEXFERDIR_FLUSH:
1817 rc = VDAsyncFlush(pThis->pDisk, drvvdAsyncReqComplete, pThis, hIoXfer);
1818 break;
1819 default:
1820 AssertMsgFailed(("Invalid transfer type %d\n", enmXferDir));
1821 rc = VERR_INVALID_PARAMETER;
1822 }
1823
1824 if (rc == VINF_VD_ASYNC_IO_FINISHED)
1825 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, hIoXfer, VINF_SUCCESS);
1826 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
1827 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, hIoXfer, rc);
1828
1829 return VINF_SUCCESS;
1830}
1831
1832/*******************************************************************************
1833* Base interface methods *
1834*******************************************************************************/
1835
1836/**
1837 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1838 */
1839static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1840{
1841 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
1842 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1843
1844 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1845 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, &pThis->IMedia);
1846 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAASYNC, pThis->fAsyncIOSupported ? &pThis->IMediaAsync : NULL);
1847 return NULL;
1848}
1849
1850
1851/*******************************************************************************
1852* Saved state notification methods *
1853*******************************************************************************/
1854
1855/**
1856 * Load done callback for re-opening the image writable during teleportation.
1857 *
1858 * This is called both for successful and failed load runs, we only care about
1859 * successful ones.
1860 *
1861 * @returns VBox status code.
1862 * @param pDrvIns The driver instance.
1863 * @param pSSM The saved state handle.
1864 */
1865static DECLCALLBACK(int) drvvdLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1866{
1867 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1868 Assert(!pThis->fErrorUseRuntime);
1869
1870 /* Drop out if we don't have any work to do or if it's a failed load. */
1871 if ( !pThis->fTempReadOnly
1872 || RT_FAILURE(SSMR3HandleGetStatus(pSSM)))
1873 return VINF_SUCCESS;
1874
1875 int rc = drvvdSetWritable(pThis);
1876 if (RT_FAILURE(rc)) /** @todo does the bugger set any errors? */
1877 return SSMR3SetLoadError(pSSM, rc, RT_SRC_POS,
1878 N_("Failed to write lock the images"));
1879 return VINF_SUCCESS;
1880}
1881
1882
1883/*******************************************************************************
1884* Driver methods *
1885*******************************************************************************/
1886
1887/**
1888 * VM resume notification that we use to undo what the temporary read-only image
1889 * mode set by drvvdSuspend.
1890 *
1891 * Also switch to runtime error mode if we're resuming after a state load
1892 * without having been powered on first.
1893 *
1894 * @param pDrvIns The driver instance data.
1895 *
1896 * @todo The VMSetError vs VMSetRuntimeError mess must be fixed elsewhere,
1897 * we're making assumptions about Main behavior here!
1898 */
1899static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
1900{
1901 LogFlowFunc(("\n"));
1902 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1903
1904 drvvdSetWritable(pThis);
1905 pThis->fErrorUseRuntime = true;
1906
1907 if (pThis->pBlkCache)
1908 {
1909 int rc = PDMR3BlkCacheResume(pThis->pBlkCache);
1910 AssertRC(rc);
1911 }
1912}
1913
1914/**
1915 * The VM is being suspended, temporarily change to read-only image mode.
1916 *
1917 * This is important for several reasons:
1918 * -# It makes sure that there are no pending writes to the image. Most
1919 * backends implements this by closing and reopening the image in read-only
1920 * mode.
1921 * -# It allows Main to read the images during snapshotting without having
1922 * to account for concurrent writes.
1923 * -# This is essential for making teleportation targets sharing images work
1924 * right. Both with regards to caching and with regards to file sharing
1925 * locks (RTFILE_O_DENY_*). (See also drvvdLoadDone.)
1926 *
1927 * @param pDrvIns The driver instance data.
1928 */
1929static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
1930{
1931 LogFlowFunc(("\n"));
1932 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1933
1934 if (pThis->pBlkCache)
1935 {
1936 int rc = PDMR3BlkCacheSuspend(pThis->pBlkCache);
1937 AssertRC(rc);
1938 }
1939
1940 drvvdSetReadonly(pThis);
1941}
1942
1943/**
1944 * VM PowerOn notification for undoing the TempReadOnly config option and
1945 * changing to runtime error mode.
1946 *
1947 * @param pDrvIns The driver instance data.
1948 *
1949 * @todo The VMSetError vs VMSetRuntimeError mess must be fixed elsewhere,
1950 * we're making assumptions about Main behavior here!
1951 */
1952static DECLCALLBACK(void) drvvdPowerOn(PPDMDRVINS pDrvIns)
1953{
1954 LogFlowFunc(("\n"));
1955 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1956 drvvdSetWritable(pThis);
1957 pThis->fErrorUseRuntime = true;
1958}
1959
1960/**
1961 * @copydoc FNPDMDRVRESET
1962 */
1963static DECLCALLBACK(void) drvvdReset(PPDMDRVINS pDrvIns)
1964{
1965 LogFlowFunc(("\n"));
1966 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1967
1968 if (pThis->fBootAccelEnabled)
1969 {
1970 pThis->fBootAccelActive = true;
1971 pThis->cbDataValid = 0;
1972 pThis->offDisk = 0;
1973 }
1974}
1975
1976/**
1977 * @copydoc FNPDMDRVDESTRUCT
1978 */
1979static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
1980{
1981 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1982 LogFlowFunc(("\n"));
1983 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1984
1985 RTSEMFASTMUTEX mutex;
1986 ASMAtomicXchgHandle(&pThis->MergeCompleteMutex, NIL_RTSEMFASTMUTEX, &mutex);
1987 if (mutex != NIL_RTSEMFASTMUTEX)
1988 {
1989 /* Request the semaphore to wait until a potentially running merge
1990 * operation has been finished. */
1991 int rc = RTSemFastMutexRequest(mutex);
1992 AssertRC(rc);
1993 pThis->fMergePending = false;
1994 rc = RTSemFastMutexRelease(mutex);
1995 AssertRC(rc);
1996 rc = RTSemFastMutexDestroy(mutex);
1997 AssertRC(rc);
1998 }
1999
2000 if (VALID_PTR(pThis->pBlkCache))
2001 {
2002 PDMR3BlkCacheRelease(pThis->pBlkCache);
2003 pThis->pBlkCache = NULL;
2004 }
2005
2006 if (VALID_PTR(pThis->pDisk))
2007 {
2008 VDDestroy(pThis->pDisk);
2009 pThis->pDisk = NULL;
2010 }
2011 drvvdFreeImages(pThis);
2012
2013 if (pThis->MergeLock != NIL_RTSEMRW)
2014 {
2015 int rc = RTSemRWDestroy(pThis->MergeLock);
2016 AssertRC(rc);
2017 pThis->MergeLock = NIL_RTSEMRW;
2018 }
2019 if (pThis->pbData)
2020 RTMemFree(pThis->pbData);
2021 if (pThis->pszBwGroup)
2022 {
2023 MMR3HeapFree(pThis->pszBwGroup);
2024 pThis->pszBwGroup = NULL;
2025 }
2026}
2027
2028/**
2029 * Construct a VBox disk media driver instance.
2030 *
2031 * @copydoc FNPDMDRVCONSTRUCT
2032 */
2033static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
2034 PCFGMNODE pCfg,
2035 uint32_t fFlags)
2036{
2037 LogFlowFunc(("\n"));
2038 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
2039 int rc = VINF_SUCCESS;
2040 char *pszName = NULL; /**< The path of the disk image file. */
2041 char *pszFormat = NULL; /**< The format backed to use for this image. */
2042 char *pszCachePath = NULL; /**< The path to the cache image. */
2043 char *pszCacheFormat = NULL; /**< The format backend to use for the cache image. */
2044 bool fReadOnly; /**< True if the media is read-only. */
2045 bool fMaybeReadOnly; /**< True if the media may or may not be read-only. */
2046 bool fHonorZeroWrites; /**< True if zero blocks should be written. */
2047 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
2048
2049 /*
2050 * Init the static parts.
2051 */
2052 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
2053 pThis->pDrvIns = pDrvIns;
2054 pThis->fTempReadOnly = false;
2055 pThis->pDisk = NULL;
2056 pThis->fAsyncIOSupported = false;
2057 pThis->fShareable = false;
2058 pThis->fMergePending = false;
2059 pThis->MergeCompleteMutex = NIL_RTSEMFASTMUTEX;
2060 pThis->uMergeSource = VD_LAST_IMAGE;
2061 pThis->uMergeTarget = VD_LAST_IMAGE;
2062
2063 /* IMedia */
2064 pThis->IMedia.pfnRead = drvvdRead;
2065 pThis->IMedia.pfnWrite = drvvdWrite;
2066 pThis->IMedia.pfnFlush = drvvdFlush;
2067 pThis->IMedia.pfnMerge = drvvdMerge;
2068 pThis->IMedia.pfnGetSize = drvvdGetSize;
2069 pThis->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
2070 pThis->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
2071 pThis->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
2072 pThis->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
2073 pThis->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
2074 pThis->IMedia.pfnGetUuid = drvvdGetUuid;
2075
2076 /* IMediaAsync */
2077 pThis->IMediaAsync.pfnStartRead = drvvdStartRead;
2078 pThis->IMediaAsync.pfnStartWrite = drvvdStartWrite;
2079 pThis->IMediaAsync.pfnStartFlush = drvvdStartFlush;
2080
2081 /* Initialize supported VD interfaces. */
2082 pThis->pVDIfsDisk = NULL;
2083
2084 pThis->VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
2085 pThis->VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
2086 pThis->VDIErrorCallbacks.pfnError = drvvdErrorCallback;
2087 pThis->VDIErrorCallbacks.pfnMessage = NULL;
2088
2089 rc = VDInterfaceAdd(&pThis->VDIError, "DrvVD_VDIError", VDINTERFACETYPE_ERROR,
2090 &pThis->VDIErrorCallbacks, pDrvIns, &pThis->pVDIfsDisk);
2091 AssertRC(rc);
2092
2093 /* This is just prepared here, the actual interface is per-image, so it's
2094 * added later. No need to have separate callback tables. */
2095 pThis->VDIConfigCallbacks.cbSize = sizeof(VDINTERFACECONFIG);
2096 pThis->VDIConfigCallbacks.enmInterface = VDINTERFACETYPE_CONFIG;
2097 pThis->VDIConfigCallbacks.pfnAreKeysValid = drvvdCfgAreKeysValid;
2098 pThis->VDIConfigCallbacks.pfnQuerySize = drvvdCfgQuerySize;
2099 pThis->VDIConfigCallbacks.pfnQuery = drvvdCfgQuery;
2100
2101 /* List of images is empty now. */
2102 pThis->pImages = NULL;
2103
2104 pThis->pDrvMediaPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAPORT);
2105 if (!pThis->pDrvMediaPort)
2106 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
2107 N_("No media port interface above"));
2108
2109 /* Try to attach async media port interface above.*/
2110 pThis->pDrvMediaAsyncPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAASYNCPORT);
2111
2112 /*
2113 * Validate configuration and find all parent images.
2114 * It's sort of up side down from the image dependency tree.
2115 */
2116 bool fHostIP = false;
2117 bool fUseNewIo = false;
2118 bool fUseBlockCache = false;
2119 unsigned iLevel = 0;
2120 PCFGMNODE pCurNode = pCfg;
2121 VDTYPE enmType = VDTYPE_HDD;
2122
2123 for (;;)
2124 {
2125 bool fValid;
2126
2127 if (pCurNode == pCfg)
2128 {
2129 /* Toplevel configuration additionally contains the global image
2130 * open flags. Some might be converted to per-image flags later. */
2131 fValid = CFGMR3AreValuesValid(pCurNode,
2132 "Format\0Path\0"
2133 "ReadOnly\0MaybeReadOnly\0TempReadOnly\0Shareable\0HonorZeroWrites\0"
2134 "HostIPStack\0UseNewIo\0BootAcceleration\0BootAccelerationBuffer\0"
2135 "SetupMerge\0MergeSource\0MergeTarget\0BwGroup\0Type\0BlockCache\0"
2136 "CachePath\0CacheFormat\0");
2137 }
2138 else
2139 {
2140 /* All other image configurations only contain image name and
2141 * the format information. */
2142 fValid = CFGMR3AreValuesValid(pCurNode, "Format\0Path\0"
2143 "MergeSource\0MergeTarget\0");
2144 }
2145 if (!fValid)
2146 {
2147 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
2148 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
2149 break;
2150 }
2151
2152 if (pCurNode == pCfg)
2153 {
2154 rc = CFGMR3QueryBoolDef(pCurNode, "HostIPStack", &fHostIP, true);
2155 if (RT_FAILURE(rc))
2156 {
2157 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2158 N_("DrvVD: Configuration error: Querying \"HostIPStack\" as boolean failed"));
2159 break;
2160 }
2161
2162 rc = CFGMR3QueryBoolDef(pCurNode, "HonorZeroWrites", &fHonorZeroWrites, false);
2163 if (RT_FAILURE(rc))
2164 {
2165 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2166 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
2167 break;
2168 }
2169
2170 rc = CFGMR3QueryBoolDef(pCurNode, "ReadOnly", &fReadOnly, false);
2171 if (RT_FAILURE(rc))
2172 {
2173 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2174 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
2175 break;
2176 }
2177
2178 rc = CFGMR3QueryBoolDef(pCurNode, "MaybeReadOnly", &fMaybeReadOnly, false);
2179 if (RT_FAILURE(rc))
2180 {
2181 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2182 N_("DrvVD: Configuration error: Querying \"MaybeReadOnly\" as boolean failed"));
2183 break;
2184 }
2185
2186 rc = CFGMR3QueryBoolDef(pCurNode, "TempReadOnly", &pThis->fTempReadOnly, false);
2187 if (RT_FAILURE(rc))
2188 {
2189 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2190 N_("DrvVD: Configuration error: Querying \"TempReadOnly\" as boolean failed"));
2191 break;
2192 }
2193 if (fReadOnly && pThis->fTempReadOnly)
2194 {
2195 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2196 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"TempReadOnly\" are set"));
2197 break;
2198 }
2199
2200 rc = CFGMR3QueryBoolDef(pCurNode, "Shareable", &pThis->fShareable, false);
2201 if (RT_FAILURE(rc))
2202 {
2203 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2204 N_("DrvVD: Configuration error: Querying \"Shareable\" as boolean failed"));
2205 break;
2206 }
2207
2208 rc = CFGMR3QueryBoolDef(pCurNode, "UseNewIo", &fUseNewIo, false);
2209 if (RT_FAILURE(rc))
2210 {
2211 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2212 N_("DrvVD: Configuration error: Querying \"UseNewIo\" as boolean failed"));
2213 break;
2214 }
2215 rc = CFGMR3QueryBoolDef(pCurNode, "SetupMerge", &pThis->fMergePending, false);
2216 if (RT_FAILURE(rc))
2217 {
2218 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2219 N_("DrvVD: Configuration error: Querying \"SetupMerge\" as boolean failed"));
2220 break;
2221 }
2222 if (fReadOnly && pThis->fMergePending)
2223 {
2224 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2225 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"MergePending\" are set"));
2226 break;
2227 }
2228 rc = CFGMR3QueryBoolDef(pCurNode, "BootAcceleration", &pThis->fBootAccelEnabled, false);
2229 if (RT_FAILURE(rc))
2230 {
2231 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2232 N_("DrvVD: Configuration error: Querying \"BootAcceleration\" as boolean failed"));
2233 break;
2234 }
2235 rc = CFGMR3QueryU32Def(pCurNode, "BootAccelerationBuffer", (uint32_t *)&pThis->cbBootAccelBuffer, 16 * _1K);
2236 if (RT_FAILURE(rc))
2237 {
2238 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2239 N_("DrvVD: Configuration error: Querying \"BootAccelerationBuffer\" as integer failed"));
2240 break;
2241 }
2242 rc = CFGMR3QueryBoolDef(pCurNode, "BlockCache", &fUseBlockCache, false);
2243 if (RT_FAILURE(rc))
2244 {
2245 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2246 N_("DrvVD: Configuration error: Querying \"BlockCache\" as boolean failed"));
2247 break;
2248 }
2249 rc = CFGMR3QueryStringAlloc(pCurNode, "BwGroup", &pThis->pszBwGroup);
2250 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
2251 {
2252 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2253 N_("DrvVD: Configuration error: Querying \"BwGroup\" as string failed"));
2254 break;
2255 }
2256 else
2257 rc = VINF_SUCCESS;
2258
2259 char *psz;
2260 rc = CFGMR3QueryStringAlloc(pCfg, "Type", &psz);
2261 if (RT_FAILURE(rc))
2262 {
2263 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_BLOCK_NO_TYPE, N_("Failed to obtain the type"));
2264 break;
2265 }
2266 else if (!strcmp(psz, "HardDisk"))
2267 enmType = VDTYPE_HDD;
2268 else if (!strcmp(psz, "DVD"))
2269 enmType = VDTYPE_DVD;
2270 else if (!strcmp(psz, "Floppy"))
2271 enmType = VDTYPE_FLOPPY;
2272 else
2273 {
2274 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_BLOCK_UNKNOWN_TYPE, RT_SRC_POS,
2275 N_("Unknown type \"%s\""), psz);
2276 MMR3HeapFree(psz);
2277 break;
2278 }
2279 MMR3HeapFree(psz); psz = NULL;
2280
2281 rc = CFGMR3QueryStringAlloc(pCurNode, "CachePath", &pszCachePath);
2282 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
2283 {
2284 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2285 N_("DrvVD: Configuration error: Querying \"CachePath\" as string failed"));
2286 break;
2287 }
2288 else
2289 rc = VINF_SUCCESS;
2290
2291 if (pszCachePath)
2292 {
2293 rc = CFGMR3QueryStringAlloc(pCurNode, "CacheFormat", &pszCacheFormat);
2294 if (RT_FAILURE(rc))
2295 {
2296 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2297 N_("DrvVD: Configuration error: Querying \"CacheFormat\" as string failed"));
2298 break;
2299 }
2300 }
2301 }
2302
2303 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
2304 if (!pParent)
2305 break;
2306 pCurNode = pParent;
2307 iLevel++;
2308 }
2309
2310 /*
2311 * Create the image container and the necessary interfaces.
2312 */
2313 if (RT_SUCCESS(rc))
2314 {
2315 /* Construct TCPNET callback table depending on the config. This is
2316 * done unconditionally, as uninterested backends will ignore it. */
2317 if (fHostIP)
2318 {
2319 pThis->VDITcpNetCallbacks.cbSize = sizeof(VDINTERFACETCPNET);
2320 pThis->VDITcpNetCallbacks.enmInterface = VDINTERFACETYPE_TCPNET;
2321 pThis->VDITcpNetCallbacks.pfnSocketCreate = drvvdTcpSocketCreate;
2322 pThis->VDITcpNetCallbacks.pfnSocketDestroy = drvvdTcpSocketDestroy;
2323 pThis->VDITcpNetCallbacks.pfnClientConnect = drvvdTcpClientConnect;
2324 pThis->VDITcpNetCallbacks.pfnIsClientConnected = drvvdTcpIsClientConnected;
2325 pThis->VDITcpNetCallbacks.pfnClientClose = drvvdTcpClientClose;
2326 pThis->VDITcpNetCallbacks.pfnSelectOne = drvvdTcpSelectOne;
2327 pThis->VDITcpNetCallbacks.pfnRead = drvvdTcpRead;
2328 pThis->VDITcpNetCallbacks.pfnWrite = drvvdTcpWrite;
2329 pThis->VDITcpNetCallbacks.pfnSgWrite = drvvdTcpSgWrite;
2330 pThis->VDITcpNetCallbacks.pfnReadNB = drvvdTcpReadNB;
2331 pThis->VDITcpNetCallbacks.pfnWriteNB = drvvdTcpWriteNB;
2332 pThis->VDITcpNetCallbacks.pfnSgWriteNB = drvvdTcpSgWriteNB;
2333 pThis->VDITcpNetCallbacks.pfnFlush = drvvdTcpFlush;
2334 pThis->VDITcpNetCallbacks.pfnSetSendCoalescing = drvvdTcpSetSendCoalescing;
2335 pThis->VDITcpNetCallbacks.pfnGetLocalAddress = drvvdTcpGetLocalAddress;
2336 pThis->VDITcpNetCallbacks.pfnGetPeerAddress = drvvdTcpGetPeerAddress;
2337
2338 /*
2339 * There is a 15ms delay between receiving the data and marking the socket
2340 * as readable on Windows XP which hurts async I/O performance of
2341 * TCP backends badly. Provide a different select method without
2342 * using poll on XP.
2343 * This is only used on XP because it is not as efficient as the one using poll
2344 * and all other Windows versions are working fine.
2345 */
2346 char szOS[64];
2347 memset(szOS, 0, sizeof(szOS));
2348 rc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, &szOS[0], sizeof(szOS));
2349
2350 if (RT_SUCCESS(rc) && !strncmp(szOS, "Windows XP", 10))
2351 {
2352 LogRel(("VD: Detected Windows XP, disabled poll based waiting for TCP\n"));
2353 pThis->VDITcpNetCallbacks.pfnSelectOneEx = drvvdTcpSelectOneExNoPoll;
2354 }
2355 else
2356 pThis->VDITcpNetCallbacks.pfnSelectOneEx = drvvdTcpSelectOneExPoll;
2357
2358 pThis->VDITcpNetCallbacks.pfnPoke = drvvdTcpPoke;
2359 }
2360 else
2361 {
2362#ifndef VBOX_WITH_INIP
2363 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
2364 RT_SRC_POS, N_("DrvVD: Configuration error: TCP over Internal Networking not compiled in"));
2365#else /* VBOX_WITH_INIP */
2366 pThis->VDITcpNetCallbacks.cbSize = sizeof(VDINTERFACETCPNET);
2367 pThis->VDITcpNetCallbacks.enmInterface = VDINTERFACETYPE_TCPNET;
2368 pThis->VDITcpNetCallbacks.pfnSocketCreate = drvvdINIPSocketCreate;
2369 pThis->VDITcpNetCallbacks.pfnSocketDestroy = drvvdINIPSocketDestroy;
2370 pThis->VDITcpNetCallbacks.pfnClientConnect = drvvdINIPClientConnect;
2371 pThis->VDITcpNetCallbacks.pfnClientClose = drvvdINIPClientClose;
2372 pThis->VDITcpNetCallbacks.pfnIsClientConnected = drvvdINIPIsClientConnected;
2373 pThis->VDITcpNetCallbacks.pfnSelectOne = drvvdINIPSelectOne;
2374 pThis->VDITcpNetCallbacks.pfnRead = drvvdINIPRead;
2375 pThis->VDITcpNetCallbacks.pfnWrite = drvvdINIPWrite;
2376 pThis->VDITcpNetCallbacks.pfnSgWrite = drvvdINIPSgWrite;
2377 pThis->VDITcpNetCallbacks.pfnFlush = drvvdINIPFlush;
2378 pThis->VDITcpNetCallbacks.pfnSetSendCoalescing = drvvdINIPSetSendCoalescing;
2379 pThis->VDITcpNetCallbacks.pfnGetLocalAddress = drvvdINIPGetLocalAddress;
2380 pThis->VDITcpNetCallbacks.pfnGetPeerAddress = drvvdINIPGetPeerAddress;
2381 pThis->VDITcpNetCallbacks.pfnSelectOneEx = drvvdINIPSelectOneEx;
2382 pThis->VDITcpNetCallbacks.pfnPoke = drvvdINIPPoke;
2383#endif /* VBOX_WITH_INIP */
2384 }
2385
2386 /*
2387 * The image has a bandwidth group but the host cache is enabled.
2388 * Use the async I/O framework but tell it to enable the host cache.
2389 */
2390 if (!fUseNewIo && pThis->pszBwGroup)
2391 {
2392 pThis->fAsyncIoWithHostCache = true;
2393 fUseNewIo = true;
2394 }
2395
2396 /** @todo quick hack to work around problems in the async I/O
2397 * implementation (rw semaphore thread ownership problem)
2398 * while a merge is running. Remove once this is fixed. */
2399 if (pThis->fMergePending)
2400 fUseNewIo = false;
2401
2402 if (RT_SUCCESS(rc) && fUseNewIo)
2403 {
2404#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
2405 pThis->VDIIOCallbacks.cbSize = sizeof(VDINTERFACEIO);
2406 pThis->VDIIOCallbacks.enmInterface = VDINTERFACETYPE_IO;
2407 pThis->VDIIOCallbacks.pfnOpen = drvvdAsyncIOOpen;
2408 pThis->VDIIOCallbacks.pfnClose = drvvdAsyncIOClose;
2409 pThis->VDIIOCallbacks.pfnGetSize = drvvdAsyncIOGetSize;
2410 pThis->VDIIOCallbacks.pfnSetSize = drvvdAsyncIOSetSize;
2411 pThis->VDIIOCallbacks.pfnReadSync = drvvdAsyncIOReadSync;
2412 pThis->VDIIOCallbacks.pfnWriteSync = drvvdAsyncIOWriteSync;
2413 pThis->VDIIOCallbacks.pfnFlushSync = drvvdAsyncIOFlushSync;
2414 pThis->VDIIOCallbacks.pfnReadAsync = drvvdAsyncIOReadAsync;
2415 pThis->VDIIOCallbacks.pfnWriteAsync = drvvdAsyncIOWriteAsync;
2416 pThis->VDIIOCallbacks.pfnFlushAsync = drvvdAsyncIOFlushAsync;
2417#else /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
2418 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
2419 RT_SRC_POS, N_("DrvVD: Configuration error: Async Completion Framework not compiled in"));
2420#endif /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
2421 }
2422
2423 if (RT_SUCCESS(rc) && pThis->fMergePending)
2424 {
2425 rc = RTSemFastMutexCreate(&pThis->MergeCompleteMutex);
2426 if (RT_SUCCESS(rc))
2427 rc = RTSemRWCreate(&pThis->MergeLock);
2428 if (RT_SUCCESS(rc))
2429 {
2430 pThis->VDIThreadSyncCallbacks.cbSize = sizeof(VDINTERFACETHREADSYNC);
2431 pThis->VDIThreadSyncCallbacks.enmInterface = VDINTERFACETYPE_THREADSYNC;
2432 pThis->VDIThreadSyncCallbacks.pfnStartRead = drvvdThreadStartRead;
2433 pThis->VDIThreadSyncCallbacks.pfnFinishRead = drvvdThreadFinishRead;
2434 pThis->VDIThreadSyncCallbacks.pfnStartWrite = drvvdThreadStartWrite;
2435 pThis->VDIThreadSyncCallbacks.pfnFinishWrite = drvvdThreadFinishWrite;
2436
2437 rc = VDInterfaceAdd(&pThis->VDIThreadSync, "DrvVD_ThreadSync", VDINTERFACETYPE_THREADSYNC,
2438 &pThis->VDIThreadSyncCallbacks, pThis, &pThis->pVDIfsDisk);
2439 }
2440 else
2441 {
2442 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2443 N_("DrvVD: Failed to create semaphores for \"MergePending\""));
2444 }
2445 }
2446
2447 if (RT_SUCCESS(rc))
2448 {
2449 rc = VDCreate(pThis->pVDIfsDisk, enmType, &pThis->pDisk);
2450 /* Error message is already set correctly. */
2451 }
2452 }
2453
2454 if (pThis->pDrvMediaAsyncPort && fUseNewIo)
2455 pThis->fAsyncIOSupported = true;
2456
2457 unsigned iImageIdx = 0;
2458 while (pCurNode && RT_SUCCESS(rc))
2459 {
2460 /* Allocate per-image data. */
2461 PVBOXIMAGE pImage = drvvdNewImage(pThis);
2462 if (!pImage)
2463 {
2464 rc = VERR_NO_MEMORY;
2465 break;
2466 }
2467
2468 /*
2469 * Read the image configuration.
2470 */
2471 rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
2472 if (RT_FAILURE(rc))
2473 {
2474 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2475 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
2476 break;
2477 }
2478
2479 rc = CFGMR3QueryStringAlloc(pCurNode, "Format", &pszFormat);
2480 if (RT_FAILURE(rc))
2481 {
2482 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2483 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
2484 break;
2485 }
2486
2487 bool fMergeSource;
2488 rc = CFGMR3QueryBoolDef(pCurNode, "MergeSource", &fMergeSource, false);
2489 if (RT_FAILURE(rc))
2490 {
2491 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2492 N_("DrvVD: Configuration error: Querying \"MergeSource\" as boolean failed"));
2493 break;
2494 }
2495 if (fMergeSource)
2496 {
2497 if (pThis->uMergeSource == VD_LAST_IMAGE)
2498 pThis->uMergeSource = iImageIdx;
2499 else
2500 {
2501 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2502 N_("DrvVD: Configuration error: Multiple \"MergeSource\" occurrences"));
2503 break;
2504 }
2505 }
2506
2507 bool fMergeTarget;
2508 rc = CFGMR3QueryBoolDef(pCurNode, "MergeTarget", &fMergeTarget, false);
2509 if (RT_FAILURE(rc))
2510 {
2511 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2512 N_("DrvVD: Configuration error: Querying \"MergeTarget\" as boolean failed"));
2513 break;
2514 }
2515 if (fMergeTarget)
2516 {
2517 if (pThis->uMergeTarget == VD_LAST_IMAGE)
2518 pThis->uMergeTarget = iImageIdx;
2519 else
2520 {
2521 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2522 N_("DrvVD: Configuration error: Multiple \"MergeTarget\" occurrences"));
2523 break;
2524 }
2525 }
2526
2527 PCFGMNODE pCfgVDConfig = CFGMR3GetChild(pCurNode, "VDConfig");
2528 rc = VDInterfaceAdd(&pImage->VDIConfig, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
2529 &pThis->VDIConfigCallbacks, pCfgVDConfig, &pImage->pVDIfsImage);
2530 AssertRC(rc);
2531
2532 /* Unconditionally insert the TCPNET interface, don't bother to check
2533 * if an image really needs it. Will be ignored. Since the TCPNET
2534 * interface is per image we could make this more flexible in the
2535 * future if we want to. */
2536 rc = VDInterfaceAdd(&pImage->VDITcpNet, "DrvVD_TCPNET",
2537 VDINTERFACETYPE_TCPNET, &pThis->VDITcpNetCallbacks,
2538 NULL, &pImage->pVDIfsImage);
2539 AssertRC(rc);
2540
2541 /* Insert the custom I/O interface only if we're told to use new IO.
2542 * Since the I/O interface is per image we could make this more
2543 * flexible in the future if we want to. */
2544 if (fUseNewIo)
2545 {
2546 rc = VDInterfaceAdd(&pImage->VDIIO, "DrvVD_IO", VDINTERFACETYPE_IO,
2547 &pThis->VDIIOCallbacks, pThis,
2548 &pImage->pVDIfsImage);
2549 AssertRC(rc);
2550 }
2551
2552 /*
2553 * Open the image.
2554 */
2555 unsigned uOpenFlags;
2556 if (fReadOnly || pThis->fTempReadOnly || iLevel != 0)
2557 uOpenFlags = VD_OPEN_FLAGS_READONLY;
2558 else
2559 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
2560 if (fHonorZeroWrites)
2561 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
2562 if (pThis->fAsyncIOSupported)
2563 uOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
2564 if (pThis->fShareable)
2565 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
2566
2567 /* Try to open backend in async I/O mode first. */
2568 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
2569 if (rc == VERR_NOT_SUPPORTED)
2570 {
2571 pThis->fAsyncIOSupported = false;
2572 uOpenFlags &= ~VD_OPEN_FLAGS_ASYNC_IO;
2573 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
2574 }
2575
2576 if (RT_SUCCESS(rc))
2577 {
2578 LogFunc(("%d - Opened '%s' in %s mode\n",
2579 iLevel, pszName,
2580 VDIsReadOnly(pThis->pDisk) ? "read-only" : "read-write"));
2581 if ( VDIsReadOnly(pThis->pDisk)
2582 && !fReadOnly
2583 && !fMaybeReadOnly
2584 && !pThis->fTempReadOnly
2585 && iLevel == 0)
2586 {
2587 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_VD_IMAGE_READ_ONLY, RT_SRC_POS,
2588 N_("Failed to open image '%s' for writing due to wrong permissions"),
2589 pszName);
2590 break;
2591 }
2592 }
2593 else
2594 {
2595 rc = PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
2596 N_("Failed to open image '%s' in %s mode rc=%Rrc"), pszName,
2597 (uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "read-only" : "read-write", rc);
2598 break;
2599 }
2600
2601
2602 MMR3HeapFree(pszName);
2603 pszName = NULL;
2604 MMR3HeapFree(pszFormat);
2605 pszFormat = NULL;
2606
2607 /* next */
2608 iLevel--;
2609 iImageIdx++;
2610 pCurNode = CFGMR3GetParent(pCurNode);
2611 }
2612
2613 /* Open the cache image if set. */
2614 if ( RT_SUCCESS(rc)
2615 && VALID_PTR(pszCachePath))
2616 {
2617 /* Insert the custom I/O interface only if we're told to use new IO.
2618 * Since the I/O interface is per image we could make this more
2619 * flexible in the future if we want to. */
2620 if (fUseNewIo)
2621 {
2622 rc = VDInterfaceAdd(&pThis->VDIIOCache, "DrvVD_IO", VDINTERFACETYPE_IO,
2623 &pThis->VDIIOCallbacks, pThis,
2624 &pThis->pVDIfsCache);
2625 AssertRC(rc);
2626 }
2627
2628 rc = VDCacheOpen(pThis->pDisk, pszCacheFormat, pszCachePath, VD_OPEN_FLAGS_NORMAL, pThis->pVDIfsCache);
2629 if (RT_FAILURE(rc))
2630 rc = PDMDRV_SET_ERROR(pDrvIns, rc, N_("DrvVD: Could not open cache image"));
2631 }
2632
2633 if (VALID_PTR(pszCachePath))
2634 MMR3HeapFree(pszCachePath);
2635 if (VALID_PTR(pszCacheFormat))
2636 MMR3HeapFree(pszCacheFormat);
2637
2638 if ( RT_SUCCESS(rc)
2639 && pThis->fMergePending
2640 && ( pThis->uMergeSource == VD_LAST_IMAGE
2641 || pThis->uMergeTarget == VD_LAST_IMAGE))
2642 {
2643 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2644 N_("DrvVD: Configuration error: Inconsistent image merge data"));
2645 }
2646
2647 /* Create the block cache if enabled. */
2648 if ( fUseBlockCache
2649 && !pThis->fShareable
2650 && RT_SUCCESS(rc))
2651 {
2652 /*
2653 * We need a unique ID for the block cache (to identify the owner of data
2654 * blocks in a saved state). UUIDs are not really suitable because
2655 * there are image formats which don't support them. Furthermore it is
2656 * possible that a new diff image was attached after a saved state
2657 * which changes the UUID.
2658 * However the device "name + device instance + LUN" triple the disk is
2659 * attached to is always constant for saved states.
2660 */
2661 char *pszId = NULL;
2662 uint32_t iInstance, iLUN;
2663 const char *pcszController;
2664
2665 rc = pThis->pDrvMediaPort->pfnQueryDeviceLocation(pThis->pDrvMediaPort, &pcszController,
2666 &iInstance, &iLUN);
2667 if (RT_FAILURE(rc))
2668 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2669 N_("DrvVD: Configuration error: Could not query device data"));
2670 else
2671 {
2672 int cbStr = RTStrAPrintf(&pszId, "%s-%d-%d", pcszController, iInstance, iLUN);
2673
2674 if (cbStr > 0)
2675 {
2676 rc = PDMDrvHlpBlkCacheRetain(pDrvIns, &pThis->pBlkCache,
2677 drvvdBlkCacheXferComplete,
2678 drvvdBlkCacheXferEnqueue,
2679 pszId);
2680 if (rc == VERR_NOT_SUPPORTED)
2681 {
2682 LogRel(("VD: Block cache is not supported\n"));
2683 rc = VINF_SUCCESS;
2684 }
2685 else
2686 AssertRC(rc);
2687
2688 RTStrFree(pszId);
2689 }
2690 else
2691 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2692 N_("DrvVD: Out of memory when creating block cache"));
2693 }
2694 }
2695
2696 /*
2697 * Register a load-done callback so we can undo TempReadOnly config before
2698 * we get to drvvdResume. Autoamtically deregistered upon destruction.
2699 */
2700 if (RT_SUCCESS(rc))
2701 rc = PDMDrvHlpSSMRegisterEx(pDrvIns, 0 /* version */, 0 /* cbGuess */,
2702 NULL /*pfnLivePrep*/, NULL /*pfnLiveExec*/, NULL /*pfnLiveVote*/,
2703 NULL /*pfnSavePrep*/, NULL /*pfnSaveExec*/, NULL /*pfnSaveDone*/,
2704 NULL /*pfnDonePrep*/, NULL /*pfnLoadExec*/, drvvdLoadDone);
2705
2706 /* Setup the boot acceleration stuff if enabled. */
2707 if (RT_SUCCESS(rc) && pThis->fBootAccelEnabled)
2708 {
2709 pThis->cbDisk = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
2710 Assert(pThis->cbDisk > 0);
2711 pThis->pbData = (uint8_t *)RTMemAllocZ(pThis->cbBootAccelBuffer);
2712 if (pThis->pbData)
2713 {
2714 pThis->fBootAccelActive = true;
2715 pThis->offDisk = 0;
2716 pThis->cbDataValid = 0;
2717 LogRel(("VD: Boot acceleration enabled\n"));
2718 }
2719 else
2720 LogRel(("VD: Boot acceleration, out of memory, disabled\n"));
2721 }
2722
2723 if (RT_FAILURE(rc))
2724 {
2725 if (VALID_PTR(pszName))
2726 MMR3HeapFree(pszName);
2727 if (VALID_PTR(pszFormat))
2728 MMR3HeapFree(pszFormat);
2729 /* drvvdDestruct does the rest. */
2730 }
2731
2732 LogFlowFunc(("returns %Rrc\n", rc));
2733 return rc;
2734}
2735
2736/**
2737 * VBox disk container media driver registration record.
2738 */
2739const PDMDRVREG g_DrvVD =
2740{
2741 /* u32Version */
2742 PDM_DRVREG_VERSION,
2743 /* szName */
2744 "VD",
2745 /* szRCMod */
2746 "",
2747 /* szR0Mod */
2748 "",
2749 /* pszDescription */
2750 "Generic VBox disk media driver.",
2751 /* fFlags */
2752 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2753 /* fClass. */
2754 PDM_DRVREG_CLASS_MEDIA,
2755 /* cMaxInstances */
2756 ~0,
2757 /* cbInstance */
2758 sizeof(VBOXDISK),
2759 /* pfnConstruct */
2760 drvvdConstruct,
2761 /* pfnDestruct */
2762 drvvdDestruct,
2763 /* pfnRelocate */
2764 NULL,
2765 /* pfnIOCtl */
2766 NULL,
2767 /* pfnPowerOn */
2768 drvvdPowerOn,
2769 /* pfnReset */
2770 drvvdReset,
2771 /* pfnSuspend */
2772 drvvdSuspend,
2773 /* pfnResume */
2774 drvvdResume,
2775 /* pfnAttach */
2776 NULL,
2777 /* pfnDetach */
2778 NULL,
2779 /* pfnPowerOff */
2780 NULL,
2781 /* pfnSoftReset */
2782 NULL,
2783 /* u32EndVersion */
2784 PDM_DRVREG_VERSION
2785};
2786
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