VirtualBox

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

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

VCI: Updates

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