VirtualBox

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

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

DrvVD: fix warning

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