VirtualBox

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

Last change on this file since 40754 was 40282, checked in by vboxsync, 12 years ago

*: gcc-4.7: ~0 => ~0U in initializers (warning: narrowing conversion of -1' from int' to `unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing])

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

© 2023 Oracle
ContactPrivacy policyTerms of Use