VirtualBox

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

Last change on this file since 55535 was 55434, checked in by vboxsync, 10 years ago

Oops, LWIP_IPV6, which is 0 or 1 should be tested with #if, not #ifdef.

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