VirtualBox

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

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

Storage: Sketch out new interface for async I/O which will replace PDMIMEDIAASYNC

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