VirtualBox

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

Last change on this file since 58466 was 58132, checked in by vboxsync, 9 years ago

*: Doxygen fixes.

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