VirtualBox

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

Last change on this file since 49890 was 48947, checked in by vboxsync, 11 years ago

Devices: Whitespace and svn:keyword cleanups by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 102.8 KB
Line 
1/* $Id: DrvVD.cpp 48947 2013-10-07 21:41:00Z vboxsync $ */
2/** @file
3 * DrvVD - Generic VBox disk media driver.
4 */
5
6/*
7 * Copyright (C) 2006-2013 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/pdmasynccompletion.h>
26#include <VBox/vmm/pdmblkcache.h>
27#include <iprt/asm.h>
28#include <iprt/alloc.h>
29#include <iprt/assert.h>
30#include <iprt/uuid.h>
31#include <iprt/file.h>
32#include <iprt/string.h>
33#include <iprt/tcp.h>
34#include <iprt/semaphore.h>
35#include <iprt/sg.h>
36#include <iprt/poll.h>
37#include <iprt/pipe.h>
38#include <iprt/system.h>
39
40#ifdef VBOX_WITH_INIP
41/* All lwip header files are not C++ safe. So hack around this. */
42RT_C_DECLS_BEGIN
43#include <lwip/opt.h>
44#include <lwip/inet.h>
45#include <lwip/tcp.h>
46#include <lwip/sockets.h>
47# ifdef VBOX_WITH_NEW_LWIP
48# include <lwip/inet6.h>
49# endif
50RT_C_DECLS_END
51#endif /* VBOX_WITH_INIP */
52
53#include "VBoxDD.h"
54
55#ifdef VBOX_WITH_INIP
56/* Small hack to get at lwIP initialized status */
57extern bool DevINIPConfigured(void);
58#endif /* VBOX_WITH_INIP */
59
60
61/*******************************************************************************
62* Defined types, constants and macros *
63*******************************************************************************/
64
65/** Converts a pointer to VBOXDISK::IMedia to a PVBOXDISK. */
66#define PDMIMEDIA_2_VBOXDISK(pInterface) \
67 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMedia)) )
68
69/** Converts a pointer to VBOXDISK::IMediaAsync to a PVBOXDISK. */
70#define PDMIMEDIAASYNC_2_VBOXDISK(pInterface) \
71 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMediaAsync)) )
72
73/**
74 * VBox disk container, image information, private part.
75 */
76
77typedef struct VBOXIMAGE
78{
79 /** Pointer to next image. */
80 struct VBOXIMAGE *pNext;
81 /** Pointer to list of VD interfaces. Per-image. */
82 PVDINTERFACE pVDIfsImage;
83 /** Configuration information interface. */
84 VDINTERFACECONFIG VDIfConfig;
85 /** TCP network stack interface. */
86 VDINTERFACETCPNET VDIfTcpNet;
87 /** I/O interface. */
88 VDINTERFACEIO VDIfIo;
89} VBOXIMAGE, *PVBOXIMAGE;
90
91/**
92 * Storage backend data.
93 */
94typedef struct DRVVDSTORAGEBACKEND
95{
96 /** PDM async completion end point. */
97 PPDMASYNCCOMPLETIONENDPOINT pEndpoint;
98 /** The template. */
99 PPDMASYNCCOMPLETIONTEMPLATE pTemplate;
100 /** Event semaphore for synchronous operations. */
101 RTSEMEVENT EventSem;
102 /** Flag whether a synchronous operation is currently pending. */
103 volatile bool fSyncIoPending;
104 /** Return code of the last completed request. */
105 int rcReqLast;
106 /** Callback routine */
107 PFNVDCOMPLETED pfnCompleted;
108} DRVVDSTORAGEBACKEND, *PDRVVDSTORAGEBACKEND;
109
110/**
111 * VBox disk container media main structure, private part.
112 *
113 * @implements PDMIMEDIA
114 * @implements PDMIMEDIAASYNC
115 * @implements VDINTERFACEERROR
116 * @implements VDINTERFACETCPNET
117 * @implements VDINTERFACEASYNCIO
118 * @implements VDINTERFACECONFIG
119 */
120typedef struct VBOXDISK
121{
122 /** The VBox disk container. */
123 PVBOXHDD pDisk;
124 /** The media interface. */
125 PDMIMEDIA IMedia;
126 /** Media port. */
127 PPDMIMEDIAPORT pDrvMediaPort;
128 /** Pointer to the driver instance. */
129 PPDMDRVINS pDrvIns;
130 /** Flag whether suspend has changed image open mode to read only. */
131 bool fTempReadOnly;
132 /** Flag whether to use the runtime (true) or startup error facility. */
133 bool fErrorUseRuntime;
134 /** Pointer to list of VD interfaces. Per-disk. */
135 PVDINTERFACE pVDIfsDisk;
136 /** Error interface. */
137 VDINTERFACEERROR VDIfError;
138 /** Thread synchronization interface. */
139 VDINTERFACETHREADSYNC VDIfThreadSync;
140
141 /** Flag whether opened disk supports async I/O operations. */
142 bool fAsyncIOSupported;
143 /** The async media interface. */
144 PDMIMEDIAASYNC IMediaAsync;
145 /** The async media port interface above. */
146 PPDMIMEDIAASYNCPORT pDrvMediaAsyncPort;
147 /** Pointer to the list of data we need to keep per image. */
148 PVBOXIMAGE pImages;
149 /** Flag whether the media should allow concurrent open for writing. */
150 bool fShareable;
151 /** Flag whether a merge operation has been set up. */
152 bool fMergePending;
153 /** Synchronization to prevent destruction before merge finishes. */
154 RTSEMFASTMUTEX MergeCompleteMutex;
155 /** Synchronization between merge and other image accesses. */
156 RTSEMRW MergeLock;
157 /** Source image index for merging. */
158 unsigned uMergeSource;
159 /** Target image index for merging. */
160 unsigned uMergeTarget;
161
162 /** Flag whether boot acceleration is enabled. */
163 bool fBootAccelEnabled;
164 /** Flag whether boot acceleration is currently active. */
165 bool fBootAccelActive;
166 /** Size of the disk, used for read truncation. */
167 size_t cbDisk;
168 /** Size of the configured buffer. */
169 size_t cbBootAccelBuffer;
170 /** Start offset for which the buffer holds data. */
171 uint64_t offDisk;
172 /** Number of valid bytes in the buffer. */
173 size_t cbDataValid;
174 /** The disk buffer. */
175 uint8_t *pbData;
176 /** Bandwidth group the disk is assigned to. */
177 char *pszBwGroup;
178 /** Flag whether async I/O using the host cache is enabled. */
179 bool fAsyncIoWithHostCache;
180
181 /** I/O interface for a cache image. */
182 VDINTERFACEIO VDIfIoCache;
183 /** Interface list for the cache image. */
184 PVDINTERFACE pVDIfsCache;
185
186 /** The block cache handle if configured. */
187 PPDMBLKCACHE pBlkCache;
188} VBOXDISK, *PVBOXDISK;
189
190
191/*******************************************************************************
192* Internal Functions *
193*******************************************************************************/
194
195/**
196 * Internal: allocate new image descriptor and put it in the list
197 */
198static PVBOXIMAGE drvvdNewImage(PVBOXDISK pThis)
199{
200 AssertPtr(pThis);
201 PVBOXIMAGE pImage = (PVBOXIMAGE)RTMemAllocZ(sizeof(VBOXIMAGE));
202 if (pImage)
203 {
204 pImage->pVDIfsImage = NULL;
205 PVBOXIMAGE *pp = &pThis->pImages;
206 while (*pp != NULL)
207 pp = &(*pp)->pNext;
208 *pp = pImage;
209 pImage->pNext = NULL;
210 }
211
212 return pImage;
213}
214
215/**
216 * Internal: free the list of images descriptors.
217 */
218static void drvvdFreeImages(PVBOXDISK pThis)
219{
220 while (pThis->pImages != NULL)
221 {
222 PVBOXIMAGE p = pThis->pImages;
223 pThis->pImages = pThis->pImages->pNext;
224 RTMemFree(p);
225 }
226}
227
228
229/**
230 * Make the image temporarily read-only.
231 *
232 * @returns VBox status code.
233 * @param pThis The driver instance data.
234 */
235static int drvvdSetReadonly(PVBOXDISK pThis)
236{
237 int rc = VINF_SUCCESS;
238 if (!VDIsReadOnly(pThis->pDisk))
239 {
240 unsigned uOpenFlags;
241 rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
242 AssertRC(rc);
243 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
244 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
245 AssertRC(rc);
246 pThis->fTempReadOnly = true;
247 }
248 return rc;
249}
250
251
252/**
253 * Undo the temporary read-only status of the image.
254 *
255 * @returns VBox status code.
256 * @param pThis The driver instance data.
257 */
258static int drvvdSetWritable(PVBOXDISK pThis)
259{
260 int rc = VINF_SUCCESS;
261 if (pThis->fTempReadOnly)
262 {
263 unsigned uOpenFlags;
264 rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
265 AssertRC(rc);
266 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
267 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
268 if (RT_SUCCESS(rc))
269 pThis->fTempReadOnly = false;
270 else
271 AssertRC(rc);
272 }
273 return rc;
274}
275
276
277/*******************************************************************************
278* Error reporting callback *
279*******************************************************************************/
280
281static void drvvdErrorCallback(void *pvUser, int rc, RT_SRC_POS_DECL,
282 const char *pszFormat, va_list va)
283{
284 PPDMDRVINS pDrvIns = (PPDMDRVINS)pvUser;
285 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
286 if (pThis->fErrorUseRuntime)
287 /* We must not pass VMSETRTERR_FLAGS_FATAL as it could lead to a
288 * deadlock: We are probably executed in a thread context != EMT
289 * and the EM thread would wait until every thread is suspended
290 * but we would wait for the EM thread ... */
291
292 PDMDrvHlpVMSetRuntimeErrorV(pDrvIns, /* fFlags=*/ 0, "DrvVD", pszFormat, va);
293 else
294 PDMDrvHlpVMSetErrorV(pDrvIns, rc, RT_SRC_POS_ARGS, pszFormat, va);
295}
296
297/*******************************************************************************
298* VD Async I/O interface implementation *
299*******************************************************************************/
300
301#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
302
303static DECLCALLBACK(void) drvvdAsyncTaskCompleted(PPDMDRVINS pDrvIns, void *pvTemplateUser, void *pvUser, int rcReq)
304{
305 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
306 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pvTemplateUser;
307
308 LogFlowFunc(("pDrvIns=%#p pvTemplateUser=%#p pvUser=%#p rcReq=%d\n",
309 pDrvIns, pvTemplateUser, pvUser, rcReq));
310
311 if (pStorageBackend->fSyncIoPending)
312 {
313 Assert(!pvUser);
314 pStorageBackend->rcReqLast = rcReq;
315 ASMAtomicWriteBool(&pStorageBackend->fSyncIoPending, false);
316 RTSemEventSignal(pStorageBackend->EventSem);
317 }
318 else
319 {
320 int rc;
321
322 AssertPtr(pvUser);
323
324 AssertPtr(pStorageBackend->pfnCompleted);
325 rc = pStorageBackend->pfnCompleted(pvUser, rcReq);
326 AssertRC(rc);
327 }
328}
329
330static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation,
331 uint32_t fOpen,
332 PFNVDCOMPLETED pfnCompleted,
333 void **ppStorage)
334{
335 PVBOXDISK pThis = (PVBOXDISK)pvUser;
336 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)RTMemAllocZ(sizeof(DRVVDSTORAGEBACKEND));
337 int rc = VINF_SUCCESS;
338
339 if (pStorageBackend)
340 {
341 pStorageBackend->fSyncIoPending = false;
342 pStorageBackend->rcReqLast = VINF_SUCCESS;
343 pStorageBackend->pfnCompleted = pfnCompleted;
344
345 rc = RTSemEventCreate(&pStorageBackend->EventSem);
346 if (RT_SUCCESS(rc))
347 {
348 rc = PDMDrvHlpAsyncCompletionTemplateCreate(pThis->pDrvIns, &pStorageBackend->pTemplate,
349 drvvdAsyncTaskCompleted, pStorageBackend, "AsyncTaskCompleted");
350 if (RT_SUCCESS(rc))
351 {
352 uint32_t fFlags = (fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ
353 ? PDMACEP_FILE_FLAGS_READ_ONLY
354 : 0;
355 if (pThis->fShareable)
356 {
357 Assert((fOpen & RTFILE_O_DENY_MASK) == RTFILE_O_DENY_NONE);
358
359 fFlags |= PDMACEP_FILE_FLAGS_DONT_LOCK;
360 }
361 if (pThis->fAsyncIoWithHostCache)
362 fFlags |= PDMACEP_FILE_FLAGS_HOST_CACHE_ENABLED;
363
364 rc = PDMR3AsyncCompletionEpCreateForFile(&pStorageBackend->pEndpoint,
365 pszLocation, fFlags,
366 pStorageBackend->pTemplate);
367
368 if (RT_SUCCESS(rc))
369 {
370 if (pThis->pszBwGroup)
371 rc = PDMR3AsyncCompletionEpSetBwMgr(pStorageBackend->pEndpoint, pThis->pszBwGroup);
372
373 if (RT_SUCCESS(rc))
374 {
375 LogFlow(("drvvdAsyncIOOpen: Successfully opened '%s'; fOpen=%#x pStorage=%p\n",
376 pszLocation, fOpen, pStorageBackend));
377 *ppStorage = pStorageBackend;
378 return VINF_SUCCESS;
379 }
380
381 PDMR3AsyncCompletionEpClose(pStorageBackend->pEndpoint);
382 }
383
384 PDMR3AsyncCompletionTemplateDestroy(pStorageBackend->pTemplate);
385 }
386 RTSemEventDestroy(pStorageBackend->EventSem);
387 }
388 RTMemFree(pStorageBackend);
389 }
390 else
391 rc = VERR_NO_MEMORY;
392
393 return rc;
394}
395
396static DECLCALLBACK(int) drvvdAsyncIOClose(void *pvUser, void *pStorage)
397{
398 PVBOXDISK pThis = (PVBOXDISK)pvUser;
399 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
400
401 PDMR3AsyncCompletionEpClose(pStorageBackend->pEndpoint);
402 PDMR3AsyncCompletionTemplateDestroy(pStorageBackend->pTemplate);
403 RTSemEventDestroy(pStorageBackend->EventSem);
404 RTMemFree(pStorageBackend);
405
406 return VINF_SUCCESS;;
407}
408
409static DECLCALLBACK(int) drvvdAsyncIOReadSync(void *pvUser, void *pStorage, uint64_t uOffset,
410 void *pvBuf, size_t cbRead, size_t *pcbRead)
411{
412 PVBOXDISK pThis = (PVBOXDISK)pvUser;
413 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
414 RTSGSEG DataSeg;
415 PPDMASYNCCOMPLETIONTASK pTask;
416
417 bool fOld = ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
418 Assert(!fOld);
419 DataSeg.cbSeg = cbRead;
420 DataSeg.pvSeg = pvBuf;
421
422 int rc = PDMR3AsyncCompletionEpRead(pStorageBackend->pEndpoint, uOffset, &DataSeg, 1, cbRead, NULL, &pTask);
423 if (RT_FAILURE(rc))
424 return rc;
425
426 if (rc == VINF_AIO_TASK_PENDING)
427 {
428 /* Wait */
429 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
430 AssertRC(rc);
431 }
432 else
433 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
434
435 if (pcbRead)
436 *pcbRead = cbRead;
437
438 return pStorageBackend->rcReqLast;
439}
440
441static DECLCALLBACK(int) drvvdAsyncIOWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
442 const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
443{
444 PVBOXDISK pThis = (PVBOXDISK)pvUser;
445 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
446 RTSGSEG DataSeg;
447 PPDMASYNCCOMPLETIONTASK pTask;
448
449 bool fOld = ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
450 Assert(!fOld);
451 DataSeg.cbSeg = cbWrite;
452 DataSeg.pvSeg = (void *)pvBuf;
453
454 int rc = PDMR3AsyncCompletionEpWrite(pStorageBackend->pEndpoint, uOffset, &DataSeg, 1, cbWrite, NULL, &pTask);
455 if (RT_FAILURE(rc))
456 return rc;
457
458 if (rc == VINF_AIO_TASK_PENDING)
459 {
460 /* Wait */
461 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
462 AssertRC(rc);
463 }
464 else
465 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
466
467 if (pcbWritten)
468 *pcbWritten = cbWrite;
469
470 return pStorageBackend->rcReqLast;
471}
472
473static DECLCALLBACK(int) drvvdAsyncIOFlushSync(void *pvUser, void *pStorage)
474{
475 PVBOXDISK pThis = (PVBOXDISK)pvUser;
476 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
477 PPDMASYNCCOMPLETIONTASK pTask;
478
479 LogFlowFunc(("pvUser=%#p pStorage=%#p\n", pvUser, pStorage));
480
481 bool fOld = ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
482 Assert(!fOld);
483
484 int rc = PDMR3AsyncCompletionEpFlush(pStorageBackend->pEndpoint, NULL, &pTask);
485 if (RT_FAILURE(rc))
486 return rc;
487
488 if (rc == VINF_AIO_TASK_PENDING)
489 {
490 /* Wait */
491 LogFlowFunc(("Waiting for flush to complete\n"));
492 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
493 AssertRC(rc);
494 }
495 else
496 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
497
498 return pStorageBackend->rcReqLast;
499}
500
501static DECLCALLBACK(int) drvvdAsyncIOReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
502 PCRTSGSEG paSegments, size_t cSegments,
503 size_t cbRead, void *pvCompletion,
504 void **ppTask)
505{
506 PVBOXDISK pThis = (PVBOXDISK)pvUser;
507 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
508
509 int rc = PDMR3AsyncCompletionEpRead(pStorageBackend->pEndpoint, uOffset, paSegments, (unsigned)cSegments, cbRead,
510 pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask);
511 if (rc == VINF_AIO_TASK_PENDING)
512 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
513
514 return rc;
515}
516
517static DECLCALLBACK(int) drvvdAsyncIOWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
518 PCRTSGSEG paSegments, size_t cSegments,
519 size_t cbWrite, void *pvCompletion,
520 void **ppTask)
521{
522 PVBOXDISK pThis = (PVBOXDISK)pvUser;
523 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
524
525 int rc = PDMR3AsyncCompletionEpWrite(pStorageBackend->pEndpoint, uOffset, paSegments, (unsigned)cSegments, cbWrite,
526 pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask);
527 if (rc == VINF_AIO_TASK_PENDING)
528 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
529
530 return rc;
531}
532
533static DECLCALLBACK(int) drvvdAsyncIOFlushAsync(void *pvUser, void *pStorage,
534 void *pvCompletion, void **ppTask)
535{
536 PVBOXDISK pThis = (PVBOXDISK)pvUser;
537 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
538
539 int rc = PDMR3AsyncCompletionEpFlush(pStorageBackend->pEndpoint, pvCompletion,
540 (PPPDMASYNCCOMPLETIONTASK)ppTask);
541 if (rc == VINF_AIO_TASK_PENDING)
542 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
543
544 return rc;
545}
546
547static DECLCALLBACK(int) drvvdAsyncIOGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
548{
549 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
550 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
551
552 return PDMR3AsyncCompletionEpGetSize(pStorageBackend->pEndpoint, pcbSize);
553}
554
555static DECLCALLBACK(int) drvvdAsyncIOSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
556{
557 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
558 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
559
560 return PDMR3AsyncCompletionEpSetSize(pStorageBackend->pEndpoint, cbSize);
561}
562
563#endif /* VBOX_WITH_PDM_ASYNC_COMPLETION */
564
565
566/*******************************************************************************
567* VD Thread Synchronization interface implementation *
568*******************************************************************************/
569
570static DECLCALLBACK(int) drvvdThreadStartRead(void *pvUser)
571{
572 PVBOXDISK pThis = (PVBOXDISK)pvUser;
573
574 return RTSemRWRequestRead(pThis->MergeLock, RT_INDEFINITE_WAIT);
575}
576
577static DECLCALLBACK(int) drvvdThreadFinishRead(void *pvUser)
578{
579 PVBOXDISK pThis = (PVBOXDISK)pvUser;
580
581 return RTSemRWReleaseRead(pThis->MergeLock);
582}
583
584static DECLCALLBACK(int) drvvdThreadStartWrite(void *pvUser)
585{
586 PVBOXDISK pThis = (PVBOXDISK)pvUser;
587
588 return RTSemRWRequestWrite(pThis->MergeLock, RT_INDEFINITE_WAIT);
589}
590
591static DECLCALLBACK(int) drvvdThreadFinishWrite(void *pvUser)
592{
593 PVBOXDISK pThis = (PVBOXDISK)pvUser;
594
595 return RTSemRWReleaseWrite(pThis->MergeLock);
596}
597
598
599/*******************************************************************************
600* VD Configuration interface implementation *
601*******************************************************************************/
602
603static bool drvvdCfgAreKeysValid(void *pvUser, const char *pszzValid)
604{
605 return CFGMR3AreValuesValid((PCFGMNODE)pvUser, pszzValid);
606}
607
608static int drvvdCfgQuerySize(void *pvUser, const char *pszName, size_t *pcb)
609{
610 return CFGMR3QuerySize((PCFGMNODE)pvUser, pszName, pcb);
611}
612
613static int drvvdCfgQuery(void *pvUser, const char *pszName, char *pszString, size_t cchString)
614{
615 return CFGMR3QueryString((PCFGMNODE)pvUser, pszName, pszString, cchString);
616}
617
618
619#ifdef VBOX_WITH_INIP
620/*******************************************************************************
621* VD TCP network stack interface implementation - INIP case *
622*******************************************************************************/
623
624/**
625 * vvl: this structure duplicate meaning of sockaddr,
626 * perhaps it'd be better to get rid of it.
627 */
628typedef union INIPSOCKADDRUNION
629{
630 struct sockaddr Addr;
631 struct sockaddr_in Ipv4;
632#ifdef VBOX_WITH_NEW_LWIP
633 struct sockaddr_in6 Ipv6;
634#endif
635} INIPSOCKADDRUNION;
636
637typedef struct INIPSOCKET
638{
639 int hSock;
640} INIPSOCKET, *PINIPSOCKET;
641
642static DECLCALLBACK(int) drvvdINIPFlush(VDSOCKET Sock);
643
644/** @copydoc VDINTERFACETCPNET::pfnSocketCreate */
645static DECLCALLBACK(int) drvvdINIPSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
646{
647 PINIPSOCKET pSocketInt = NULL;
648
649 /*
650 * The extended select method is not supported because it is impossible to wakeup
651 * the thread.
652 */
653 if (fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT)
654 return VERR_NOT_SUPPORTED;
655
656 pSocketInt = (PINIPSOCKET)RTMemAllocZ(sizeof(INIPSOCKET));
657 if (pSocketInt)
658 {
659 pSocketInt->hSock = INT32_MAX;
660 *pSock = (VDSOCKET)pSocketInt;
661 return VINF_SUCCESS;
662 }
663
664 return VERR_NO_MEMORY;
665}
666
667/** @copydoc VDINTERFACETCPNET::pfnSocketCreate */
668static DECLCALLBACK(int) drvvdINIPSocketDestroy(VDSOCKET Sock)
669{
670 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
671
672 RTMemFree(pSocketInt);
673 return VINF_SUCCESS;
674}
675
676/** @copydoc VDINTERFACETCPNET::pfnClientConnect */
677static DECLCALLBACK(int) drvvdINIPClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort)
678{
679 int rc = VINF_SUCCESS;
680 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
681 int iInetFamily = PF_INET;
682 struct in_addr ip;
683#ifdef VBOX_WITH_NEW_LWIP
684 ip6_addr_t ip6;
685#endif
686
687 /* Check whether lwIP is set up in this VM instance. */
688 if (!DevINIPConfigured())
689 {
690 LogRelFunc(("no IP stack\n"));
691 return VERR_NET_HOST_UNREACHABLE;
692 }
693 /* Resolve hostname. As there is no standard resolver for lwIP yet,
694 * just accept numeric IP addresses for now. */
695#ifdef VBOX_WITH_NEW_LWIP
696 if (inet6_aton(pszAddress, &ip6))
697 iInetFamily = PF_INET6;
698 else /* concatination with if */
699#endif
700 if (!lwip_inet_aton(pszAddress, &ip))
701 {
702 LogRelFunc(("cannot resolve IP %s\n", pszAddress));
703 return VERR_NET_HOST_UNREACHABLE;
704 }
705 /* Create socket and connect. */
706 int iSock = lwip_socket(iInetFamily, SOCK_STREAM, 0);
707 if (iSock != -1)
708 {
709 struct sockaddr *pSockAddr = NULL;
710 if (iInetFamily == PF_INET)
711 {
712 struct sockaddr_in InAddr = {0};
713 InAddr.sin_family = AF_INET;
714 InAddr.sin_port = htons(uPort);
715 InAddr.sin_addr = ip;
716 InAddr.sin_len = sizeof(InAddr);
717 pSockAddr = (struct sockaddr *)&InAddr;
718 }
719#ifdef VBOX_WITH_NEW_LWIP
720 else
721 {
722 struct sockaddr_in6 In6Addr = {0};
723 In6Addr.sin6_family = AF_INET6;
724 In6Addr.sin6_port = htons(uPort);
725 memcpy(&In6Addr.sin6_addr, &ip6, sizeof(ip6));
726 In6Addr.sin6_len = sizeof(In6Addr);
727 pSockAddr = (struct sockaddr *)&In6Addr;
728 }
729#endif
730 if ( pSockAddr
731 && !lwip_connect(iSock, pSockAddr, pSockAddr->sa_len))
732 {
733 pSocketInt->hSock = iSock;
734 return VINF_SUCCESS;
735 }
736 rc = VERR_NET_CONNECTION_REFUSED; /* @todo real solution needed */
737 lwip_close(iSock);
738 }
739 else
740 rc = VERR_NET_CONNECTION_REFUSED; /* @todo real solution needed */
741 return rc;
742}
743
744/** @copydoc VDINTERFACETCPNET::pfnClientClose */
745static DECLCALLBACK(int) drvvdINIPClientClose(VDSOCKET Sock)
746{
747 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
748
749 lwip_close(pSocketInt->hSock);
750 pSocketInt->hSock = INT32_MAX;
751 return VINF_SUCCESS; /** @todo real solution needed */
752}
753
754/** @copydoc VDINTERFACETCPNET::pfnIsClientConnected */
755static DECLCALLBACK(bool) drvvdINIPIsClientConnected(VDSOCKET Sock)
756{
757 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
758
759 return pSocketInt->hSock != INT32_MAX;
760}
761
762/** @copydoc VDINTERFACETCPNET::pfnSelectOne */
763static DECLCALLBACK(int) drvvdINIPSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
764{
765 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
766 fd_set fdsetR;
767 FD_ZERO(&fdsetR);
768 FD_SET((uintptr_t)pSocketInt->hSock, &fdsetR);
769 fd_set fdsetE = fdsetR;
770
771 int rc;
772 if (cMillies == RT_INDEFINITE_WAIT)
773 rc = lwip_select(pSocketInt->hSock + 1, &fdsetR, NULL, &fdsetE, NULL);
774 else
775 {
776 struct timeval timeout;
777 timeout.tv_sec = cMillies / 1000;
778 timeout.tv_usec = (cMillies % 1000) * 1000;
779 rc = lwip_select(pSocketInt->hSock + 1, &fdsetR, NULL, &fdsetE, &timeout);
780 }
781 if (rc > 0)
782 return VINF_SUCCESS;
783 if (rc == 0)
784 return VERR_TIMEOUT;
785 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
786}
787
788/** @copydoc VDINTERFACETCPNET::pfnRead */
789static DECLCALLBACK(int) drvvdINIPRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
790{
791 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
792
793 /* Do params checking */
794 if (!pvBuffer || !cbBuffer)
795 {
796 AssertMsgFailed(("Invalid params\n"));
797 return VERR_INVALID_PARAMETER;
798 }
799
800 /*
801 * Read loop.
802 * If pcbRead is NULL we have to fill the entire buffer!
803 */
804 size_t cbRead = 0;
805 size_t cbToRead = cbBuffer;
806 for (;;)
807 {
808 /** @todo this clipping here is just in case (the send function
809 * needed it, so I added it here, too). Didn't investigate if this
810 * really has issues. Better be safe than sorry. */
811 ssize_t cbBytesRead = lwip_recv(pSocketInt->hSock, (char *)pvBuffer + cbRead,
812 RT_MIN(cbToRead, 32768), 0);
813 if (cbBytesRead < 0)
814 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution */
815 if (cbBytesRead == 0 && errno) /** @todo r=bird: lwip_recv will not touch errno on Windows. This may apply to other hosts as well */
816 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution */
817 if (pcbRead)
818 {
819 /* return partial data */
820 *pcbRead = cbBytesRead;
821 break;
822 }
823
824 /* read more? */
825 cbRead += cbBytesRead;
826 if (cbRead == cbBuffer)
827 break;
828
829 /* next */
830 cbToRead = cbBuffer - cbRead;
831 }
832
833 return VINF_SUCCESS;
834}
835
836/** @copydoc VDINTERFACETCPNET::pfnWrite */
837static DECLCALLBACK(int) drvvdINIPWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
838{
839 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
840
841 do
842 {
843 /** @todo lwip send only supports up to 65535 bytes in a single
844 * send (stupid limitation buried in the code), so make sure we
845 * don't get any wraparounds. This should be moved to DevINIP
846 * stack interface once that's implemented. */
847 ssize_t cbWritten = lwip_send(pSocketInt->hSock, (void *)pvBuffer,
848 RT_MIN(cbBuffer, 32768), 0);
849 if (cbWritten < 0)
850 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
851 AssertMsg(cbBuffer >= (size_t)cbWritten, ("Wrote more than we requested!!! cbWritten=%d cbBuffer=%d\n",
852 cbWritten, cbBuffer));
853 cbBuffer -= cbWritten;
854 pvBuffer = (const char *)pvBuffer + cbWritten;
855 } while (cbBuffer);
856
857 return VINF_SUCCESS;
858}
859
860/** @copydoc VDINTERFACETCPNET::pfnSgWrite */
861static DECLCALLBACK(int) drvvdINIPSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
862{
863 int rc = VINF_SUCCESS;
864
865 /* This is an extremely crude emulation, however it's good enough
866 * for our iSCSI code. INIP has no sendmsg(). */
867 for (unsigned i = 0; i < pSgBuf->cSegs; i++)
868 {
869 rc = drvvdINIPWrite(Sock, pSgBuf->paSegs[i].pvSeg,
870 pSgBuf->paSegs[i].cbSeg);
871 if (RT_FAILURE(rc))
872 break;
873 }
874 if (RT_SUCCESS(rc))
875 drvvdINIPFlush(Sock);
876
877 return rc;
878}
879
880/** @copydoc VDINTERFACETCPNET::pfnFlush */
881static DECLCALLBACK(int) drvvdINIPFlush(VDSOCKET Sock)
882{
883 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
884
885 int fFlag = 1;
886 lwip_setsockopt(pSocketInt->hSock, IPPROTO_TCP, TCP_NODELAY,
887 (const char *)&fFlag, sizeof(fFlag));
888 fFlag = 0;
889 lwip_setsockopt(pSocketInt->hSock, IPPROTO_TCP, TCP_NODELAY,
890 (const char *)&fFlag, sizeof(fFlag));
891 return VINF_SUCCESS;
892}
893
894/** @copydoc VDINTERFACETCPNET::pfnSetSendCoalescing */
895static DECLCALLBACK(int) drvvdINIPSetSendCoalescing(VDSOCKET Sock, bool fEnable)
896{
897 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
898
899 int fFlag = fEnable ? 0 : 1;
900 lwip_setsockopt(pSocketInt->hSock, IPPROTO_TCP, TCP_NODELAY,
901 (const char *)&fFlag, sizeof(fFlag));
902 return VINF_SUCCESS;
903}
904
905/** @copydoc VDINTERFACETCPNET::pfnGetLocalAddress */
906static DECLCALLBACK(int) drvvdINIPGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
907{
908 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
909 INIPSOCKADDRUNION u;
910 socklen_t cbAddr = sizeof(u);
911 RT_ZERO(u);
912 if (!lwip_getsockname(pSocketInt->hSock, &u.Addr, &cbAddr))
913 {
914 /*
915 * Convert the address.
916 */
917 if ( cbAddr == sizeof(struct sockaddr_in)
918 && u.Addr.sa_family == AF_INET)
919 {
920 RT_ZERO(*pAddr);
921 pAddr->enmType = RTNETADDRTYPE_IPV4;
922 pAddr->uPort = RT_N2H_U16(u.Ipv4.sin_port);
923 pAddr->uAddr.IPv4.u = u.Ipv4.sin_addr.s_addr;
924 }
925#ifdef VBOX_WITH_NEW_LWIP
926 else if ( cbAddr == sizeof(struct sockaddr_in6)
927 && u.Addr.sa_family == AF_INET6)
928 {
929 RT_ZERO(*pAddr);
930 pAddr->enmType = RTNETADDRTYPE_IPV6;
931 pAddr->uPort = RT_N2H_U16(u.Ipv6.sin6_port);
932 memcpy(&pAddr->uAddr.IPv6, &u.Ipv6.sin6_addr, sizeof(RTNETADDRIPV6));
933 }
934#endif
935 else
936 return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
937 return VINF_SUCCESS;
938 }
939 return VERR_NET_OPERATION_NOT_SUPPORTED;
940}
941
942/** @copydoc VDINTERFACETCPNET::pfnGetPeerAddress */
943static DECLCALLBACK(int) drvvdINIPGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
944{
945 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
946 INIPSOCKADDRUNION u;
947 socklen_t cbAddr = sizeof(u);
948 RT_ZERO(u);
949 if (!lwip_getpeername(pSocketInt->hSock, &u.Addr, &cbAddr))
950 {
951 /*
952 * Convert the address.
953 */
954 if ( cbAddr == sizeof(struct sockaddr_in)
955 && u.Addr.sa_family == AF_INET)
956 {
957 RT_ZERO(*pAddr);
958 pAddr->enmType = RTNETADDRTYPE_IPV4;
959 pAddr->uPort = RT_N2H_U16(u.Ipv4.sin_port);
960 pAddr->uAddr.IPv4.u = u.Ipv4.sin_addr.s_addr;
961 }
962#ifdef VBOX_WITH_NEW_LWIP
963 else if ( cbAddr == sizeof(struct sockaddr_in6)
964 && u.Addr.sa_family == AF_INET6)
965 {
966 RT_ZERO(*pAddr);
967 pAddr->enmType = RTNETADDRTYPE_IPV6;
968 pAddr->uPort = RT_N2H_U16(u.Ipv6.sin6_port);
969 memcpy(&pAddr->uAddr.IPv6, &u.Ipv6.sin6_addr, sizeof(RTNETADDRIPV6));
970 }
971#endif
972 else
973 return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
974 return VINF_SUCCESS;
975 }
976 return VERR_NET_OPERATION_NOT_SUPPORTED;
977}
978
979/** @copydoc VDINTERFACETCPNET::pfnSelectOneEx */
980static DECLCALLBACK(int) drvvdINIPSelectOneEx(VDSOCKET Sock, uint32_t fEvents, uint32_t *pfEvents, RTMSINTERVAL cMillies)
981{
982 AssertMsgFailed(("Not supported!\n"));
983 return VERR_NOT_SUPPORTED;
984}
985
986/** @copydoc VDINTERFACETCPNET::pfnPoke */
987static DECLCALLBACK(int) drvvdINIPPoke(VDSOCKET Sock)
988{
989 AssertMsgFailed(("Not supported!\n"));
990 return VERR_NOT_SUPPORTED;
991}
992
993#endif /* VBOX_WITH_INIP */
994
995
996/*******************************************************************************
997* VD TCP network stack interface implementation - Host TCP case *
998*******************************************************************************/
999
1000/**
1001 * Socket data.
1002 */
1003typedef struct VDSOCKETINT
1004{
1005 /** IPRT socket handle. */
1006 RTSOCKET hSocket;
1007 /** Pollset with the wakeup pipe and socket. */
1008 RTPOLLSET hPollSet;
1009 /** Pipe endpoint - read (in the pollset). */
1010 RTPIPE hPipeR;
1011 /** Pipe endpoint - write. */
1012 RTPIPE hPipeW;
1013 /** Flag whether the thread was woken up. */
1014 volatile bool fWokenUp;
1015 /** Flag whether the thread is waiting in the select call. */
1016 volatile bool fWaiting;
1017 /** Old event mask. */
1018 uint32_t fEventsOld;
1019} VDSOCKETINT, *PVDSOCKETINT;
1020
1021/** Pollset id of the socket. */
1022#define VDSOCKET_POLL_ID_SOCKET 0
1023/** Pollset id of the pipe. */
1024#define VDSOCKET_POLL_ID_PIPE 1
1025
1026/** @copydoc VDINTERFACETCPNET::pfnSocketCreate */
1027static DECLCALLBACK(int) drvvdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
1028{
1029 int rc = VINF_SUCCESS;
1030 int rc2 = VINF_SUCCESS;
1031 PVDSOCKETINT pSockInt = NULL;
1032
1033 pSockInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
1034 if (!pSockInt)
1035 return VERR_NO_MEMORY;
1036
1037 pSockInt->hSocket = NIL_RTSOCKET;
1038 pSockInt->hPollSet = NIL_RTPOLLSET;
1039 pSockInt->hPipeR = NIL_RTPIPE;
1040 pSockInt->hPipeW = NIL_RTPIPE;
1041 pSockInt->fWokenUp = false;
1042 pSockInt->fWaiting = false;
1043
1044 if (fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT)
1045 {
1046 /* Init pipe and pollset. */
1047 rc = RTPipeCreate(&pSockInt->hPipeR, &pSockInt->hPipeW, 0);
1048 if (RT_SUCCESS(rc))
1049 {
1050 rc = RTPollSetCreate(&pSockInt->hPollSet);
1051 if (RT_SUCCESS(rc))
1052 {
1053 rc = RTPollSetAddPipe(pSockInt->hPollSet, pSockInt->hPipeR,
1054 RTPOLL_EVT_READ, VDSOCKET_POLL_ID_PIPE);
1055 if (RT_SUCCESS(rc))
1056 {
1057 *pSock = pSockInt;
1058 return VINF_SUCCESS;
1059 }
1060
1061 RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_PIPE);
1062 rc2 = RTPollSetDestroy(pSockInt->hPollSet);
1063 AssertRC(rc2);
1064 }
1065
1066 rc2 = RTPipeClose(pSockInt->hPipeR);
1067 AssertRC(rc2);
1068 rc2 = RTPipeClose(pSockInt->hPipeW);
1069 AssertRC(rc2);
1070 }
1071 }
1072 else
1073 {
1074 *pSock = pSockInt;
1075 return VINF_SUCCESS;
1076 }
1077
1078 RTMemFree(pSockInt);
1079
1080 return rc;
1081}
1082
1083/** @copydoc VDINTERFACETCPNET::pfnSocketDestroy */
1084static DECLCALLBACK(int) drvvdTcpSocketDestroy(VDSOCKET Sock)
1085{
1086 int rc = VINF_SUCCESS;
1087 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1088
1089 /* Destroy the pipe and pollset if necessary. */
1090 if (pSockInt->hPollSet != NIL_RTPOLLSET)
1091 {
1092 if (pSockInt->hSocket != NIL_RTSOCKET)
1093 {
1094 rc = RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_SOCKET);
1095 Assert(RT_SUCCESS(rc) || rc == VERR_POLL_HANDLE_ID_NOT_FOUND);
1096 }
1097 rc = RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_PIPE);
1098 AssertRC(rc);
1099 rc = RTPollSetDestroy(pSockInt->hPollSet);
1100 AssertRC(rc);
1101 rc = RTPipeClose(pSockInt->hPipeR);
1102 AssertRC(rc);
1103 rc = RTPipeClose(pSockInt->hPipeW);
1104 AssertRC(rc);
1105 }
1106
1107 if (pSockInt->hSocket != NIL_RTSOCKET)
1108 rc = RTTcpClientCloseEx(pSockInt->hSocket, false /*fGracefulShutdown*/);
1109
1110 RTMemFree(pSockInt);
1111
1112 return rc;
1113}
1114
1115/** @copydoc VDINTERFACETCPNET::pfnClientConnect */
1116static DECLCALLBACK(int) drvvdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort)
1117{
1118 int rc = VINF_SUCCESS;
1119 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1120
1121 rc = RTTcpClientConnect(pszAddress, uPort, &pSockInt->hSocket);
1122 if (RT_SUCCESS(rc))
1123 {
1124 /* Add to the pollset if required. */
1125 if (pSockInt->hPollSet != NIL_RTPOLLSET)
1126 {
1127 pSockInt->fEventsOld = RTPOLL_EVT_READ | RTPOLL_EVT_WRITE | RTPOLL_EVT_ERROR;
1128
1129 rc = RTPollSetAddSocket(pSockInt->hPollSet, pSockInt->hSocket,
1130 pSockInt->fEventsOld, VDSOCKET_POLL_ID_SOCKET);
1131 }
1132
1133 if (RT_SUCCESS(rc))
1134 return VINF_SUCCESS;
1135
1136 rc = RTTcpClientCloseEx(pSockInt->hSocket, false /*fGracefulShutdown*/);
1137 }
1138
1139 return rc;
1140}
1141
1142/** @copydoc VDINTERFACETCPNET::pfnClientClose */
1143static DECLCALLBACK(int) drvvdTcpClientClose(VDSOCKET Sock)
1144{
1145 int rc = VINF_SUCCESS;
1146 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1147
1148 if (pSockInt->hPollSet != NIL_RTPOLLSET)
1149 {
1150 rc = RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_SOCKET);
1151 AssertRC(rc);
1152 }
1153
1154 rc = RTTcpClientCloseEx(pSockInt->hSocket, false /*fGracefulShutdown*/);
1155 pSockInt->hSocket = NIL_RTSOCKET;
1156
1157 return rc;
1158}
1159
1160/** @copydoc VDINTERFACETCPNET::pfnIsClientConnected */
1161static DECLCALLBACK(bool) drvvdTcpIsClientConnected(VDSOCKET Sock)
1162{
1163 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1164
1165 return pSockInt->hSocket != NIL_RTSOCKET;
1166}
1167
1168/** @copydoc VDINTERFACETCPNET::pfnSelectOne */
1169static DECLCALLBACK(int) drvvdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
1170{
1171 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1172
1173 return RTTcpSelectOne(pSockInt->hSocket, cMillies);
1174}
1175
1176/** @copydoc VDINTERFACETCPNET::pfnRead */
1177static DECLCALLBACK(int) drvvdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
1178{
1179 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1180
1181 return RTTcpRead(pSockInt->hSocket, pvBuffer, cbBuffer, pcbRead);
1182}
1183
1184/** @copydoc VDINTERFACETCPNET::pfnWrite */
1185static DECLCALLBACK(int) drvvdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
1186{
1187 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1188
1189 return RTTcpWrite(pSockInt->hSocket, pvBuffer, cbBuffer);
1190}
1191
1192/** @copydoc VDINTERFACETCPNET::pfnSgWrite */
1193static DECLCALLBACK(int) drvvdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
1194{
1195 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1196
1197 return RTTcpSgWrite(pSockInt->hSocket, pSgBuf);
1198}
1199
1200/** @copydoc VDINTERFACETCPNET::pfnReadNB */
1201static DECLCALLBACK(int) drvvdTcpReadNB(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
1202{
1203 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1204
1205 return RTTcpReadNB(pSockInt->hSocket, pvBuffer, cbBuffer, pcbRead);
1206}
1207
1208/** @copydoc VDINTERFACETCPNET::pfnWriteNB */
1209static DECLCALLBACK(int) drvvdTcpWriteNB(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten)
1210{
1211 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1212
1213 return RTTcpWriteNB(pSockInt->hSocket, pvBuffer, cbBuffer, pcbWritten);
1214}
1215
1216/** @copydoc VDINTERFACETCPNET::pfnSgWriteNB */
1217static DECLCALLBACK(int) drvvdTcpSgWriteNB(VDSOCKET Sock, PRTSGBUF pSgBuf, size_t *pcbWritten)
1218{
1219 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1220
1221 return RTTcpSgWriteNB(pSockInt->hSocket, pSgBuf, pcbWritten);
1222}
1223
1224/** @copydoc VDINTERFACETCPNET::pfnFlush */
1225static DECLCALLBACK(int) drvvdTcpFlush(VDSOCKET Sock)
1226{
1227 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1228
1229 return RTTcpFlush(pSockInt->hSocket);
1230}
1231
1232/** @copydoc VDINTERFACETCPNET::pfnSetSendCoalescing */
1233static DECLCALLBACK(int) drvvdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
1234{
1235 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1236
1237 return RTTcpSetSendCoalescing(pSockInt->hSocket, fEnable);
1238}
1239
1240/** @copydoc VDINTERFACETCPNET::pfnGetLocalAddress */
1241static DECLCALLBACK(int) drvvdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
1242{
1243 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1244
1245 return RTTcpGetLocalAddress(pSockInt->hSocket, pAddr);
1246}
1247
1248/** @copydoc VDINTERFACETCPNET::pfnGetPeerAddress */
1249static DECLCALLBACK(int) drvvdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
1250{
1251 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1252
1253 return RTTcpGetPeerAddress(pSockInt->hSocket, pAddr);
1254}
1255
1256static int drvvdTcpSelectOneExPoll(VDSOCKET Sock, uint32_t fEvents,
1257 uint32_t *pfEvents, RTMSINTERVAL cMillies)
1258{
1259 int rc = VINF_SUCCESS;
1260 uint32_t id = 0;
1261 uint32_t fEventsRecv = 0;
1262 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1263
1264 *pfEvents = 0;
1265
1266 if ( pSockInt->fEventsOld != fEvents
1267 && pSockInt->hSocket != NIL_RTSOCKET)
1268 {
1269 uint32_t fPollEvents = 0;
1270
1271 if (fEvents & VD_INTERFACETCPNET_EVT_READ)
1272 fPollEvents |= RTPOLL_EVT_READ;
1273 if (fEvents & VD_INTERFACETCPNET_EVT_WRITE)
1274 fPollEvents |= RTPOLL_EVT_WRITE;
1275 if (fEvents & VD_INTERFACETCPNET_EVT_ERROR)
1276 fPollEvents |= RTPOLL_EVT_ERROR;
1277
1278 rc = RTPollSetEventsChange(pSockInt->hPollSet, VDSOCKET_POLL_ID_SOCKET, fPollEvents);
1279 if (RT_FAILURE(rc))
1280 return rc;
1281
1282 pSockInt->fEventsOld = fEvents;
1283 }
1284
1285 ASMAtomicXchgBool(&pSockInt->fWaiting, true);
1286 if (ASMAtomicXchgBool(&pSockInt->fWokenUp, false))
1287 {
1288 ASMAtomicXchgBool(&pSockInt->fWaiting, false);
1289 return VERR_INTERRUPTED;
1290 }
1291
1292 rc = RTPoll(pSockInt->hPollSet, cMillies, &fEventsRecv, &id);
1293 Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT);
1294
1295 ASMAtomicXchgBool(&pSockInt->fWaiting, false);
1296
1297 if (RT_SUCCESS(rc))
1298 {
1299 if (id == VDSOCKET_POLL_ID_SOCKET)
1300 {
1301 fEventsRecv &= RTPOLL_EVT_VALID_MASK;
1302
1303 if (fEventsRecv & RTPOLL_EVT_READ)
1304 *pfEvents |= VD_INTERFACETCPNET_EVT_READ;
1305 if (fEventsRecv & RTPOLL_EVT_WRITE)
1306 *pfEvents |= VD_INTERFACETCPNET_EVT_WRITE;
1307 if (fEventsRecv & RTPOLL_EVT_ERROR)
1308 *pfEvents |= VD_INTERFACETCPNET_EVT_ERROR;
1309 }
1310 else
1311 {
1312 size_t cbRead = 0;
1313 uint8_t abBuf[10];
1314 Assert(id == VDSOCKET_POLL_ID_PIPE);
1315 Assert((fEventsRecv & RTPOLL_EVT_VALID_MASK) == RTPOLL_EVT_READ);
1316
1317 /* We got interrupted, drain the pipe. */
1318 rc = RTPipeRead(pSockInt->hPipeR, abBuf, sizeof(abBuf), &cbRead);
1319 AssertRC(rc);
1320
1321 ASMAtomicXchgBool(&pSockInt->fWokenUp, false);
1322
1323 rc = VERR_INTERRUPTED;
1324 }
1325 }
1326
1327 return rc;
1328}
1329
1330/** @copydoc VDINTERFACETCPNET::pfnSelectOneEx */
1331static DECLCALLBACK(int) drvvdTcpSelectOneExNoPoll(VDSOCKET Sock, uint32_t fEvents,
1332 uint32_t *pfEvents, RTMSINTERVAL cMillies)
1333{
1334 int rc = VINF_SUCCESS;
1335 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1336
1337 *pfEvents = 0;
1338
1339 ASMAtomicXchgBool(&pSockInt->fWaiting, true);
1340 if (ASMAtomicXchgBool(&pSockInt->fWokenUp, false))
1341 {
1342 ASMAtomicXchgBool(&pSockInt->fWaiting, false);
1343 return VERR_INTERRUPTED;
1344 }
1345
1346 if ( pSockInt->hSocket == NIL_RTSOCKET
1347 || !fEvents)
1348 {
1349 /*
1350 * Only the pipe is configured or the caller doesn't wait for a socket event,
1351 * wait until there is something to read from the pipe.
1352 */
1353 size_t cbRead = 0;
1354 char ch = 0;
1355 rc = RTPipeReadBlocking(pSockInt->hPipeR, &ch, 1, &cbRead);
1356 if (RT_SUCCESS(rc))
1357 {
1358 Assert(cbRead == 1);
1359 rc = VERR_INTERRUPTED;
1360 ASMAtomicXchgBool(&pSockInt->fWokenUp, false);
1361 }
1362 }
1363 else
1364 {
1365 uint32_t fSelectEvents = 0;
1366
1367 if (fEvents & VD_INTERFACETCPNET_EVT_READ)
1368 fSelectEvents |= RTSOCKET_EVT_READ;
1369 if (fEvents & VD_INTERFACETCPNET_EVT_WRITE)
1370 fSelectEvents |= RTSOCKET_EVT_WRITE;
1371 if (fEvents & VD_INTERFACETCPNET_EVT_ERROR)
1372 fSelectEvents |= RTSOCKET_EVT_ERROR;
1373
1374 if (fEvents & VD_INTERFACETCPNET_HINT_INTERRUPT)
1375 {
1376 uint32_t fEventsRecv = 0;
1377
1378 /* Make sure the socket is not in the pollset. */
1379 rc = RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_SOCKET);
1380 Assert(RT_SUCCESS(rc) || rc == VERR_POLL_HANDLE_ID_NOT_FOUND);
1381
1382 for (;;)
1383 {
1384 uint32_t id = 0;
1385 rc = RTPoll(pSockInt->hPollSet, 5, &fEvents, &id);
1386 if (rc == VERR_TIMEOUT)
1387 {
1388 /* Check the socket. */
1389 rc = RTTcpSelectOneEx(pSockInt->hSocket, fSelectEvents, &fEventsRecv, 0);
1390 if (RT_SUCCESS(rc))
1391 {
1392 if (fEventsRecv & RTSOCKET_EVT_READ)
1393 *pfEvents |= VD_INTERFACETCPNET_EVT_READ;
1394 if (fEventsRecv & RTSOCKET_EVT_WRITE)
1395 *pfEvents |= VD_INTERFACETCPNET_EVT_WRITE;
1396 if (fEventsRecv & RTSOCKET_EVT_ERROR)
1397 *pfEvents |= VD_INTERFACETCPNET_EVT_ERROR;
1398 break; /* Quit */
1399 }
1400 else if (rc != VERR_TIMEOUT)
1401 break;
1402 }
1403 else if (RT_SUCCESS(rc))
1404 {
1405 size_t cbRead = 0;
1406 uint8_t abBuf[10];
1407 Assert(id == VDSOCKET_POLL_ID_PIPE);
1408 Assert((fEventsRecv & RTPOLL_EVT_VALID_MASK) == RTPOLL_EVT_READ);
1409
1410 /* We got interrupted, drain the pipe. */
1411 rc = RTPipeRead(pSockInt->hPipeR, abBuf, sizeof(abBuf), &cbRead);
1412 AssertRC(rc);
1413
1414 ASMAtomicXchgBool(&pSockInt->fWokenUp, false);
1415
1416 rc = VERR_INTERRUPTED;
1417 break;
1418 }
1419 else
1420 break;
1421 }
1422 }
1423 else /* The caller waits for a socket event. */
1424 {
1425 uint32_t fEventsRecv = 0;
1426
1427 /* Loop until we got woken up or a socket event occurred. */
1428 for (;;)
1429 {
1430 /** @todo find an adaptive wait algorithm based on the
1431 * number of wakeups in the past. */
1432 rc = RTTcpSelectOneEx(pSockInt->hSocket, fSelectEvents, &fEventsRecv, 5);
1433 if (rc == VERR_TIMEOUT)
1434 {
1435 /* Check if there is an event pending. */
1436 size_t cbRead = 0;
1437 char ch = 0;
1438 rc = RTPipeRead(pSockInt->hPipeR, &ch, 1, &cbRead);
1439 if (RT_SUCCESS(rc) && rc != VINF_TRY_AGAIN)
1440 {
1441 Assert(cbRead == 1);
1442 rc = VERR_INTERRUPTED;
1443 ASMAtomicXchgBool(&pSockInt->fWokenUp, false);
1444 break; /* Quit */
1445 }
1446 else
1447 Assert(rc == VINF_TRY_AGAIN);
1448 }
1449 else if (RT_SUCCESS(rc))
1450 {
1451 if (fEventsRecv & RTSOCKET_EVT_READ)
1452 *pfEvents |= VD_INTERFACETCPNET_EVT_READ;
1453 if (fEventsRecv & RTSOCKET_EVT_WRITE)
1454 *pfEvents |= VD_INTERFACETCPNET_EVT_WRITE;
1455 if (fEventsRecv & RTSOCKET_EVT_ERROR)
1456 *pfEvents |= VD_INTERFACETCPNET_EVT_ERROR;
1457 break; /* Quit */
1458 }
1459 else
1460 break;
1461 }
1462 }
1463 }
1464
1465 ASMAtomicXchgBool(&pSockInt->fWaiting, false);
1466
1467 return rc;
1468}
1469
1470/** @copydoc VDINTERFACETCPNET::pfnPoke */
1471static DECLCALLBACK(int) drvvdTcpPoke(VDSOCKET Sock)
1472{
1473 int rc = VINF_SUCCESS;
1474 size_t cbWritten = 0;
1475 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1476
1477 ASMAtomicXchgBool(&pSockInt->fWokenUp, true);
1478
1479 if (ASMAtomicReadBool(&pSockInt->fWaiting))
1480 {
1481 rc = RTPipeWrite(pSockInt->hPipeW, "", 1, &cbWritten);
1482 Assert(RT_SUCCESS(rc) || cbWritten == 0);
1483 }
1484
1485 return VINF_SUCCESS;
1486}
1487
1488
1489/*******************************************************************************
1490* Media interface methods *
1491*******************************************************************************/
1492
1493/** @copydoc PDMIMEDIA::pfnRead */
1494static DECLCALLBACK(int) drvvdRead(PPDMIMEDIA pInterface,
1495 uint64_t off, void *pvBuf, size_t cbRead)
1496{
1497 int rc = VINF_SUCCESS;
1498
1499 LogFlowFunc(("off=%#llx pvBuf=%p cbRead=%d\n", off, pvBuf, cbRead));
1500 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1501
1502 if (!pThis->fBootAccelActive)
1503 rc = VDRead(pThis->pDisk, off, pvBuf, cbRead);
1504 else
1505 {
1506 /* Can we serve the request from the buffer? */
1507 if ( off >= pThis->offDisk
1508 && off - pThis->offDisk < pThis->cbDataValid)
1509 {
1510 size_t cbToCopy = RT_MIN(cbRead, pThis->offDisk + pThis->cbDataValid - off);
1511
1512 memcpy(pvBuf, pThis->pbData + (off - pThis->offDisk), cbToCopy);
1513 cbRead -= cbToCopy;
1514 off += cbToCopy;
1515 pvBuf = (char *)pvBuf + cbToCopy;
1516 }
1517
1518 if ( cbRead > 0
1519 && cbRead < pThis->cbBootAccelBuffer)
1520 {
1521 /* Increase request to the buffer size and read. */
1522 pThis->cbDataValid = RT_MIN(pThis->cbDisk - off, pThis->cbBootAccelBuffer);
1523 pThis->offDisk = off;
1524 rc = VDRead(pThis->pDisk, off, pThis->pbData, pThis->cbDataValid);
1525 if (RT_FAILURE(rc))
1526 pThis->cbDataValid = 0;
1527 else
1528 memcpy(pvBuf, pThis->pbData, cbRead);
1529 }
1530 else if (cbRead >= pThis->cbBootAccelBuffer)
1531 {
1532 pThis->fBootAccelActive = false; /* Deactiviate */
1533 }
1534 }
1535
1536 if (RT_SUCCESS(rc))
1537 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d\n%.*Rhxd\n", __FUNCTION__,
1538 off, pvBuf, cbRead, cbRead, pvBuf));
1539 LogFlowFunc(("returns %Rrc\n", rc));
1540 return rc;
1541}
1542
1543/** @copydoc PDMIMEDIA::pfnWrite */
1544static DECLCALLBACK(int) drvvdWrite(PPDMIMEDIA pInterface,
1545 uint64_t off, const void *pvBuf,
1546 size_t cbWrite)
1547{
1548 LogFlowFunc(("off=%#llx pvBuf=%p cbWrite=%d\n", off, pvBuf, cbWrite));
1549 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1550 Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d\n%.*Rhxd\n", __FUNCTION__,
1551 off, pvBuf, cbWrite, cbWrite, pvBuf));
1552
1553 /* Invalidate any buffer if boot acceleration is enabled. */
1554 if (pThis->fBootAccelActive)
1555 {
1556 pThis->cbDataValid = 0;
1557 pThis->offDisk = 0;
1558 }
1559
1560 int rc = VDWrite(pThis->pDisk, off, pvBuf, cbWrite);
1561 LogFlowFunc(("returns %Rrc\n", rc));
1562 return rc;
1563}
1564
1565/** @copydoc PDMIMEDIA::pfnFlush */
1566static DECLCALLBACK(int) drvvdFlush(PPDMIMEDIA pInterface)
1567{
1568 LogFlowFunc(("\n"));
1569 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1570 int rc = VDFlush(pThis->pDisk);
1571 LogFlowFunc(("returns %Rrc\n", rc));
1572 return rc;
1573}
1574
1575/** @copydoc PDMIMEDIA::pfnMerge */
1576static DECLCALLBACK(int) drvvdMerge(PPDMIMEDIA pInterface,
1577 PFNSIMPLEPROGRESS pfnProgress,
1578 void *pvUser)
1579{
1580 LogFlowFunc(("\n"));
1581 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1582 int rc = VINF_SUCCESS;
1583
1584 /* Note: There is an unavoidable race between destruction and another
1585 * thread invoking this function. This is handled safely and gracefully by
1586 * atomically invalidating the lock handle in drvvdDestruct. */
1587 int rc2 = RTSemFastMutexRequest(pThis->MergeCompleteMutex);
1588 AssertRC(rc2);
1589 if (RT_SUCCESS(rc2) && pThis->fMergePending)
1590 {
1591 /* Take shortcut: PFNSIMPLEPROGRESS is exactly the same type as
1592 * PFNVDPROGRESS, so there's no need for a conversion function. */
1593 /** @todo maybe introduce a conversion which limits update frequency. */
1594 PVDINTERFACE pVDIfsOperation = NULL;
1595 VDINTERFACEPROGRESS VDIfProgress;
1596 VDIfProgress.pfnProgress = pfnProgress;
1597 rc2 = VDInterfaceAdd(&VDIfProgress.Core, "DrvVD_VDIProgress", VDINTERFACETYPE_PROGRESS,
1598 pvUser, sizeof(VDINTERFACEPROGRESS), &pVDIfsOperation);
1599 AssertRC(rc2);
1600 pThis->fMergePending = false;
1601 rc = VDMerge(pThis->pDisk, pThis->uMergeSource,
1602 pThis->uMergeTarget, pVDIfsOperation);
1603 }
1604 rc2 = RTSemFastMutexRelease(pThis->MergeCompleteMutex);
1605 AssertRC(rc2);
1606 LogFlowFunc(("returns %Rrc\n", rc));
1607 return rc;
1608}
1609
1610/** @copydoc PDMIMEDIA::pfnGetSize */
1611static DECLCALLBACK(uint64_t) drvvdGetSize(PPDMIMEDIA pInterface)
1612{
1613 LogFlowFunc(("\n"));
1614 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1615 uint64_t cb = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
1616 LogFlowFunc(("returns %#llx (%llu)\n", cb, cb));
1617 return cb;
1618}
1619
1620/** @copydoc PDMIMEDIA::pfnGetSectorSize */
1621static DECLCALLBACK(uint32_t) drvvdGetSectorSize(PPDMIMEDIA pInterface)
1622{
1623 LogFlowFunc(("\n"));
1624 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1625 uint32_t cb = VDGetSectorSize(pThis->pDisk, VD_LAST_IMAGE);
1626 LogFlowFunc(("returns %u\n", cb));
1627 return cb;
1628}
1629
1630/** @copydoc PDMIMEDIA::pfnIsReadOnly */
1631static DECLCALLBACK(bool) drvvdIsReadOnly(PPDMIMEDIA pInterface)
1632{
1633 LogFlowFunc(("\n"));
1634 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1635 bool f = VDIsReadOnly(pThis->pDisk);
1636 LogFlowFunc(("returns %d\n", f));
1637 return f;
1638}
1639
1640/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
1641static DECLCALLBACK(int) drvvdBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
1642 PPDMMEDIAGEOMETRY pPCHSGeometry)
1643{
1644 LogFlowFunc(("\n"));
1645 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1646 VDGEOMETRY geo;
1647 int rc = VDGetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
1648 if (RT_SUCCESS(rc))
1649 {
1650 pPCHSGeometry->cCylinders = geo.cCylinders;
1651 pPCHSGeometry->cHeads = geo.cHeads;
1652 pPCHSGeometry->cSectors = geo.cSectors;
1653 }
1654 else
1655 {
1656 LogFunc(("geometry not available.\n"));
1657 rc = VERR_PDM_GEOMETRY_NOT_SET;
1658 }
1659 LogFlowFunc(("returns %Rrc (CHS=%d/%d/%d)\n",
1660 rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1661 return rc;
1662}
1663
1664/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
1665static DECLCALLBACK(int) drvvdBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
1666 PCPDMMEDIAGEOMETRY pPCHSGeometry)
1667{
1668 LogFlowFunc(("CHS=%d/%d/%d\n",
1669 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1670 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1671 VDGEOMETRY geo;
1672 geo.cCylinders = pPCHSGeometry->cCylinders;
1673 geo.cHeads = pPCHSGeometry->cHeads;
1674 geo.cSectors = pPCHSGeometry->cSectors;
1675 int rc = VDSetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
1676 if (rc == VERR_VD_GEOMETRY_NOT_SET)
1677 rc = VERR_PDM_GEOMETRY_NOT_SET;
1678 LogFlowFunc(("returns %Rrc\n", rc));
1679 return rc;
1680}
1681
1682/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
1683static DECLCALLBACK(int) drvvdBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
1684 PPDMMEDIAGEOMETRY pLCHSGeometry)
1685{
1686 LogFlowFunc(("\n"));
1687 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1688 VDGEOMETRY geo;
1689 int rc = VDGetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
1690 if (RT_SUCCESS(rc))
1691 {
1692 pLCHSGeometry->cCylinders = geo.cCylinders;
1693 pLCHSGeometry->cHeads = geo.cHeads;
1694 pLCHSGeometry->cSectors = geo.cSectors;
1695 }
1696 else
1697 {
1698 LogFunc(("geometry not available.\n"));
1699 rc = VERR_PDM_GEOMETRY_NOT_SET;
1700 }
1701 LogFlowFunc(("returns %Rrc (CHS=%d/%d/%d)\n",
1702 rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1703 return rc;
1704}
1705
1706/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
1707static DECLCALLBACK(int) drvvdBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
1708 PCPDMMEDIAGEOMETRY pLCHSGeometry)
1709{
1710 LogFlowFunc(("CHS=%d/%d/%d\n",
1711 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1712 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1713 VDGEOMETRY geo;
1714 geo.cCylinders = pLCHSGeometry->cCylinders;
1715 geo.cHeads = pLCHSGeometry->cHeads;
1716 geo.cSectors = pLCHSGeometry->cSectors;
1717 int rc = VDSetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
1718 if (rc == VERR_VD_GEOMETRY_NOT_SET)
1719 rc = VERR_PDM_GEOMETRY_NOT_SET;
1720 LogFlowFunc(("returns %Rrc\n", rc));
1721 return rc;
1722}
1723
1724/** @copydoc PDMIMEDIA::pfnGetUuid */
1725static DECLCALLBACK(int) drvvdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
1726{
1727 LogFlowFunc(("\n"));
1728 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1729 int rc = VDGetUuid(pThis->pDisk, 0, pUuid);
1730 LogFlowFunc(("returns %Rrc ({%RTuuid})\n", rc, pUuid));
1731 return rc;
1732}
1733
1734static DECLCALLBACK(int) drvvdDiscard(PPDMIMEDIA pInterface, PCRTRANGE paRanges, unsigned cRanges)
1735{
1736 LogFlowFunc(("\n"));
1737 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1738
1739 int rc = VDDiscardRanges(pThis->pDisk, paRanges, cRanges);
1740 LogFlowFunc(("returns %Rrc\n", rc));
1741 return rc;
1742}
1743
1744/*******************************************************************************
1745* Async Media interface methods *
1746*******************************************************************************/
1747
1748static void drvvdAsyncReqComplete(void *pvUser1, void *pvUser2, int rcReq)
1749{
1750 PVBOXDISK pThis = (PVBOXDISK)pvUser1;
1751
1752 if (!pThis->pBlkCache)
1753 {
1754 int rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort,
1755 pvUser2, rcReq);
1756 AssertRC(rc);
1757 }
1758 else
1759 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, (PPDMBLKCACHEIOXFER)pvUser2, rcReq);
1760}
1761
1762static DECLCALLBACK(int) drvvdStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
1763 PCRTSGSEG paSeg, unsigned cSeg,
1764 size_t cbRead, void *pvUser)
1765{
1766 LogFlowFunc(("uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d pvUser=%#p\n",
1767 uOffset, paSeg, cSeg, cbRead, pvUser));
1768 int rc = VINF_SUCCESS;
1769 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
1770
1771 pThis->fBootAccelActive = false;
1772
1773 RTSGBUF SgBuf;
1774 RTSgBufInit(&SgBuf, paSeg, cSeg);
1775 if (!pThis->pBlkCache)
1776 rc = VDAsyncRead(pThis->pDisk, uOffset, cbRead, &SgBuf,
1777 drvvdAsyncReqComplete, pThis, pvUser);
1778 else
1779 {
1780 rc = PDMR3BlkCacheRead(pThis->pBlkCache, uOffset, &SgBuf, cbRead, pvUser);
1781 if (rc == VINF_AIO_TASK_PENDING)
1782 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1783 else if (rc == VINF_SUCCESS)
1784 rc = VINF_VD_ASYNC_IO_FINISHED;
1785 }
1786
1787 LogFlowFunc(("returns %Rrc\n", rc));
1788 return rc;
1789}
1790
1791static DECLCALLBACK(int) drvvdStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
1792 PCRTSGSEG paSeg, unsigned cSeg,
1793 size_t cbWrite, void *pvUser)
1794{
1795 LogFlowFunc(("uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d pvUser=%#p\n",
1796 uOffset, paSeg, cSeg, cbWrite, pvUser));
1797 int rc = VINF_SUCCESS;
1798 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
1799
1800 pThis->fBootAccelActive = false;
1801
1802 RTSGBUF SgBuf;
1803 RTSgBufInit(&SgBuf, paSeg, cSeg);
1804
1805 if (!pThis->pBlkCache)
1806 rc = VDAsyncWrite(pThis->pDisk, uOffset, cbWrite, &SgBuf,
1807 drvvdAsyncReqComplete, pThis, pvUser);
1808 else
1809 {
1810 rc = PDMR3BlkCacheWrite(pThis->pBlkCache, uOffset, &SgBuf, cbWrite, pvUser);
1811 if (rc == VINF_AIO_TASK_PENDING)
1812 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1813 else if (rc == VINF_SUCCESS)
1814 rc = VINF_VD_ASYNC_IO_FINISHED;
1815 }
1816
1817 LogFlowFunc(("returns %Rrc\n", rc));
1818 return rc;
1819}
1820
1821static DECLCALLBACK(int) drvvdStartFlush(PPDMIMEDIAASYNC pInterface, void *pvUser)
1822{
1823 LogFlowFunc(("pvUser=%#p\n", pvUser));
1824 int rc = VINF_SUCCESS;
1825 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
1826
1827 if (!pThis->pBlkCache)
1828 rc = VDAsyncFlush(pThis->pDisk, drvvdAsyncReqComplete, pThis, pvUser);
1829 else
1830 {
1831 rc = PDMR3BlkCacheFlush(pThis->pBlkCache, pvUser);
1832 if (rc == VINF_AIO_TASK_PENDING)
1833 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1834 else if (rc == VINF_SUCCESS)
1835 rc = VINF_VD_ASYNC_IO_FINISHED;
1836 }
1837 LogFlowFunc(("returns %Rrc\n", rc));
1838 return rc;
1839}
1840
1841static DECLCALLBACK(int) drvvdStartDiscard(PPDMIMEDIAASYNC pInterface, PCRTRANGE paRanges,
1842 unsigned cRanges, void *pvUser)
1843{
1844 int rc = VINF_SUCCESS;
1845 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
1846
1847 LogFlowFunc(("paRanges=%#p cRanges=%u pvUser=%#p\n",
1848 paRanges, cRanges, pvUser));
1849
1850 if (!pThis->pBlkCache)
1851 rc = VDAsyncDiscardRanges(pThis->pDisk, paRanges, cRanges, drvvdAsyncReqComplete,
1852 pThis, pvUser);
1853 else
1854 {
1855 rc = PDMR3BlkCacheDiscard(pThis->pBlkCache, paRanges, cRanges, pvUser);
1856 if (rc == VINF_AIO_TASK_PENDING)
1857 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1858 else if (rc == VINF_SUCCESS)
1859 rc = VINF_VD_ASYNC_IO_FINISHED;
1860 }
1861 LogFlowFunc(("returns %Rrc\n", rc));
1862 return rc;
1863}
1864
1865/** @copydoc FNPDMBLKCACHEXFERCOMPLETEDRV */
1866static void drvvdBlkCacheXferComplete(PPDMDRVINS pDrvIns, void *pvUser, int rcReq)
1867{
1868 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1869
1870 int rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort,
1871 pvUser, rcReq);
1872 AssertRC(rc);
1873}
1874
1875/** @copydoc FNPDMBLKCACHEXFERENQUEUEDRV */
1876static int drvvdBlkCacheXferEnqueue(PPDMDRVINS pDrvIns,
1877 PDMBLKCACHEXFERDIR enmXferDir,
1878 uint64_t off, size_t cbXfer,
1879 PCRTSGBUF pcSgBuf, PPDMBLKCACHEIOXFER hIoXfer)
1880{
1881 int rc = VINF_SUCCESS;
1882 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1883
1884 switch (enmXferDir)
1885 {
1886 case PDMBLKCACHEXFERDIR_READ:
1887 rc = VDAsyncRead(pThis->pDisk, off, cbXfer, pcSgBuf, drvvdAsyncReqComplete,
1888 pThis, hIoXfer);
1889 break;
1890 case PDMBLKCACHEXFERDIR_WRITE:
1891 rc = VDAsyncWrite(pThis->pDisk, off, cbXfer, pcSgBuf, drvvdAsyncReqComplete,
1892 pThis, hIoXfer);
1893 break;
1894 case PDMBLKCACHEXFERDIR_FLUSH:
1895 rc = VDAsyncFlush(pThis->pDisk, drvvdAsyncReqComplete, pThis, hIoXfer);
1896 break;
1897 default:
1898 AssertMsgFailed(("Invalid transfer type %d\n", enmXferDir));
1899 rc = VERR_INVALID_PARAMETER;
1900 }
1901
1902 if (rc == VINF_VD_ASYNC_IO_FINISHED)
1903 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, hIoXfer, VINF_SUCCESS);
1904 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
1905 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, hIoXfer, rc);
1906
1907 return VINF_SUCCESS;
1908}
1909
1910/** @copydoc FNPDMBLKCACHEXFERENQUEUEDISCARDDRV */
1911static int drvvdBlkCacheXferEnqueueDiscard(PPDMDRVINS pDrvIns, PCRTRANGE paRanges,
1912 unsigned cRanges, PPDMBLKCACHEIOXFER hIoXfer)
1913{
1914 int rc = VINF_SUCCESS;
1915 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1916
1917 rc = VDAsyncDiscardRanges(pThis->pDisk, paRanges, cRanges,
1918 drvvdAsyncReqComplete, pThis, hIoXfer);
1919
1920 if (rc == VINF_VD_ASYNC_IO_FINISHED)
1921 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, hIoXfer, VINF_SUCCESS);
1922 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
1923 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, hIoXfer, rc);
1924
1925 return VINF_SUCCESS;
1926}
1927
1928/*******************************************************************************
1929* Base interface methods *
1930*******************************************************************************/
1931
1932/**
1933 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1934 */
1935static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1936{
1937 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1938 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1939
1940 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1941 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, &pThis->IMedia);
1942 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAASYNC, pThis->fAsyncIOSupported ? &pThis->IMediaAsync : NULL);
1943 return NULL;
1944}
1945
1946
1947/*******************************************************************************
1948* Saved state notification methods *
1949*******************************************************************************/
1950
1951/**
1952 * Load done callback for re-opening the image writable during teleportation.
1953 *
1954 * This is called both for successful and failed load runs, we only care about
1955 * successful ones.
1956 *
1957 * @returns VBox status code.
1958 * @param pDrvIns The driver instance.
1959 * @param pSSM The saved state handle.
1960 */
1961static DECLCALLBACK(int) drvvdLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1962{
1963 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1964 Assert(!pThis->fErrorUseRuntime);
1965
1966 /* Drop out if we don't have any work to do or if it's a failed load. */
1967 if ( !pThis->fTempReadOnly
1968 || RT_FAILURE(SSMR3HandleGetStatus(pSSM)))
1969 return VINF_SUCCESS;
1970
1971 int rc = drvvdSetWritable(pThis);
1972 if (RT_FAILURE(rc)) /** @todo does the bugger set any errors? */
1973 return SSMR3SetLoadError(pSSM, rc, RT_SRC_POS,
1974 N_("Failed to write lock the images"));
1975 return VINF_SUCCESS;
1976}
1977
1978
1979/*******************************************************************************
1980* Driver methods *
1981*******************************************************************************/
1982
1983/**
1984 * VM resume notification that we use to undo what the temporary read-only image
1985 * mode set by drvvdSuspend.
1986 *
1987 * Also switch to runtime error mode if we're resuming after a state load
1988 * without having been powered on first.
1989 *
1990 * @param pDrvIns The driver instance data.
1991 *
1992 * @todo The VMSetError vs VMSetRuntimeError mess must be fixed elsewhere,
1993 * we're making assumptions about Main behavior here!
1994 */
1995static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
1996{
1997 LogFlowFunc(("\n"));
1998 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1999
2000 drvvdSetWritable(pThis);
2001 pThis->fErrorUseRuntime = true;
2002
2003 if (pThis->pBlkCache)
2004 {
2005 int rc = PDMR3BlkCacheResume(pThis->pBlkCache);
2006 AssertRC(rc);
2007 }
2008}
2009
2010/**
2011 * The VM is being suspended, temporarily change to read-only image mode.
2012 *
2013 * This is important for several reasons:
2014 * -# It makes sure that there are no pending writes to the image. Most
2015 * backends implements this by closing and reopening the image in read-only
2016 * mode.
2017 * -# It allows Main to read the images during snapshotting without having
2018 * to account for concurrent writes.
2019 * -# This is essential for making teleportation targets sharing images work
2020 * right. Both with regards to caching and with regards to file sharing
2021 * locks (RTFILE_O_DENY_*). (See also drvvdLoadDone.)
2022 *
2023 * @param pDrvIns The driver instance data.
2024 */
2025static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
2026{
2027 LogFlowFunc(("\n"));
2028 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
2029
2030 if (pThis->pBlkCache)
2031 {
2032 int rc = PDMR3BlkCacheSuspend(pThis->pBlkCache);
2033 AssertRC(rc);
2034 }
2035
2036 drvvdSetReadonly(pThis);
2037}
2038
2039/**
2040 * VM PowerOn notification for undoing the TempReadOnly config option and
2041 * changing to runtime error mode.
2042 *
2043 * @param pDrvIns The driver instance data.
2044 *
2045 * @todo The VMSetError vs VMSetRuntimeError mess must be fixed elsewhere,
2046 * we're making assumptions about Main behavior here!
2047 */
2048static DECLCALLBACK(void) drvvdPowerOn(PPDMDRVINS pDrvIns)
2049{
2050 LogFlowFunc(("\n"));
2051 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
2052 drvvdSetWritable(pThis);
2053 pThis->fErrorUseRuntime = true;
2054}
2055
2056/**
2057 * @copydoc FNPDMDRVRESET
2058 */
2059static DECLCALLBACK(void) drvvdReset(PPDMDRVINS pDrvIns)
2060{
2061 LogFlowFunc(("\n"));
2062 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
2063
2064 if (pThis->pBlkCache)
2065 {
2066 int rc = PDMR3BlkCacheClear(pThis->pBlkCache);
2067 AssertRC(rc);
2068 }
2069
2070 if (pThis->fBootAccelEnabled)
2071 {
2072 pThis->fBootAccelActive = true;
2073 pThis->cbDataValid = 0;
2074 pThis->offDisk = 0;
2075 }
2076}
2077
2078/**
2079 * @copydoc FNPDMDRVDESTRUCT
2080 */
2081static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
2082{
2083 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2084 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
2085 LogFlowFunc(("\n"));
2086
2087 RTSEMFASTMUTEX mutex;
2088 ASMAtomicXchgHandle(&pThis->MergeCompleteMutex, NIL_RTSEMFASTMUTEX, &mutex);
2089 if (mutex != NIL_RTSEMFASTMUTEX)
2090 {
2091 /* Request the semaphore to wait until a potentially running merge
2092 * operation has been finished. */
2093 int rc = RTSemFastMutexRequest(mutex);
2094 AssertRC(rc);
2095 pThis->fMergePending = false;
2096 rc = RTSemFastMutexRelease(mutex);
2097 AssertRC(rc);
2098 rc = RTSemFastMutexDestroy(mutex);
2099 AssertRC(rc);
2100 }
2101
2102 if (RT_VALID_PTR(pThis->pBlkCache))
2103 {
2104 PDMR3BlkCacheRelease(pThis->pBlkCache);
2105 pThis->pBlkCache = NULL;
2106 }
2107
2108 if (RT_VALID_PTR(pThis->pDisk))
2109 {
2110 VDDestroy(pThis->pDisk);
2111 pThis->pDisk = NULL;
2112 }
2113 drvvdFreeImages(pThis);
2114
2115 if (pThis->MergeLock != NIL_RTSEMRW)
2116 {
2117 int rc = RTSemRWDestroy(pThis->MergeLock);
2118 AssertRC(rc);
2119 pThis->MergeLock = NIL_RTSEMRW;
2120 }
2121 if (pThis->pbData)
2122 {
2123 RTMemFree(pThis->pbData);
2124 pThis->pbData = NULL;
2125 }
2126 if (pThis->pszBwGroup)
2127 {
2128 MMR3HeapFree(pThis->pszBwGroup);
2129 pThis->pszBwGroup = NULL;
2130 }
2131}
2132
2133/**
2134 * Construct a VBox disk media driver instance.
2135 *
2136 * @copydoc FNPDMDRVCONSTRUCT
2137 */
2138static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
2139{
2140 LogFlowFunc(("\n"));
2141 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
2142 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
2143 int rc = VINF_SUCCESS;
2144 char *pszName = NULL; /**< The path of the disk image file. */
2145 char *pszFormat = NULL; /**< The format backed to use for this image. */
2146 char *pszCachePath = NULL; /**< The path to the cache image. */
2147 char *pszCacheFormat = NULL; /**< The format backend to use for the cache image. */
2148 bool fReadOnly; /**< True if the media is read-only. */
2149 bool fMaybeReadOnly; /**< True if the media may or may not be read-only. */
2150 bool fHonorZeroWrites; /**< True if zero blocks should be written. */
2151
2152 /*
2153 * Init the static parts.
2154 */
2155 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
2156 pThis->pDrvIns = pDrvIns;
2157 pThis->fTempReadOnly = false;
2158 pThis->pDisk = NULL;
2159 pThis->fAsyncIOSupported = false;
2160 pThis->fShareable = false;
2161 pThis->fMergePending = false;
2162 pThis->MergeCompleteMutex = NIL_RTSEMFASTMUTEX;
2163 pThis->MergeLock = NIL_RTSEMRW;
2164 pThis->uMergeSource = VD_LAST_IMAGE;
2165 pThis->uMergeTarget = VD_LAST_IMAGE;
2166
2167 /* IMedia */
2168 pThis->IMedia.pfnRead = drvvdRead;
2169 pThis->IMedia.pfnWrite = drvvdWrite;
2170 pThis->IMedia.pfnFlush = drvvdFlush;
2171 pThis->IMedia.pfnMerge = drvvdMerge;
2172 pThis->IMedia.pfnGetSize = drvvdGetSize;
2173 pThis->IMedia.pfnGetSectorSize = drvvdGetSectorSize;
2174 pThis->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
2175 pThis->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
2176 pThis->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
2177 pThis->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
2178 pThis->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
2179 pThis->IMedia.pfnGetUuid = drvvdGetUuid;
2180 pThis->IMedia.pfnDiscard = drvvdDiscard;
2181
2182 /* IMediaAsync */
2183 pThis->IMediaAsync.pfnStartRead = drvvdStartRead;
2184 pThis->IMediaAsync.pfnStartWrite = drvvdStartWrite;
2185 pThis->IMediaAsync.pfnStartFlush = drvvdStartFlush;
2186 pThis->IMediaAsync.pfnStartDiscard = drvvdStartDiscard;
2187
2188 /* Initialize supported VD interfaces. */
2189 pThis->pVDIfsDisk = NULL;
2190
2191 pThis->VDIfError.pfnError = drvvdErrorCallback;
2192 pThis->VDIfError.pfnMessage = NULL;
2193 rc = VDInterfaceAdd(&pThis->VDIfError.Core, "DrvVD_VDIError", VDINTERFACETYPE_ERROR,
2194 pDrvIns, sizeof(VDINTERFACEERROR), &pThis->pVDIfsDisk);
2195 AssertRC(rc);
2196
2197 /* List of images is empty now. */
2198 pThis->pImages = NULL;
2199
2200 pThis->pDrvMediaPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAPORT);
2201 if (!pThis->pDrvMediaPort)
2202 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
2203 N_("No media port interface above"));
2204
2205 /* Try to attach async media port interface above.*/
2206 pThis->pDrvMediaAsyncPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAASYNCPORT);
2207
2208 /*
2209 * Validate configuration and find all parent images.
2210 * It's sort of up side down from the image dependency tree.
2211 */
2212 bool fHostIP = false;
2213 bool fUseNewIo = false;
2214 bool fUseBlockCache = false;
2215 bool fDiscard = false;
2216 bool fInformAboutZeroBlocks = false;
2217 bool fSkipConsistencyChecks = false;
2218 unsigned iLevel = 0;
2219 PCFGMNODE pCurNode = pCfg;
2220 VDTYPE enmType = VDTYPE_HDD;
2221
2222 for (;;)
2223 {
2224 bool fValid;
2225
2226 if (pCurNode == pCfg)
2227 {
2228 /* Toplevel configuration additionally contains the global image
2229 * open flags. Some might be converted to per-image flags later. */
2230 fValid = CFGMR3AreValuesValid(pCurNode,
2231 "Format\0Path\0"
2232 "ReadOnly\0MaybeReadOnly\0TempReadOnly\0Shareable\0HonorZeroWrites\0"
2233 "HostIPStack\0UseNewIo\0BootAcceleration\0BootAccelerationBuffer\0"
2234 "SetupMerge\0MergeSource\0MergeTarget\0BwGroup\0Type\0BlockCache\0"
2235 "CachePath\0CacheFormat\0Discard\0InformAboutZeroBlocks\0"
2236 "SkipConsistencyChecks\0");
2237 }
2238 else
2239 {
2240 /* All other image configurations only contain image name and
2241 * the format information. */
2242 fValid = CFGMR3AreValuesValid(pCurNode, "Format\0Path\0"
2243 "MergeSource\0MergeTarget\0");
2244 }
2245 if (!fValid)
2246 {
2247 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
2248 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
2249 break;
2250 }
2251
2252 if (pCurNode == pCfg)
2253 {
2254 rc = CFGMR3QueryBoolDef(pCurNode, "HostIPStack", &fHostIP, true);
2255 if (RT_FAILURE(rc))
2256 {
2257 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2258 N_("DrvVD: Configuration error: Querying \"HostIPStack\" as boolean failed"));
2259 break;
2260 }
2261
2262 rc = CFGMR3QueryBoolDef(pCurNode, "HonorZeroWrites", &fHonorZeroWrites, false);
2263 if (RT_FAILURE(rc))
2264 {
2265 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2266 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
2267 break;
2268 }
2269
2270 rc = CFGMR3QueryBoolDef(pCurNode, "ReadOnly", &fReadOnly, false);
2271 if (RT_FAILURE(rc))
2272 {
2273 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2274 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
2275 break;
2276 }
2277
2278 rc = CFGMR3QueryBoolDef(pCurNode, "MaybeReadOnly", &fMaybeReadOnly, false);
2279 if (RT_FAILURE(rc))
2280 {
2281 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2282 N_("DrvVD: Configuration error: Querying \"MaybeReadOnly\" as boolean failed"));
2283 break;
2284 }
2285
2286 rc = CFGMR3QueryBoolDef(pCurNode, "TempReadOnly", &pThis->fTempReadOnly, false);
2287 if (RT_FAILURE(rc))
2288 {
2289 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2290 N_("DrvVD: Configuration error: Querying \"TempReadOnly\" as boolean failed"));
2291 break;
2292 }
2293 if (fReadOnly && pThis->fTempReadOnly)
2294 {
2295 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2296 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"TempReadOnly\" are set"));
2297 break;
2298 }
2299
2300 rc = CFGMR3QueryBoolDef(pCurNode, "Shareable", &pThis->fShareable, false);
2301 if (RT_FAILURE(rc))
2302 {
2303 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2304 N_("DrvVD: Configuration error: Querying \"Shareable\" as boolean failed"));
2305 break;
2306 }
2307
2308 rc = CFGMR3QueryBoolDef(pCurNode, "UseNewIo", &fUseNewIo, false);
2309 if (RT_FAILURE(rc))
2310 {
2311 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2312 N_("DrvVD: Configuration error: Querying \"UseNewIo\" as boolean failed"));
2313 break;
2314 }
2315 rc = CFGMR3QueryBoolDef(pCurNode, "SetupMerge", &pThis->fMergePending, false);
2316 if (RT_FAILURE(rc))
2317 {
2318 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2319 N_("DrvVD: Configuration error: Querying \"SetupMerge\" as boolean failed"));
2320 break;
2321 }
2322 if (fReadOnly && pThis->fMergePending)
2323 {
2324 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2325 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"MergePending\" are set"));
2326 break;
2327 }
2328 rc = CFGMR3QueryBoolDef(pCurNode, "BootAcceleration", &pThis->fBootAccelEnabled, false);
2329 if (RT_FAILURE(rc))
2330 {
2331 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2332 N_("DrvVD: Configuration error: Querying \"BootAcceleration\" as boolean failed"));
2333 break;
2334 }
2335 rc = CFGMR3QueryU32Def(pCurNode, "BootAccelerationBuffer", (uint32_t *)&pThis->cbBootAccelBuffer, 16 * _1K);
2336 if (RT_FAILURE(rc))
2337 {
2338 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2339 N_("DrvVD: Configuration error: Querying \"BootAccelerationBuffer\" as integer failed"));
2340 break;
2341 }
2342 rc = CFGMR3QueryBoolDef(pCurNode, "BlockCache", &fUseBlockCache, false);
2343 if (RT_FAILURE(rc))
2344 {
2345 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2346 N_("DrvVD: Configuration error: Querying \"BlockCache\" as boolean failed"));
2347 break;
2348 }
2349 rc = CFGMR3QueryStringAlloc(pCurNode, "BwGroup", &pThis->pszBwGroup);
2350 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
2351 {
2352 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2353 N_("DrvVD: Configuration error: Querying \"BwGroup\" as string failed"));
2354 break;
2355 }
2356 else
2357 rc = VINF_SUCCESS;
2358 rc = CFGMR3QueryBoolDef(pCurNode, "Discard", &fDiscard, false);
2359 if (RT_FAILURE(rc))
2360 {
2361 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2362 N_("DrvVD: Configuration error: Querying \"Discard\" as boolean failed"));
2363 break;
2364 }
2365 if (fReadOnly && fDiscard)
2366 {
2367 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2368 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"Discard\" are set"));
2369 break;
2370 }
2371 rc = CFGMR3QueryBoolDef(pCurNode, "InformAboutZeroBlocks", &fInformAboutZeroBlocks, false);
2372 if (RT_FAILURE(rc))
2373 {
2374 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2375 N_("DrvVD: Configuration error: Querying \"InformAboutZeroBlocks\" as boolean failed"));
2376 break;
2377 }
2378 rc = CFGMR3QueryBoolDef(pCurNode, "SkipConsistencyChecks", &fSkipConsistencyChecks, true);
2379 if (RT_FAILURE(rc))
2380 {
2381 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2382 N_("DrvVD: Configuration error: Querying \"SKipConsistencyChecks\" as boolean failed"));
2383 break;
2384 }
2385
2386 char *psz;
2387 rc = CFGMR3QueryStringAlloc(pCfg, "Type", &psz);
2388 if (RT_FAILURE(rc))
2389 {
2390 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_BLOCK_NO_TYPE, N_("Failed to obtain the type"));
2391 break;
2392 }
2393 else if (!strcmp(psz, "HardDisk"))
2394 enmType = VDTYPE_HDD;
2395 else if (!strcmp(psz, "DVD"))
2396 enmType = VDTYPE_DVD;
2397 else if (!strcmp(psz, "Floppy"))
2398 enmType = VDTYPE_FLOPPY;
2399 else
2400 {
2401 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_BLOCK_UNKNOWN_TYPE, RT_SRC_POS,
2402 N_("Unknown type \"%s\""), psz);
2403 MMR3HeapFree(psz);
2404 break;
2405 }
2406 MMR3HeapFree(psz); psz = NULL;
2407
2408 rc = CFGMR3QueryStringAlloc(pCurNode, "CachePath", &pszCachePath);
2409 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
2410 {
2411 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2412 N_("DrvVD: Configuration error: Querying \"CachePath\" as string failed"));
2413 break;
2414 }
2415 else
2416 rc = VINF_SUCCESS;
2417
2418 if (pszCachePath)
2419 {
2420 rc = CFGMR3QueryStringAlloc(pCurNode, "CacheFormat", &pszCacheFormat);
2421 if (RT_FAILURE(rc))
2422 {
2423 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2424 N_("DrvVD: Configuration error: Querying \"CacheFormat\" as string failed"));
2425 break;
2426 }
2427 }
2428 }
2429
2430 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
2431 if (!pParent)
2432 break;
2433 pCurNode = pParent;
2434 iLevel++;
2435 }
2436
2437 /*
2438 * Create the image container and the necessary interfaces.
2439 */
2440 if (RT_SUCCESS(rc))
2441 {
2442 /*
2443 * The image has a bandwidth group but the host cache is enabled.
2444 * Use the async I/O framework but tell it to enable the host cache.
2445 */
2446 if (!fUseNewIo && pThis->pszBwGroup)
2447 {
2448 pThis->fAsyncIoWithHostCache = true;
2449 fUseNewIo = true;
2450 }
2451
2452 /** @todo quick hack to work around problems in the async I/O
2453 * implementation (rw semaphore thread ownership problem)
2454 * while a merge is running. Remove once this is fixed. */
2455 if (pThis->fMergePending)
2456 fUseNewIo = false;
2457
2458 if (RT_SUCCESS(rc) && pThis->fMergePending)
2459 {
2460 rc = RTSemFastMutexCreate(&pThis->MergeCompleteMutex);
2461 if (RT_SUCCESS(rc))
2462 rc = RTSemRWCreate(&pThis->MergeLock);
2463 if (RT_SUCCESS(rc))
2464 {
2465 pThis->VDIfThreadSync.pfnStartRead = drvvdThreadStartRead;
2466 pThis->VDIfThreadSync.pfnFinishRead = drvvdThreadFinishRead;
2467 pThis->VDIfThreadSync.pfnStartWrite = drvvdThreadStartWrite;
2468 pThis->VDIfThreadSync.pfnFinishWrite = drvvdThreadFinishWrite;
2469
2470 rc = VDInterfaceAdd(&pThis->VDIfThreadSync.Core, "DrvVD_ThreadSync", VDINTERFACETYPE_THREADSYNC,
2471 pThis, sizeof(VDINTERFACETHREADSYNC), &pThis->pVDIfsDisk);
2472 }
2473 else
2474 {
2475 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2476 N_("DrvVD: Failed to create semaphores for \"MergePending\""));
2477 }
2478 }
2479
2480 if (RT_SUCCESS(rc))
2481 {
2482 rc = VDCreate(pThis->pVDIfsDisk, enmType, &pThis->pDisk);
2483 /* Error message is already set correctly. */
2484 }
2485 }
2486
2487 if (pThis->pDrvMediaAsyncPort && fUseNewIo)
2488 pThis->fAsyncIOSupported = true;
2489
2490 uint64_t tsStart = RTTimeNanoTS();
2491
2492 unsigned iImageIdx = 0;
2493 while (pCurNode && RT_SUCCESS(rc))
2494 {
2495 /* Allocate per-image data. */
2496 PVBOXIMAGE pImage = drvvdNewImage(pThis);
2497 if (!pImage)
2498 {
2499 rc = VERR_NO_MEMORY;
2500 break;
2501 }
2502
2503 /*
2504 * Read the image configuration.
2505 */
2506 rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
2507 if (RT_FAILURE(rc))
2508 {
2509 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2510 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
2511 break;
2512 }
2513
2514 rc = CFGMR3QueryStringAlloc(pCurNode, "Format", &pszFormat);
2515 if (RT_FAILURE(rc))
2516 {
2517 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2518 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
2519 break;
2520 }
2521
2522 bool fMergeSource;
2523 rc = CFGMR3QueryBoolDef(pCurNode, "MergeSource", &fMergeSource, false);
2524 if (RT_FAILURE(rc))
2525 {
2526 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2527 N_("DrvVD: Configuration error: Querying \"MergeSource\" as boolean failed"));
2528 break;
2529 }
2530 if (fMergeSource)
2531 {
2532 if (pThis->uMergeSource == VD_LAST_IMAGE)
2533 pThis->uMergeSource = iImageIdx;
2534 else
2535 {
2536 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2537 N_("DrvVD: Configuration error: Multiple \"MergeSource\" occurrences"));
2538 break;
2539 }
2540 }
2541
2542 bool fMergeTarget;
2543 rc = CFGMR3QueryBoolDef(pCurNode, "MergeTarget", &fMergeTarget, false);
2544 if (RT_FAILURE(rc))
2545 {
2546 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2547 N_("DrvVD: Configuration error: Querying \"MergeTarget\" as boolean failed"));
2548 break;
2549 }
2550 if (fMergeTarget)
2551 {
2552 if (pThis->uMergeTarget == VD_LAST_IMAGE)
2553 pThis->uMergeTarget = iImageIdx;
2554 else
2555 {
2556 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2557 N_("DrvVD: Configuration error: Multiple \"MergeTarget\" occurrences"));
2558 break;
2559 }
2560 }
2561
2562 PCFGMNODE pCfgVDConfig = CFGMR3GetChild(pCurNode, "VDConfig");
2563 pImage->VDIfConfig.pfnAreKeysValid = drvvdCfgAreKeysValid;
2564 pImage->VDIfConfig.pfnQuerySize = drvvdCfgQuerySize;
2565 pImage->VDIfConfig.pfnQuery = drvvdCfgQuery;
2566 rc = VDInterfaceAdd(&pImage->VDIfConfig.Core, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
2567 pCfgVDConfig, sizeof(VDINTERFACECONFIG), &pImage->pVDIfsImage);
2568 AssertRC(rc);
2569
2570 /* Unconditionally insert the TCPNET interface, don't bother to check
2571 * if an image really needs it. Will be ignored. Since the TCPNET
2572 * interface is per image we could make this more flexible in the
2573 * future if we want to. */
2574 /* Construct TCPNET callback table depending on the config. This is
2575 * done unconditionally, as uninterested backends will ignore it. */
2576 if (fHostIP)
2577 {
2578 pImage->VDIfTcpNet.pfnSocketCreate = drvvdTcpSocketCreate;
2579 pImage->VDIfTcpNet.pfnSocketDestroy = drvvdTcpSocketDestroy;
2580 pImage->VDIfTcpNet.pfnClientConnect = drvvdTcpClientConnect;
2581 pImage->VDIfTcpNet.pfnIsClientConnected = drvvdTcpIsClientConnected;
2582 pImage->VDIfTcpNet.pfnClientClose = drvvdTcpClientClose;
2583 pImage->VDIfTcpNet.pfnSelectOne = drvvdTcpSelectOne;
2584 pImage->VDIfTcpNet.pfnRead = drvvdTcpRead;
2585 pImage->VDIfTcpNet.pfnWrite = drvvdTcpWrite;
2586 pImage->VDIfTcpNet.pfnSgWrite = drvvdTcpSgWrite;
2587 pImage->VDIfTcpNet.pfnReadNB = drvvdTcpReadNB;
2588 pImage->VDIfTcpNet.pfnWriteNB = drvvdTcpWriteNB;
2589 pImage->VDIfTcpNet.pfnSgWriteNB = drvvdTcpSgWriteNB;
2590 pImage->VDIfTcpNet.pfnFlush = drvvdTcpFlush;
2591 pImage->VDIfTcpNet.pfnSetSendCoalescing = drvvdTcpSetSendCoalescing;
2592 pImage->VDIfTcpNet.pfnGetLocalAddress = drvvdTcpGetLocalAddress;
2593 pImage->VDIfTcpNet.pfnGetPeerAddress = drvvdTcpGetPeerAddress;
2594
2595 /*
2596 * There is a 15ms delay between receiving the data and marking the socket
2597 * as readable on Windows XP which hurts async I/O performance of
2598 * TCP backends badly. Provide a different select method without
2599 * using poll on XP.
2600 * This is only used on XP because it is not as efficient as the one using poll
2601 * and all other Windows versions are working fine.
2602 */
2603 char szOS[64];
2604 memset(szOS, 0, sizeof(szOS));
2605 rc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, &szOS[0], sizeof(szOS));
2606
2607 if (RT_SUCCESS(rc) && !strncmp(szOS, "Windows XP", 10))
2608 {
2609 LogRel(("VD: Detected Windows XP, disabled poll based waiting for TCP\n"));
2610 pImage->VDIfTcpNet.pfnSelectOneEx = drvvdTcpSelectOneExNoPoll;
2611 }
2612 else
2613 pImage->VDIfTcpNet.pfnSelectOneEx = drvvdTcpSelectOneExPoll;
2614
2615 pImage->VDIfTcpNet.pfnPoke = drvvdTcpPoke;
2616 }
2617 else
2618 {
2619#ifndef VBOX_WITH_INIP
2620 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
2621 RT_SRC_POS, N_("DrvVD: Configuration error: TCP over Internal Networking not compiled in"));
2622#else /* VBOX_WITH_INIP */
2623 pImage->VDIfTcpNet.pfnSocketCreate = drvvdINIPSocketCreate;
2624 pImage->VDIfTcpNet.pfnSocketDestroy = drvvdINIPSocketDestroy;
2625 pImage->VDIfTcpNet.pfnClientConnect = drvvdINIPClientConnect;
2626 pImage->VDIfTcpNet.pfnClientClose = drvvdINIPClientClose;
2627 pImage->VDIfTcpNet.pfnIsClientConnected = drvvdINIPIsClientConnected;
2628 pImage->VDIfTcpNet.pfnSelectOne = drvvdINIPSelectOne;
2629 pImage->VDIfTcpNet.pfnRead = drvvdINIPRead;
2630 pImage->VDIfTcpNet.pfnWrite = drvvdINIPWrite;
2631 pImage->VDIfTcpNet.pfnSgWrite = drvvdINIPSgWrite;
2632 pImage->VDIfTcpNet.pfnFlush = drvvdINIPFlush;
2633 pImage->VDIfTcpNet.pfnSetSendCoalescing = drvvdINIPSetSendCoalescing;
2634 pImage->VDIfTcpNet.pfnGetLocalAddress = drvvdINIPGetLocalAddress;
2635 pImage->VDIfTcpNet.pfnGetPeerAddress = drvvdINIPGetPeerAddress;
2636 pImage->VDIfTcpNet.pfnSelectOneEx = drvvdINIPSelectOneEx;
2637 pImage->VDIfTcpNet.pfnPoke = drvvdINIPPoke;
2638#endif /* VBOX_WITH_INIP */
2639 }
2640 rc = VDInterfaceAdd(&pImage->VDIfTcpNet.Core, "DrvVD_TCPNET",
2641 VDINTERFACETYPE_TCPNET, NULL,
2642 sizeof(VDINTERFACETCPNET), &pImage->pVDIfsImage);
2643 AssertRC(rc);
2644
2645 /* Insert the custom I/O interface only if we're told to use new IO.
2646 * Since the I/O interface is per image we could make this more
2647 * flexible in the future if we want to. */
2648 if (fUseNewIo)
2649 {
2650#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
2651 pImage->VDIfIo.pfnOpen = drvvdAsyncIOOpen;
2652 pImage->VDIfIo.pfnClose = drvvdAsyncIOClose;
2653 pImage->VDIfIo.pfnGetSize = drvvdAsyncIOGetSize;
2654 pImage->VDIfIo.pfnSetSize = drvvdAsyncIOSetSize;
2655 pImage->VDIfIo.pfnReadSync = drvvdAsyncIOReadSync;
2656 pImage->VDIfIo.pfnWriteSync = drvvdAsyncIOWriteSync;
2657 pImage->VDIfIo.pfnFlushSync = drvvdAsyncIOFlushSync;
2658 pImage->VDIfIo.pfnReadAsync = drvvdAsyncIOReadAsync;
2659 pImage->VDIfIo.pfnWriteAsync = drvvdAsyncIOWriteAsync;
2660 pImage->VDIfIo.pfnFlushAsync = drvvdAsyncIOFlushAsync;
2661#else /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
2662 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
2663 RT_SRC_POS, N_("DrvVD: Configuration error: Async Completion Framework not compiled in"));
2664#endif /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
2665 if (RT_SUCCESS(rc))
2666 rc = VDInterfaceAdd(&pImage->VDIfIo.Core, "DrvVD_IO", VDINTERFACETYPE_IO,
2667 pThis, sizeof(VDINTERFACEIO), &pImage->pVDIfsImage);
2668 AssertRC(rc);
2669 }
2670
2671 /*
2672 * Open the image.
2673 */
2674 unsigned uOpenFlags;
2675 if (fReadOnly || pThis->fTempReadOnly || iLevel != 0)
2676 uOpenFlags = VD_OPEN_FLAGS_READONLY;
2677 else
2678 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
2679 if (fHonorZeroWrites)
2680 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
2681 if (pThis->fAsyncIOSupported)
2682 uOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
2683 if (pThis->fShareable)
2684 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
2685 if (fDiscard && iLevel == 0)
2686 uOpenFlags |= VD_OPEN_FLAGS_DISCARD;
2687 if (fInformAboutZeroBlocks)
2688 uOpenFlags |= VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS;
2689 if ( (uOpenFlags & VD_OPEN_FLAGS_READONLY)
2690 && fSkipConsistencyChecks)
2691 uOpenFlags |= VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS;
2692
2693 /* Try to open backend in async I/O mode first. */
2694 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
2695 if (rc == VERR_NOT_SUPPORTED)
2696 {
2697 pThis->fAsyncIOSupported = false;
2698 uOpenFlags &= ~VD_OPEN_FLAGS_ASYNC_IO;
2699 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
2700 }
2701
2702 if (rc == VERR_VD_DISCARD_NOT_SUPPORTED)
2703 {
2704 fDiscard = false;
2705 uOpenFlags &= ~VD_OPEN_FLAGS_DISCARD;
2706 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
2707 }
2708
2709 if (!fDiscard)
2710 {
2711 pThis->IMedia.pfnDiscard = NULL;
2712 pThis->IMediaAsync.pfnStartDiscard = NULL;
2713 }
2714
2715 if (RT_SUCCESS(rc))
2716 {
2717 LogFunc(("%d - Opened '%s' in %s mode\n",
2718 iLevel, pszName,
2719 VDIsReadOnly(pThis->pDisk) ? "read-only" : "read-write"));
2720 if ( VDIsReadOnly(pThis->pDisk)
2721 && !fReadOnly
2722 && !fMaybeReadOnly
2723 && !pThis->fTempReadOnly
2724 && iLevel == 0)
2725 {
2726 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_VD_IMAGE_READ_ONLY, RT_SRC_POS,
2727 N_("Failed to open image '%s' for writing due to wrong permissions"),
2728 pszName);
2729 break;
2730 }
2731 }
2732 else
2733 {
2734 rc = PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
2735 N_("Failed to open image '%s' in %s mode rc=%Rrc"), pszName,
2736 (uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "read-only" : "read-write", rc);
2737 break;
2738 }
2739
2740
2741 MMR3HeapFree(pszName);
2742 pszName = NULL;
2743 MMR3HeapFree(pszFormat);
2744 pszFormat = NULL;
2745
2746 /* next */
2747 iLevel--;
2748 iImageIdx++;
2749 pCurNode = CFGMR3GetParent(pCurNode);
2750 }
2751
2752 LogRel(("VD: Opening the disk took %lld ns\n", RTTimeNanoTS() - tsStart));
2753
2754 /* Open the cache image if set. */
2755 if ( RT_SUCCESS(rc)
2756 && RT_VALID_PTR(pszCachePath))
2757 {
2758 /* Insert the custom I/O interface only if we're told to use new IO.
2759 * Since the I/O interface is per image we could make this more
2760 * flexible in the future if we want to. */
2761 if (fUseNewIo)
2762 {
2763#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
2764 pThis->VDIfIoCache.pfnOpen = drvvdAsyncIOOpen;
2765 pThis->VDIfIoCache.pfnClose = drvvdAsyncIOClose;
2766 pThis->VDIfIoCache.pfnGetSize = drvvdAsyncIOGetSize;
2767 pThis->VDIfIoCache.pfnSetSize = drvvdAsyncIOSetSize;
2768 pThis->VDIfIoCache.pfnReadSync = drvvdAsyncIOReadSync;
2769 pThis->VDIfIoCache.pfnWriteSync = drvvdAsyncIOWriteSync;
2770 pThis->VDIfIoCache.pfnFlushSync = drvvdAsyncIOFlushSync;
2771 pThis->VDIfIoCache.pfnReadAsync = drvvdAsyncIOReadAsync;
2772 pThis->VDIfIoCache.pfnWriteAsync = drvvdAsyncIOWriteAsync;
2773 pThis->VDIfIoCache.pfnFlushAsync = drvvdAsyncIOFlushAsync;
2774#else /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
2775 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
2776 RT_SRC_POS, N_("DrvVD: Configuration error: Async Completion Framework not compiled in"));
2777#endif /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
2778 if (RT_SUCCESS(rc))
2779 rc = VDInterfaceAdd(&pThis->VDIfIoCache.Core, "DrvVD_IO", VDINTERFACETYPE_IO,
2780 pThis, sizeof(VDINTERFACEIO), &pThis->pVDIfsCache);
2781 AssertRC(rc);
2782 }
2783
2784 rc = VDCacheOpen(pThis->pDisk, pszCacheFormat, pszCachePath, VD_OPEN_FLAGS_NORMAL, pThis->pVDIfsCache);
2785 if (RT_FAILURE(rc))
2786 rc = PDMDRV_SET_ERROR(pDrvIns, rc, N_("DrvVD: Could not open cache image"));
2787 }
2788
2789 if (RT_VALID_PTR(pszCachePath))
2790 MMR3HeapFree(pszCachePath);
2791 if (RT_VALID_PTR(pszCacheFormat))
2792 MMR3HeapFree(pszCacheFormat);
2793
2794 if ( RT_SUCCESS(rc)
2795 && pThis->fMergePending
2796 && ( pThis->uMergeSource == VD_LAST_IMAGE
2797 || pThis->uMergeTarget == VD_LAST_IMAGE))
2798 {
2799 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2800 N_("DrvVD: Configuration error: Inconsistent image merge data"));
2801 }
2802
2803 /* Create the block cache if enabled. */
2804 if ( fUseBlockCache
2805 && !pThis->fShareable
2806 && !fDiscard
2807 && RT_SUCCESS(rc))
2808 {
2809 /*
2810 * We need a unique ID for the block cache (to identify the owner of data
2811 * blocks in a saved state). UUIDs are not really suitable because
2812 * there are image formats which don't support them. Furthermore it is
2813 * possible that a new diff image was attached after a saved state
2814 * which changes the UUID.
2815 * However the device "name + device instance + LUN" triple the disk is
2816 * attached to is always constant for saved states.
2817 */
2818 char *pszId = NULL;
2819 uint32_t iInstance, iLUN;
2820 const char *pcszController;
2821
2822 rc = pThis->pDrvMediaPort->pfnQueryDeviceLocation(pThis->pDrvMediaPort, &pcszController,
2823 &iInstance, &iLUN);
2824 if (RT_FAILURE(rc))
2825 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2826 N_("DrvVD: Configuration error: Could not query device data"));
2827 else
2828 {
2829 int cbStr = RTStrAPrintf(&pszId, "%s-%d-%d", pcszController, iInstance, iLUN);
2830
2831 if (cbStr > 0)
2832 {
2833 rc = PDMDrvHlpBlkCacheRetain(pDrvIns, &pThis->pBlkCache,
2834 drvvdBlkCacheXferComplete,
2835 drvvdBlkCacheXferEnqueue,
2836 drvvdBlkCacheXferEnqueueDiscard,
2837 pszId);
2838 if (rc == VERR_NOT_SUPPORTED)
2839 {
2840 LogRel(("VD: Block cache is not supported\n"));
2841 rc = VINF_SUCCESS;
2842 }
2843 else
2844 AssertRC(rc);
2845
2846 RTStrFree(pszId);
2847 }
2848 else
2849 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2850 N_("DrvVD: Out of memory when creating block cache"));
2851 }
2852 }
2853
2854 /*
2855 * Register a load-done callback so we can undo TempReadOnly config before
2856 * we get to drvvdResume. Autoamtically deregistered upon destruction.
2857 */
2858 if (RT_SUCCESS(rc))
2859 rc = PDMDrvHlpSSMRegisterEx(pDrvIns, 0 /* version */, 0 /* cbGuess */,
2860 NULL /*pfnLivePrep*/, NULL /*pfnLiveExec*/, NULL /*pfnLiveVote*/,
2861 NULL /*pfnSavePrep*/, NULL /*pfnSaveExec*/, NULL /*pfnSaveDone*/,
2862 NULL /*pfnDonePrep*/, NULL /*pfnLoadExec*/, drvvdLoadDone);
2863
2864 /* Setup the boot acceleration stuff if enabled. */
2865 if (RT_SUCCESS(rc) && pThis->fBootAccelEnabled)
2866 {
2867 pThis->cbDisk = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
2868 Assert(pThis->cbDisk > 0);
2869 pThis->pbData = (uint8_t *)RTMemAllocZ(pThis->cbBootAccelBuffer);
2870 if (pThis->pbData)
2871 {
2872 pThis->fBootAccelActive = true;
2873 pThis->offDisk = 0;
2874 pThis->cbDataValid = 0;
2875 LogRel(("VD: Boot acceleration enabled\n"));
2876 }
2877 else
2878 LogRel(("VD: Boot acceleration, out of memory, disabled\n"));
2879 }
2880
2881 if (RT_FAILURE(rc))
2882 {
2883 if (RT_VALID_PTR(pszName))
2884 MMR3HeapFree(pszName);
2885 if (RT_VALID_PTR(pszFormat))
2886 MMR3HeapFree(pszFormat);
2887 /* drvvdDestruct does the rest. */
2888 }
2889
2890 LogFlowFunc(("returns %Rrc\n", rc));
2891 return rc;
2892}
2893
2894/**
2895 * VBox disk container media driver registration record.
2896 */
2897const PDMDRVREG g_DrvVD =
2898{
2899 /* u32Version */
2900 PDM_DRVREG_VERSION,
2901 /* szName */
2902 "VD",
2903 /* szRCMod */
2904 "",
2905 /* szR0Mod */
2906 "",
2907 /* pszDescription */
2908 "Generic VBox disk media driver.",
2909 /* fFlags */
2910 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2911 /* fClass. */
2912 PDM_DRVREG_CLASS_MEDIA,
2913 /* cMaxInstances */
2914 ~0U,
2915 /* cbInstance */
2916 sizeof(VBOXDISK),
2917 /* pfnConstruct */
2918 drvvdConstruct,
2919 /* pfnDestruct */
2920 drvvdDestruct,
2921 /* pfnRelocate */
2922 NULL,
2923 /* pfnIOCtl */
2924 NULL,
2925 /* pfnPowerOn */
2926 drvvdPowerOn,
2927 /* pfnReset */
2928 drvvdReset,
2929 /* pfnSuspend */
2930 drvvdSuspend,
2931 /* pfnResume */
2932 drvvdResume,
2933 /* pfnAttach */
2934 NULL,
2935 /* pfnDetach */
2936 NULL,
2937 /* pfnPowerOff */
2938 NULL,
2939 /* pfnSoftReset */
2940 NULL,
2941 /* u32EndVersion */
2942 PDM_DRVREG_VERSION
2943};
2944
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