VirtualBox

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

Last change on this file since 34433 was 34433, checked in by vboxsync, 14 years ago

Storage: Introduce interface to query the location of a medium (device + instance + LUN) in a VM to generate IDs which are constant for saved states

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette