VirtualBox

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

Last change on this file since 45349 was 45061, checked in by vboxsync, 12 years ago

Review of PDM driver destructors making sure that variables they use are correctly initialized in the constructor. Found several RTFileClose(0) cases.

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