VirtualBox

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

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

Storage/NVMe: Convert emulation to use PDMIMEDIAEX instead of PDMIMEDIAASYNC

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