VirtualBox

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

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

Switch to the new block cache and disable the old one for now

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