VirtualBox

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

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

DrvVD: Bugfix

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