VirtualBox

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

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

Devices: scm

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