VirtualBox

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

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

Storage: Get rid of the block driver and merge the the little extra functionality it had into the VD driver. Enables us to get rid of PDMIBLOCK which is basically a subset of PDMIMEDIA and makes changes to the latter interface tedious because it had to be replicated in the former. (bugref:4114)

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