VirtualBox

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

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

Move the misc files the in src/VBox/Devices/ directory into a build/ subdirectory, changing their names to match the target module.

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