VirtualBox

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

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

pdmifs.h: Move the storage related interfaces (PDMIMEDIA, PDMIMOUNT, PDMISCSICONNECTOR, etc.) into a separate header to reduce the overall size of the header a bit

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