VirtualBox

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

Last change on this file since 63942 was 63919, checked in by vboxsync, 8 years ago

DrvVD: Fix handling encrypted disks when PDMIMEDIAEX is used (only used by the NVMe controller so far)

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