VirtualBox

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

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

Devices: warnings

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