VirtualBox

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

Last change on this file since 30468 was 30468, checked in by vboxsync, 15 years ago

IPRT:

  • Corrected RTSGBUF member names.
  • Corrected RTSgBufInit parameter names.
  • Don't use AssertReturnVoid (and variations) in RTSgBuf* as there the result is that we use uninitialized structures. Better to crash and burn in IPRT then.
  • Added RTSocketSgWriteL/LV and RTTcpSgWriteL/LV for lazy guys like me.
  • RTSocketSgWrite doesn't need a do {} while (0).
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 62.9 KB
Line 
1/* $Id: DrvVD.cpp 30468 2010-06-28 13:58:04Z 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.215389.xyz. 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/VBoxHDD.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
36#ifdef VBOX_WITH_INIP
37/* All lwip header files are not C++ safe. So hack around this. */
38RT_C_DECLS_BEGIN
39#include <lwip/inet.h>
40#include <lwip/tcp.h>
41#include <lwip/sockets.h>
42RT_C_DECLS_END
43#endif /* VBOX_WITH_INIP */
44
45#include "Builtins.h"
46
47#ifdef VBOX_WITH_INIP
48/* Small hack to get at lwIP initialized status */
49extern bool DevINIPConfigured(void);
50#endif /* VBOX_WITH_INIP */
51
52
53/*******************************************************************************
54* Defined types, constants and macros *
55*******************************************************************************/
56
57/** Converts a pointer to VBOXDISK::IMedia to a PVBOXDISK. */
58#define PDMIMEDIA_2_VBOXDISK(pInterface) \
59 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMedia)) )
60
61/** Converts a pointer to PDMDRVINS::IBase to a PPDMDRVINS. */
62#define PDMIBASE_2_DRVINS(pInterface) \
63 ( (PPDMDRVINS)((uintptr_t)pInterface - RT_OFFSETOF(PDMDRVINS, IBase)) )
64
65/** Converts a pointer to PDMDRVINS::IBase to a PVBOXDISK. */
66#define PDMIBASE_2_VBOXDISK(pInterface) \
67 ( PDMINS_2_DATA(PDMIBASE_2_DRVINS(pInterface), PVBOXDISK) )
68
69/** Converts a pointer to VBOXDISK::IMediaAsync to a PVBOXDISK. */
70#define PDMIMEDIAASYNC_2_VBOXDISK(pInterface) \
71 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMediaAsync)) )
72
73/**
74 * VBox disk container, image information, private part.
75 */
76
77typedef struct VBOXIMAGE
78{
79 /** Pointer to next image. */
80 struct VBOXIMAGE *pNext;
81 /** Pointer to list of VD interfaces. Per-image. */
82 PVDINTERFACE pVDIfsImage;
83 /** Common structure for the configuration information interface. */
84 VDINTERFACE VDIConfig;
85} VBOXIMAGE, *PVBOXIMAGE;
86
87/**
88 * Storage backend data.
89 */
90typedef struct DRVVDSTORAGEBACKEND
91{
92 /** PDM async completion end point. */
93 PPDMASYNCCOMPLETIONENDPOINT pEndpoint;
94 /** The template. */
95 PPDMASYNCCOMPLETIONTEMPLATE pTemplate;
96 /** Event semaphore for synchronous operations. */
97 RTSEMEVENT EventSem;
98 /** Flag whether a synchronous operation is currently pending. */
99 volatile bool fSyncIoPending;
100 /** Return code of the last completed request. */
101 int rcReqLast;
102 /** Callback routine */
103 PFNVDCOMPLETED pfnCompleted;
104
105 /** Pointer to the optional thread synchronization interface of the disk. */
106 PVDINTERFACE pInterfaceThreadSync;
107 /** Pointer to the optional thread synchronization callbacks of the disk. */
108 PVDINTERFACETHREADSYNC pInterfaceThreadSyncCallbacks;
109} DRVVDSTORAGEBACKEND, *PDRVVDSTORAGEBACKEND;
110
111/**
112 * VBox disk container media main structure, private part.
113 *
114 * @implements PDMIMEDIA
115 * @implements PDMIMEDIAASYNC
116 * @implements VDINTERFACEERROR
117 * @implements VDINTERFACETCPNET
118 * @implements VDINTERFACEASYNCIO
119 * @implements VDINTERFACECONFIG
120 */
121typedef struct VBOXDISK
122{
123 /** The VBox disk container. */
124 PVBOXHDD pDisk;
125 /** The media interface. */
126 PDMIMEDIA IMedia;
127 /** Pointer to the driver instance. */
128 PPDMDRVINS pDrvIns;
129 /** Flag whether suspend has changed image open mode to read only. */
130 bool fTempReadOnly;
131 /** Flag whether to use the runtime (true) or startup error facility. */
132 bool fErrorUseRuntime;
133 /** Pointer to list of VD interfaces. Per-disk. */
134 PVDINTERFACE pVDIfsDisk;
135 /** Common structure for the supported error interface. */
136 VDINTERFACE VDIError;
137 /** Callback table for error interface. */
138 VDINTERFACEERROR VDIErrorCallbacks;
139 /** Common structure for the supported TCP network stack interface. */
140 VDINTERFACE VDITcpNet;
141 /** Callback table for TCP network stack interface. */
142 VDINTERFACETCPNET VDITcpNetCallbacks;
143 /** Common structure for the supported async I/O interface. */
144 VDINTERFACE VDIAsyncIO;
145 /** Callback table for async I/O interface. */
146 VDINTERFACEASYNCIO VDIAsyncIOCallbacks;
147 /** Common structure for the supported thread synchronization interface. */
148 VDINTERFACE VDIThreadSync;
149 /** Callback table for thread synchronization interface. */
150 VDINTERFACETHREADSYNC VDIThreadSyncCallbacks;
151 /** Callback table for the configuration information interface. */
152 VDINTERFACECONFIG VDIConfigCallbacks;
153 /** Flag whether opened disk suppports 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 a merge operation has been set up. */
162 bool fMergePending;
163 /** Synchronization to prevent destruction before merge finishes. */
164 RTSEMFASTMUTEX MergeCompleteMutex;
165 /** Synchronization between merge and other image accesses. */
166 RTSEMRW MergeLock;
167 /** Source image index for merging. */
168 unsigned uMergeSource;
169 /** Target image index for merging. */
170 unsigned uMergeTarget;
171} VBOXDISK, *PVBOXDISK;
172
173
174/*******************************************************************************
175* Internal Functions *
176*******************************************************************************/
177
178/**
179 * Internal: allocate new image descriptor and put it in the list
180 */
181static PVBOXIMAGE drvvdNewImage(PVBOXDISK pThis)
182{
183 AssertPtr(pThis);
184 PVBOXIMAGE pImage = (PVBOXIMAGE)RTMemAllocZ(sizeof(VBOXIMAGE));
185 if (pImage)
186 {
187 pImage->pVDIfsImage = NULL;
188 PVBOXIMAGE *pp = &pThis->pImages;
189 while (*pp != NULL)
190 pp = &(*pp)->pNext;
191 *pp = pImage;
192 pImage->pNext = NULL;
193 }
194
195 return pImage;
196}
197
198/**
199 * Internal: free the list of images descriptors.
200 */
201static void drvvdFreeImages(PVBOXDISK pThis)
202{
203 while (pThis->pImages != NULL)
204 {
205 PVBOXIMAGE p = pThis->pImages;
206 pThis->pImages = pThis->pImages->pNext;
207 RTMemFree(p);
208 }
209}
210
211
212/**
213 * Make the image temporarily read-only.
214 *
215 * @returns VBox status code.
216 * @param pThis The driver instance data.
217 */
218static int drvvdSetReadonly(PVBOXDISK pThis)
219{
220 int rc = VINF_SUCCESS;
221 if (!VDIsReadOnly(pThis->pDisk))
222 {
223 unsigned uOpenFlags;
224 rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
225 AssertRC(rc);
226 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
227 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
228 AssertRC(rc);
229 pThis->fTempReadOnly = true;
230 }
231 return rc;
232}
233
234
235/**
236 * Undo the temporary read-only status of the image.
237 *
238 * @returns VBox status code.
239 * @param pThis The driver instance data.
240 */
241static int drvvdSetWritable(PVBOXDISK pThis)
242{
243 int rc = VINF_SUCCESS;
244 if (pThis->fTempReadOnly)
245 {
246 unsigned uOpenFlags;
247 rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
248 AssertRC(rc);
249 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
250 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
251 if (RT_SUCCESS(rc))
252 pThis->fTempReadOnly = false;
253 else
254 AssertRC(rc);
255 }
256 return rc;
257}
258
259
260/*******************************************************************************
261* Error reporting callback *
262*******************************************************************************/
263
264static void drvvdErrorCallback(void *pvUser, int rc, RT_SRC_POS_DECL,
265 const char *pszFormat, va_list va)
266{
267 PPDMDRVINS pDrvIns = (PPDMDRVINS)pvUser;
268 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
269 if (pThis->fErrorUseRuntime)
270 /* We must not pass VMSETRTERR_FLAGS_FATAL as it could lead to a
271 * deadlock: We are probably executed in a thread context != EMT
272 * and the EM thread would wait until every thread is suspended
273 * but we would wait for the EM thread ... */
274
275 PDMDrvHlpVMSetRuntimeErrorV(pDrvIns, /* fFlags=*/ 0, "DrvVD", pszFormat, va);
276 else
277 PDMDrvHlpVMSetErrorV(pDrvIns, rc, RT_SRC_POS_ARGS, pszFormat, va);
278}
279
280/*******************************************************************************
281* VD Async I/O interface implementation *
282*******************************************************************************/
283
284#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
285
286static DECLCALLBACK(void) drvvdAsyncTaskCompleted(PPDMDRVINS pDrvIns, void *pvTemplateUser, void *pvUser, int rcReq)
287{
288 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
289 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pvTemplateUser;
290
291 if (pStorageBackend->fSyncIoPending)
292 {
293 pStorageBackend->rcReqLast = rcReq;
294 pStorageBackend->fSyncIoPending = false;
295 RTSemEventSignal(pStorageBackend->EventSem);
296 }
297 else
298 {
299 int rc;
300
301 AssertPtr(pStorageBackend->pfnCompleted);
302 rc = pStorageBackend->pfnCompleted(pvUser, rcReq);
303 AssertRC(rc);
304 }
305}
306
307static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation,
308 unsigned uOpenFlags,
309 PFNVDCOMPLETED pfnCompleted,
310 PVDINTERFACE pVDIfsDisk,
311 void **ppStorage)
312{
313 PVBOXDISK pThis = (PVBOXDISK)pvUser;
314 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)RTMemAllocZ(sizeof(DRVVDSTORAGEBACKEND));
315 int rc = VINF_SUCCESS;
316
317 if (pStorageBackend)
318 {
319 pStorageBackend->fSyncIoPending = false;
320 pStorageBackend->rcReqLast = VINF_SUCCESS;
321 pStorageBackend->pfnCompleted = pfnCompleted;
322 pStorageBackend->pInterfaceThreadSync = NULL;
323 pStorageBackend->pInterfaceThreadSyncCallbacks = NULL;
324
325 pStorageBackend->pInterfaceThreadSync = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_THREADSYNC);
326 if (RT_UNLIKELY(pStorageBackend->pInterfaceThreadSync))
327 pStorageBackend->pInterfaceThreadSyncCallbacks = VDGetInterfaceThreadSync(pStorageBackend->pInterfaceThreadSync);
328
329 rc = RTSemEventCreate(&pStorageBackend->EventSem);
330 if (RT_SUCCESS(rc))
331 {
332 rc = PDMDrvHlpAsyncCompletionTemplateCreate(pThis->pDrvIns, &pStorageBackend->pTemplate,
333 drvvdAsyncTaskCompleted, pStorageBackend, "AsyncTaskCompleted");
334 if (RT_SUCCESS(rc))
335 {
336 rc = PDMR3AsyncCompletionEpCreateForFile(&pStorageBackend->pEndpoint, pszLocation,
337 uOpenFlags & VD_INTERFACEASYNCIO_OPEN_FLAGS_READONLY
338 ? PDMACEP_FILE_FLAGS_READ_ONLY | PDMACEP_FILE_FLAGS_CACHING
339 : PDMACEP_FILE_FLAGS_CACHING,
340 pStorageBackend->pTemplate);
341 if (RT_SUCCESS(rc))
342 {
343 *ppStorage = pStorageBackend;
344 return VINF_SUCCESS;
345 }
346
347 PDMR3AsyncCompletionTemplateDestroy(pStorageBackend->pTemplate);
348 }
349 RTSemEventDestroy(pStorageBackend->EventSem);
350 }
351 RTMemFree(pStorageBackend);
352 }
353 else
354 rc = VERR_NO_MEMORY;
355
356 return rc;
357}
358
359static DECLCALLBACK(int) drvvdAsyncIOClose(void *pvUser, void *pStorage)
360{
361 PVBOXDISK pThis = (PVBOXDISK)pvUser;
362 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
363
364 PDMR3AsyncCompletionEpClose(pStorageBackend->pEndpoint);
365 PDMR3AsyncCompletionTemplateDestroy(pStorageBackend->pTemplate);
366 RTSemEventDestroy(pStorageBackend->EventSem);
367 RTMemFree(pStorageBackend);
368
369 return VINF_SUCCESS;;
370}
371
372static DECLCALLBACK(int) drvvdAsyncIOReadSync(void *pvUser, void *pStorage, uint64_t uOffset,
373 size_t cbRead, void *pvBuf, size_t *pcbRead)
374{
375 PVBOXDISK pThis = (PVBOXDISK)pvUser;
376 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
377 RTSGSEG DataSeg;
378 PPDMASYNCCOMPLETIONTASK pTask;
379
380 Assert(!pStorageBackend->fSyncIoPending);
381 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
382 DataSeg.cbSeg = cbRead;
383 DataSeg.pvSeg = pvBuf;
384
385 int rc = PDMR3AsyncCompletionEpRead(pStorageBackend->pEndpoint, uOffset, &DataSeg, 1, cbRead, NULL, &pTask);
386 if (RT_FAILURE(rc))
387 return rc;
388
389 if (rc == VINF_AIO_TASK_PENDING)
390 {
391 /* Wait */
392 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
393 AssertRC(rc);
394 }
395 else
396 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
397
398 if (pcbRead)
399 *pcbRead = cbRead;
400
401 return pStorageBackend->rcReqLast;
402}
403
404static DECLCALLBACK(int) drvvdAsyncIOWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
405 size_t cbWrite, const void *pvBuf, size_t *pcbWritten)
406{
407 PVBOXDISK pThis = (PVBOXDISK)pvUser;
408 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
409 RTSGSEG DataSeg;
410 PPDMASYNCCOMPLETIONTASK pTask;
411
412 Assert(!pStorageBackend->fSyncIoPending);
413 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
414 DataSeg.cbSeg = cbWrite;
415 DataSeg.pvSeg = (void *)pvBuf;
416
417 int rc = PDMR3AsyncCompletionEpWrite(pStorageBackend->pEndpoint, uOffset, &DataSeg, 1, cbWrite, NULL, &pTask);
418 if (RT_FAILURE(rc))
419 return rc;
420
421 if (rc == VINF_AIO_TASK_PENDING)
422 {
423 /* Wait */
424 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
425 AssertRC(rc);
426 }
427 else
428 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
429
430 if (pcbWritten)
431 *pcbWritten = cbWrite;
432
433 return pStorageBackend->rcReqLast;
434}
435
436static DECLCALLBACK(int) drvvdAsyncIOFlushSync(void *pvUser, void *pStorage)
437{
438 PVBOXDISK pThis = (PVBOXDISK)pvUser;
439 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
440 PPDMASYNCCOMPLETIONTASK pTask;
441
442 Assert(!pStorageBackend->fSyncIoPending);
443 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
444
445 int rc = PDMR3AsyncCompletionEpFlush(pStorageBackend->pEndpoint, NULL, &pTask);
446 if (RT_FAILURE(rc))
447 return rc;
448
449 if (rc == VINF_AIO_TASK_PENDING)
450 {
451 /* Wait */
452 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
453 AssertRC(rc);
454 }
455 else
456 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
457
458 return pStorageBackend->rcReqLast;
459}
460
461static DECLCALLBACK(int) drvvdAsyncIOReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
462 PCRTSGSEG paSegments, size_t cSegments,
463 size_t cbRead, void *pvCompletion,
464 void **ppTask)
465{
466 PVBOXDISK pThis = (PVBOXDISK)pvUser;
467 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
468
469 int rc = PDMR3AsyncCompletionEpRead(pStorageBackend->pEndpoint, uOffset, paSegments, cSegments, cbRead,
470 pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask);
471 if (rc == VINF_AIO_TASK_PENDING)
472 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
473
474 return rc;
475}
476
477static DECLCALLBACK(int) drvvdAsyncIOWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
478 PCRTSGSEG paSegments, size_t cSegments,
479 size_t cbWrite, void *pvCompletion,
480 void **ppTask)
481{
482 PVBOXDISK pThis = (PVBOXDISK)pvUser;
483 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
484
485 int rc = PDMR3AsyncCompletionEpWrite(pStorageBackend->pEndpoint, uOffset, paSegments, cSegments, cbWrite,
486 pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask);
487 if (rc == VINF_AIO_TASK_PENDING)
488 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
489
490 return rc;
491}
492
493static DECLCALLBACK(int) drvvdAsyncIOFlushAsync(void *pvUser, void *pStorage,
494 void *pvCompletion, void **ppTask)
495{
496 PVBOXDISK pThis = (PVBOXDISK)pvUser;
497 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
498
499 int rc = PDMR3AsyncCompletionEpFlush(pStorageBackend->pEndpoint, pvCompletion,
500 (PPPDMASYNCCOMPLETIONTASK)ppTask);
501 if (rc == VINF_AIO_TASK_PENDING)
502 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
503
504 return rc;
505}
506
507static DECLCALLBACK(int) drvvdAsyncIOGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
508{
509 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
510 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
511
512 return PDMR3AsyncCompletionEpGetSize(pStorageBackend->pEndpoint, pcbSize);
513}
514
515static DECLCALLBACK(int) drvvdAsyncIOSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
516{
517 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
518 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
519
520 int rc = drvvdAsyncIOFlushSync(pvUser, pStorage);
521 if (RT_SUCCESS(rc))
522 rc = PDMR3AsyncCompletionEpSetSize(pStorageBackend->pEndpoint, cbSize);
523
524 return rc;
525}
526
527#endif /* VBOX_WITH_PDM_ASYNC_COMPLETION */
528
529
530/*******************************************************************************
531* VD Thread Synchronization interface implementation *
532*******************************************************************************/
533
534static DECLCALLBACK(int) drvvdThreadStartRead(void *pvUser)
535{
536 PVBOXDISK pThis = (PVBOXDISK)pvUser;
537
538 return RTSemRWRequestRead(pThis->MergeLock, RT_INDEFINITE_WAIT);
539}
540
541static DECLCALLBACK(int) drvvdThreadFinishRead(void *pvUser)
542{
543 PVBOXDISK pThis = (PVBOXDISK)pvUser;
544
545 return RTSemRWReleaseRead(pThis->MergeLock);
546}
547
548static DECLCALLBACK(int) drvvdThreadStartWrite(void *pvUser)
549{
550 PVBOXDISK pThis = (PVBOXDISK)pvUser;
551
552 return RTSemRWRequestWrite(pThis->MergeLock, RT_INDEFINITE_WAIT);
553}
554
555static DECLCALLBACK(int) drvvdThreadFinishWrite(void *pvUser)
556{
557 PVBOXDISK pThis = (PVBOXDISK)pvUser;
558
559 return RTSemRWReleaseWrite(pThis->MergeLock);
560}
561
562
563/*******************************************************************************
564* VD Configuration interface implementation *
565*******************************************************************************/
566
567static bool drvvdCfgAreKeysValid(void *pvUser, const char *pszzValid)
568{
569 return CFGMR3AreValuesValid((PCFGMNODE)pvUser, pszzValid);
570}
571
572static int drvvdCfgQuerySize(void *pvUser, const char *pszName, size_t *pcb)
573{
574 return CFGMR3QuerySize((PCFGMNODE)pvUser, pszName, pcb);
575}
576
577static int drvvdCfgQuery(void *pvUser, const char *pszName, char *pszString, size_t cchString)
578{
579 return CFGMR3QueryString((PCFGMNODE)pvUser, pszName, pszString, cchString);
580}
581
582
583#ifdef VBOX_WITH_INIP
584/*******************************************************************************
585* VD TCP network stack interface implementation - INIP case *
586*******************************************************************************/
587
588typedef union INIPSOCKADDRUNION
589{
590 struct sockaddr Addr;
591 struct sockaddr_in Ipv4;
592} INIPSOCKADDRUNION;
593
594static DECLCALLBACK(int) drvvdINIPFlush(RTSOCKET Sock);
595
596/** @copydoc VDINTERFACETCPNET::pfnClientConnect */
597static DECLCALLBACK(int) drvvdINIPClientConnect(const char *pszAddress, uint32_t uPort, PRTSOCKET pSock)
598{
599 int rc = VINF_SUCCESS;
600 /* First check whether lwIP is set up in this VM instance. */
601 if (!DevINIPConfigured())
602 {
603 LogRelFunc(("no IP stack\n"));
604 return VERR_NET_HOST_UNREACHABLE;
605 }
606 /* Resolve hostname. As there is no standard resolver for lwIP yet,
607 * just accept numeric IP addresses for now. */
608 struct in_addr ip;
609 if (!lwip_inet_aton(pszAddress, &ip))
610 {
611 LogRelFunc(("cannot resolve IP %s\n", pszAddress));
612 return VERR_NET_HOST_UNREACHABLE;
613 }
614 /* Create socket and connect. */
615 int Sock = lwip_socket(PF_INET, SOCK_STREAM, 0);
616 if (Sock != -1)
617 {
618 struct sockaddr_in InAddr = {0};
619 InAddr.sin_family = AF_INET;
620 InAddr.sin_port = htons(uPort);
621 InAddr.sin_addr = ip;
622 if (!lwip_connect(Sock, (struct sockaddr *)&InAddr, sizeof(InAddr)))
623 {
624 *pSock = (RTSOCKET)Sock;
625 return VINF_SUCCESS;
626 }
627 rc = VERR_NET_CONNECTION_REFUSED; /* @todo real solution needed */
628 lwip_close(Sock);
629 }
630 else
631 rc = VERR_NET_CONNECTION_REFUSED; /* @todo real solution needed */
632 return rc;
633}
634
635/** @copydoc VDINTERFACETCPNET::pfnClientClose */
636static DECLCALLBACK(int) drvvdINIPClientClose(RTSOCKET Sock)
637{
638 lwip_close((uintptr_t)Sock);
639 return VINF_SUCCESS; /** @todo real solution needed */
640}
641
642/** @copydoc VDINTERFACETCPNET::pfnSelectOne */
643static DECLCALLBACK(int) drvvdINIPSelectOne(RTSOCKET Sock, RTMSINTERVAL cMillies)
644{
645 fd_set fdsetR;
646 FD_ZERO(&fdsetR);
647 FD_SET((uintptr_t)Sock, &fdsetR);
648 fd_set fdsetE = fdsetR;
649
650 int rc;
651 if (cMillies == RT_INDEFINITE_WAIT)
652 rc = lwip_select((uintptr_t)Sock + 1, &fdsetR, NULL, &fdsetE, NULL);
653 else
654 {
655 struct timeval timeout;
656 timeout.tv_sec = cMillies / 1000;
657 timeout.tv_usec = (cMillies % 1000) * 1000;
658 rc = lwip_select((uintptr_t)Sock + 1, &fdsetR, NULL, &fdsetE, &timeout);
659 }
660 if (rc > 0)
661 return VINF_SUCCESS;
662 if (rc == 0)
663 return VERR_TIMEOUT;
664 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
665}
666
667/** @copydoc VDINTERFACETCPNET::pfnRead */
668static DECLCALLBACK(int) drvvdINIPRead(RTSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
669{
670 /* Do params checking */
671 if (!pvBuffer || !cbBuffer)
672 {
673 AssertMsgFailed(("Invalid params\n"));
674 return VERR_INVALID_PARAMETER;
675 }
676
677 /*
678 * Read loop.
679 * If pcbRead is NULL we have to fill the entire buffer!
680 */
681 size_t cbRead = 0;
682 size_t cbToRead = cbBuffer;
683 for (;;)
684 {
685 /** @todo this clipping here is just in case (the send function
686 * needed it, so I added it here, too). Didn't investigate if this
687 * really has issues. Better be safe than sorry. */
688 ssize_t cbBytesRead = lwip_recv((uintptr_t)Sock, (char *)pvBuffer + cbRead,
689 RT_MIN(cbToRead, 32768), 0);
690 if (cbBytesRead < 0)
691 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution */
692 if (cbBytesRead == 0 && errno) /** @todo r=bird: lwip_recv will not touch errno on Windows. This may apply to other hosts as well */
693 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution */
694 if (pcbRead)
695 {
696 /* return partial data */
697 *pcbRead = cbBytesRead;
698 break;
699 }
700
701 /* read more? */
702 cbRead += cbBytesRead;
703 if (cbRead == cbBuffer)
704 break;
705
706 /* next */
707 cbToRead = cbBuffer - cbRead;
708 }
709
710 return VINF_SUCCESS;
711}
712
713/** @copydoc VDINTERFACETCPNET::pfnWrite */
714static DECLCALLBACK(int) drvvdINIPWrite(RTSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
715{
716 do
717 {
718 /** @todo lwip send only supports up to 65535 bytes in a single
719 * send (stupid limitation buried in the code), so make sure we
720 * don't get any wraparounds. This should be moved to DevINIP
721 * stack interface once that's implemented. */
722 ssize_t cbWritten = lwip_send((uintptr_t)Sock, (void *)pvBuffer,
723 RT_MIN(cbBuffer, 32768), 0);
724 if (cbWritten < 0)
725 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
726 AssertMsg(cbBuffer >= (size_t)cbWritten, ("Wrote more than we requested!!! cbWritten=%d cbBuffer=%d\n",
727 cbWritten, cbBuffer));
728 cbBuffer -= cbWritten;
729 pvBuffer = (const char *)pvBuffer + cbWritten;
730 } while (cbBuffer);
731
732 return VINF_SUCCESS;
733}
734
735/** @copydoc VDINTERFACETCPNET::pfnSgWrite */
736static DECLCALLBACK(int) drvvdINIPSgWrite(RTSOCKET Sock, PCRTSGBUF pSgBuf)
737{
738 int rc = VINF_SUCCESS;
739
740 /* This is an extremely crude emulation, however it's good enough
741 * for our iSCSI code. INIP has no sendmsg(). */
742 for (unsigned i = 0; i < pSgBuf->cSegs; i++)
743 {
744 rc = drvvdINIPWrite(Sock, pSgBuf->paSegs[i].pvSeg,
745 pSgBuf->paSegs[i].cbSeg);
746 if (RT_FAILURE(rc))
747 break;
748 }
749 if (RT_SUCCESS(rc))
750 drvvdINIPFlush(Sock);
751
752 return rc;
753}
754
755/** @copydoc VDINTERFACETCPNET::pfnFlush */
756static DECLCALLBACK(int) drvvdINIPFlush(RTSOCKET Sock)
757{
758 int fFlag = 1;
759 lwip_setsockopt((uintptr_t)Sock, IPPROTO_TCP, TCP_NODELAY,
760 (const char *)&fFlag, sizeof(fFlag));
761 fFlag = 0;
762 lwip_setsockopt((uintptr_t)Sock, IPPROTO_TCP, TCP_NODELAY,
763 (const char *)&fFlag, sizeof(fFlag));
764 return VINF_SUCCESS;
765}
766
767/** @copydoc VDINTERFACETCPNET::pfnSetSendCoalescing */
768static DECLCALLBACK(int) drvvdINIPSetSendCoalescing(RTSOCKET Sock, bool fEnable)
769{
770 int fFlag = fEnable ? 0 : 1;
771 lwip_setsockopt((uintptr_t)Sock, IPPROTO_TCP, TCP_NODELAY,
772 (const char *)&fFlag, sizeof(fFlag));
773 return VINF_SUCCESS;
774}
775
776/** @copydoc VDINTERFACETCPNET::pfnGetLocalAddress */
777static DECLCALLBACK(int) drvvdINIPGetLocalAddress(RTSOCKET Sock, PRTNETADDR pAddr)
778{
779 INIPSOCKADDRUNION u;
780 socklen_t cbAddr = sizeof(u);
781 RT_ZERO(u);
782 if (!lwip_getsockname((uintptr_t)Sock, &u.Addr, &cbAddr))
783 {
784 /*
785 * Convert the address.
786 */
787 if ( cbAddr == sizeof(struct sockaddr_in)
788 && u.Addr.sa_family == AF_INET)
789 {
790 RT_ZERO(*pAddr);
791 pAddr->enmType = RTNETADDRTYPE_IPV4;
792 pAddr->uPort = RT_N2H_U16(u.Ipv4.sin_port);
793 pAddr->uAddr.IPv4.u = u.Ipv4.sin_addr.s_addr;
794 }
795 else
796 return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
797 return VINF_SUCCESS;
798 }
799 return VERR_NET_OPERATION_NOT_SUPPORTED;
800}
801
802/** @copydoc VDINTERFACETCPNET::pfnGetPeerAddress */
803static DECLCALLBACK(int) drvvdINIPGetPeerAddress(RTSOCKET Sock, PRTNETADDR pAddr)
804{
805 INIPSOCKADDRUNION u;
806 socklen_t cbAddr = sizeof(u);
807 RT_ZERO(u);
808 if (!lwip_getpeername((uintptr_t)Sock, &u.Addr, &cbAddr))
809 {
810 /*
811 * Convert the address.
812 */
813 if ( cbAddr == sizeof(struct sockaddr_in)
814 && u.Addr.sa_family == AF_INET)
815 {
816 RT_ZERO(*pAddr);
817 pAddr->enmType = RTNETADDRTYPE_IPV4;
818 pAddr->uPort = RT_N2H_U16(u.Ipv4.sin_port);
819 pAddr->uAddr.IPv4.u = u.Ipv4.sin_addr.s_addr;
820 }
821 else
822 return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
823 return VINF_SUCCESS;
824 }
825 return VERR_NET_OPERATION_NOT_SUPPORTED;
826}
827#endif /* VBOX_WITH_INIP */
828
829
830/*******************************************************************************
831* Media interface methods *
832*******************************************************************************/
833
834/** @copydoc PDMIMEDIA::pfnRead */
835static DECLCALLBACK(int) drvvdRead(PPDMIMEDIA pInterface,
836 uint64_t off, void *pvBuf, size_t cbRead)
837{
838 LogFlow(("%s: off=%#llx pvBuf=%p cbRead=%d\n", __FUNCTION__,
839 off, pvBuf, cbRead));
840 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
841 int rc = VDRead(pThis->pDisk, off, pvBuf, cbRead);
842 if (RT_SUCCESS(rc))
843 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d %.*Rhxd\n", __FUNCTION__,
844 off, pvBuf, cbRead, cbRead, pvBuf));
845 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
846 return rc;
847}
848
849/** @copydoc PDMIMEDIA::pfnWrite */
850static DECLCALLBACK(int) drvvdWrite(PPDMIMEDIA pInterface,
851 uint64_t off, const void *pvBuf,
852 size_t cbWrite)
853{
854 LogFlow(("%s: off=%#llx pvBuf=%p cbWrite=%d\n", __FUNCTION__,
855 off, pvBuf, cbWrite));
856 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
857 Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d %.*Rhxd\n", __FUNCTION__,
858 off, pvBuf, cbWrite, cbWrite, pvBuf));
859 int rc = VDWrite(pThis->pDisk, off, pvBuf, cbWrite);
860 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
861 return rc;
862}
863
864/** @copydoc PDMIMEDIA::pfnFlush */
865static DECLCALLBACK(int) drvvdFlush(PPDMIMEDIA pInterface)
866{
867 LogFlow(("%s:\n", __FUNCTION__));
868 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
869 int rc = VDFlush(pThis->pDisk);
870 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
871 return rc;
872}
873
874/** @copydoc PDMIMEDIA::pfnMerge */
875static DECLCALLBACK(int) drvvdMerge(PPDMIMEDIA pInterface,
876 PFNSIMPLEPROGRESS pfnProgress,
877 void *pvUser)
878{
879 LogFlow(("%s:\n", __FUNCTION__));
880 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
881 int rc = VINF_SUCCESS;
882
883 /* Note: There is an unavoidable race between destruction and another
884 * thread invoking this function. This is handled safely and gracefully by
885 * atomically invalidating the lock handle in drvvdDestruct. */
886 int rc2 = RTSemFastMutexRequest(pThis->MergeCompleteMutex);
887 AssertRC(rc2);
888 if (RT_SUCCESS(rc2) && pThis->fMergePending)
889 {
890 /* Take shortcut: PFNSIMPLEPROGRESS is exactly the same type as
891 * PFNVDPROGRESS, so there's no need for a conversion function. */
892 /** @todo maybe introduce a conversion which limits update frequency. */
893 PVDINTERFACE pVDIfsOperation = NULL;
894 VDINTERFACE VDIProgress;
895 VDINTERFACEPROGRESS VDIProgressCallbacks;
896 VDIProgressCallbacks.cbSize = sizeof(VDINTERFACEPROGRESS);
897 VDIProgressCallbacks.enmInterface = VDINTERFACETYPE_PROGRESS;
898 VDIProgressCallbacks.pfnProgress = pfnProgress;
899 rc2 = VDInterfaceAdd(&VDIProgress, "DrvVD_VDIProgress", VDINTERFACETYPE_PROGRESS,
900 &VDIProgressCallbacks, pvUser, &pVDIfsOperation);
901 AssertRC(rc2);
902 pThis->fMergePending = false;
903 rc = VDMerge(pThis->pDisk, pThis->uMergeSource,
904 pThis->uMergeTarget, pVDIfsOperation);
905 }
906 rc2 = RTSemFastMutexRelease(pThis->MergeCompleteMutex);
907 AssertRC(rc2);
908 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
909 return rc;
910}
911
912/** @copydoc PDMIMEDIA::pfnGetSize */
913static DECLCALLBACK(uint64_t) drvvdGetSize(PPDMIMEDIA pInterface)
914{
915 LogFlow(("%s:\n", __FUNCTION__));
916 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
917 uint64_t cb = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
918 LogFlow(("%s: returns %#llx (%llu)\n", __FUNCTION__, cb, cb));
919 return cb;
920}
921
922/** @copydoc PDMIMEDIA::pfnIsReadOnly */
923static DECLCALLBACK(bool) drvvdIsReadOnly(PPDMIMEDIA pInterface)
924{
925 LogFlow(("%s:\n", __FUNCTION__));
926 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
927 bool f = VDIsReadOnly(pThis->pDisk);
928 LogFlow(("%s: returns %d\n", __FUNCTION__, f));
929 return f;
930}
931
932/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
933static DECLCALLBACK(int) drvvdBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
934 PPDMMEDIAGEOMETRY pPCHSGeometry)
935{
936 LogFlow(("%s:\n", __FUNCTION__));
937 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
938 int rc = VDGetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
939 if (RT_FAILURE(rc))
940 {
941 Log(("%s: geometry not available.\n", __FUNCTION__));
942 rc = VERR_PDM_GEOMETRY_NOT_SET;
943 }
944 LogFlow(("%s: returns %Rrc (CHS=%d/%d/%d)\n", __FUNCTION__,
945 rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
946 return rc;
947}
948
949/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
950static DECLCALLBACK(int) drvvdBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
951 PCPDMMEDIAGEOMETRY pPCHSGeometry)
952{
953 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
954 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
955 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
956 int rc = VDSetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
957 if (rc == VERR_VD_GEOMETRY_NOT_SET)
958 rc = VERR_PDM_GEOMETRY_NOT_SET;
959 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
960 return rc;
961}
962
963/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
964static DECLCALLBACK(int) drvvdBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
965 PPDMMEDIAGEOMETRY pLCHSGeometry)
966{
967 LogFlow(("%s:\n", __FUNCTION__));
968 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
969 int rc = VDGetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
970 if (RT_FAILURE(rc))
971 {
972 Log(("%s: geometry not available.\n", __FUNCTION__));
973 rc = VERR_PDM_GEOMETRY_NOT_SET;
974 }
975 LogFlow(("%s: returns %Rrc (CHS=%d/%d/%d)\n", __FUNCTION__,
976 rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
977 return rc;
978}
979
980/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
981static DECLCALLBACK(int) drvvdBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
982 PCPDMMEDIAGEOMETRY pLCHSGeometry)
983{
984 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
985 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
986 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
987 int rc = VDSetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
988 if (rc == VERR_VD_GEOMETRY_NOT_SET)
989 rc = VERR_PDM_GEOMETRY_NOT_SET;
990 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
991 return rc;
992}
993
994/** @copydoc PDMIMEDIA::pfnGetUuid */
995static DECLCALLBACK(int) drvvdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
996{
997 LogFlow(("%s:\n", __FUNCTION__));
998 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
999 int rc = VDGetUuid(pThis->pDisk, 0, pUuid);
1000 LogFlow(("%s: returns %Rrc ({%RTuuid})\n", __FUNCTION__, rc, pUuid));
1001 return rc;
1002}
1003
1004/*******************************************************************************
1005* Async Media interface methods *
1006*******************************************************************************/
1007
1008static void drvvdAsyncReqComplete(void *pvUser1, void *pvUser2, int rcReq)
1009{
1010 PVBOXDISK pThis = (PVBOXDISK)pvUser1;
1011
1012 int rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort,
1013 pvUser2, rcReq);
1014 AssertRC(rc);
1015}
1016
1017static DECLCALLBACK(int) drvvdStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
1018 PCRTSGSEG paSeg, unsigned cSeg,
1019 size_t cbRead, void *pvUser)
1020{
1021 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d\n pvUser=%#p", __FUNCTION__,
1022 uOffset, paSeg, cSeg, cbRead, pvUser));
1023 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
1024 int rc = VDAsyncRead(pThis->pDisk, uOffset, cbRead, paSeg, cSeg,
1025 drvvdAsyncReqComplete, pThis, pvUser);
1026 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
1027 return rc;
1028}
1029
1030static DECLCALLBACK(int) drvvdStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
1031 PCRTSGSEG paSeg, unsigned cSeg,
1032 size_t cbWrite, void *pvUser)
1033{
1034 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d pvUser=%#p\n", __FUNCTION__,
1035 uOffset, paSeg, cSeg, cbWrite, pvUser));
1036 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
1037 int rc = VDAsyncWrite(pThis->pDisk, uOffset, cbWrite, paSeg, cSeg,
1038 drvvdAsyncReqComplete, pThis, pvUser);
1039 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
1040 return rc;
1041}
1042
1043static DECLCALLBACK(int) drvvdStartFlush(PPDMIMEDIAASYNC pInterface, void *pvUser)
1044{
1045 LogFlow(("%s: pvUser=%#p\n", __FUNCTION__, pvUser));
1046 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
1047 int rc = VDAsyncFlush(pThis->pDisk, drvvdAsyncReqComplete, pThis, pvUser);
1048 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
1049 return rc;
1050}
1051
1052
1053/*******************************************************************************
1054* Base interface methods *
1055*******************************************************************************/
1056
1057/**
1058 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1059 */
1060static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1061{
1062 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
1063 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1064
1065 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1066 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, &pThis->IMedia);
1067 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAASYNC, pThis->fAsyncIOSupported ? &pThis->IMediaAsync : NULL);
1068 return NULL;
1069}
1070
1071
1072/*******************************************************************************
1073* Saved state notification methods *
1074*******************************************************************************/
1075
1076/**
1077 * Load done callback for re-opening the image writable during teleportation.
1078 *
1079 * This is called both for successful and failed load runs, we only care about
1080 * successfull ones.
1081 *
1082 * @returns VBox status code.
1083 * @param pDrvIns The driver instance.
1084 * @param pSSM The saved state handle.
1085 */
1086static DECLCALLBACK(int) drvvdLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1087{
1088 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1089 Assert(!pThis->fErrorUseRuntime);
1090
1091 /* Drop out if we don't have any work to do or if it's a failed load. */
1092 if ( !pThis->fTempReadOnly
1093 || RT_FAILURE(SSMR3HandleGetStatus(pSSM)))
1094 return VINF_SUCCESS;
1095
1096 int rc = drvvdSetWritable(pThis);
1097 if (RT_FAILURE(rc)) /** @todo does the bugger set any errors? */
1098 return SSMR3SetLoadError(pSSM, rc, RT_SRC_POS,
1099 N_("Failed to write lock the images"));
1100 return VINF_SUCCESS;
1101}
1102
1103
1104/*******************************************************************************
1105* Driver methods *
1106*******************************************************************************/
1107
1108static DECLCALLBACK(void) drvvdPowerOff(PPDMDRVINS pDrvIns)
1109{
1110 LogFlow(("%s:\n", __FUNCTION__));
1111 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1112
1113 /*
1114 * We must close the disk here to ensure that
1115 * the backend closes all files before the
1116 * async transport driver is destructed.
1117 */
1118 int rc = VDCloseAll(pThis->pDisk);
1119 AssertRC(rc);
1120}
1121
1122/**
1123 * VM resume notification that we use to undo what the temporary read-only image
1124 * mode set by drvvdSuspend.
1125 *
1126 * Also switch to runtime error mode if we're resuming after a state load
1127 * without having been powered on first.
1128 *
1129 * @param pDrvIns The driver instance data.
1130 *
1131 * @todo The VMSetError vs VMSetRuntimeError mess must be fixed elsewhere,
1132 * we're making assumptions about Main behavior here!
1133 */
1134static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
1135{
1136 LogFlow(("%s:\n", __FUNCTION__));
1137 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1138 drvvdSetWritable(pThis);
1139 pThis->fErrorUseRuntime = true;
1140}
1141
1142/**
1143 * The VM is being suspended, temporarily change to read-only image mode.
1144 *
1145 * This is important for several reasons:
1146 * -# It makes sure that there are no pending writes to the image. Most
1147 * backends implements this by closing and reopening the image in read-only
1148 * mode.
1149 * -# It allows Main to read the images during snapshotting without having
1150 * to account for concurrent writes.
1151 * -# This is essential for making teleportation targets sharing images work
1152 * right. Both with regards to caching and with regards to file sharing
1153 * locks (RTFILE_O_DENY_*). (See also drvvdLoadDone.)
1154 *
1155 * @param pDrvIns The driver instance data.
1156 */
1157static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
1158{
1159 LogFlow(("%s:\n", __FUNCTION__));
1160 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1161 drvvdSetReadonly(pThis);
1162}
1163
1164/**
1165 * VM PowerOn notification for undoing the TempReadOnly config option and
1166 * changing to runtime error mode.
1167 *
1168 * @param pDrvIns The driver instance data.
1169 *
1170 * @todo The VMSetError vs VMSetRuntimeError mess must be fixed elsewhere,
1171 * we're making assumptions about Main behavior here!
1172 */
1173static DECLCALLBACK(void) drvvdPowerOn(PPDMDRVINS pDrvIns)
1174{
1175 LogFlow(("%s:\n", __FUNCTION__));
1176 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1177 drvvdSetWritable(pThis);
1178 pThis->fErrorUseRuntime = true;
1179}
1180
1181/**
1182 * @copydoc FNPDMDRVDESTRUCT
1183 */
1184static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
1185{
1186 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1187 LogFlow(("%s:\n", __FUNCTION__));
1188 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1189
1190 RTSEMFASTMUTEX mutex;
1191 ASMAtomicXchgHandle(&pThis->MergeCompleteMutex, NIL_RTSEMFASTMUTEX, &mutex);
1192 if (mutex != NIL_RTSEMFASTMUTEX)
1193 {
1194 /* Request the semaphore to wait until a potentially running merge
1195 * operation has been finished. */
1196 int rc = RTSemFastMutexRequest(mutex);
1197 AssertRC(rc);
1198 pThis->fMergePending = false;
1199 rc = RTSemFastMutexRelease(mutex);
1200 AssertRC(rc);
1201 rc = RTSemFastMutexDestroy(mutex);
1202 AssertRC(rc);
1203 }
1204
1205 if (VALID_PTR(pThis->pDisk))
1206 {
1207 VDDestroy(pThis->pDisk);
1208 pThis->pDisk = NULL;
1209 }
1210 drvvdFreeImages(pThis);
1211
1212 if (pThis->MergeLock != NIL_RTSEMRW)
1213 {
1214 int rc = RTSemRWDestroy(pThis->MergeLock);
1215 AssertRC(rc);
1216 pThis->MergeLock = NIL_RTSEMRW;
1217 }
1218}
1219
1220/**
1221 * Construct a VBox disk media driver instance.
1222 *
1223 * @copydoc FNPDMDRVCONSTRUCT
1224 */
1225static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
1226 PCFGMNODE pCfg,
1227 uint32_t fFlags)
1228{
1229 LogFlow(("%s:\n", __FUNCTION__));
1230 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1231 int rc = VINF_SUCCESS;
1232 char *pszName = NULL; /**< The path of the disk image file. */
1233 char *pszFormat = NULL; /**< The format backed to use for this image. */
1234 bool fReadOnly; /**< True if the media is read-only. */
1235 bool fHonorZeroWrites; /**< True if zero blocks should be written. */
1236 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1237
1238 /*
1239 * Init the static parts.
1240 */
1241 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
1242 pThis->pDrvIns = pDrvIns;
1243 pThis->fTempReadOnly = false;
1244 pThis->pDisk = NULL;
1245 pThis->fAsyncIOSupported = false;
1246 pThis->fMergePending = false;
1247 pThis->MergeCompleteMutex = NIL_RTSEMFASTMUTEX;
1248 pThis->uMergeSource = VD_LAST_IMAGE;
1249 pThis->uMergeTarget = VD_LAST_IMAGE;
1250
1251 /* IMedia */
1252 pThis->IMedia.pfnRead = drvvdRead;
1253 pThis->IMedia.pfnWrite = drvvdWrite;
1254 pThis->IMedia.pfnFlush = drvvdFlush;
1255 pThis->IMedia.pfnMerge = drvvdMerge;
1256 pThis->IMedia.pfnGetSize = drvvdGetSize;
1257 pThis->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
1258 pThis->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
1259 pThis->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
1260 pThis->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
1261 pThis->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
1262 pThis->IMedia.pfnGetUuid = drvvdGetUuid;
1263
1264 /* IMediaAsync */
1265 pThis->IMediaAsync.pfnStartRead = drvvdStartRead;
1266 pThis->IMediaAsync.pfnStartWrite = drvvdStartWrite;
1267 pThis->IMediaAsync.pfnStartFlush = drvvdStartFlush;
1268
1269 /* Initialize supported VD interfaces. */
1270 pThis->pVDIfsDisk = NULL;
1271
1272 pThis->VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
1273 pThis->VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
1274 pThis->VDIErrorCallbacks.pfnError = drvvdErrorCallback;
1275 pThis->VDIErrorCallbacks.pfnMessage = NULL;
1276
1277 rc = VDInterfaceAdd(&pThis->VDIError, "DrvVD_VDIError", VDINTERFACETYPE_ERROR,
1278 &pThis->VDIErrorCallbacks, pDrvIns, &pThis->pVDIfsDisk);
1279 AssertRC(rc);
1280
1281 /* This is just prepared here, the actual interface is per-image, so it's
1282 * added later. No need to have separate callback tables. */
1283 pThis->VDIConfigCallbacks.cbSize = sizeof(VDINTERFACECONFIG);
1284 pThis->VDIConfigCallbacks.enmInterface = VDINTERFACETYPE_CONFIG;
1285 pThis->VDIConfigCallbacks.pfnAreKeysValid = drvvdCfgAreKeysValid;
1286 pThis->VDIConfigCallbacks.pfnQuerySize = drvvdCfgQuerySize;
1287 pThis->VDIConfigCallbacks.pfnQuery = drvvdCfgQuery;
1288
1289 /* List of images is empty now. */
1290 pThis->pImages = NULL;
1291
1292 /* Try to attach async media port interface above.*/
1293 pThis->pDrvMediaAsyncPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAASYNCPORT);
1294
1295 /*
1296 * Validate configuration and find all parent images.
1297 * It's sort of up side down from the image dependency tree.
1298 */
1299 bool fHostIP = false;
1300 bool fUseNewIo = false;
1301 unsigned iLevel = 0;
1302 PCFGMNODE pCurNode = pCfg;
1303
1304 for (;;)
1305 {
1306 bool fValid;
1307
1308 if (pCurNode == pCfg)
1309 {
1310 /* Toplevel configuration additionally contains the global image
1311 * open flags. Some might be converted to per-image flags later. */
1312 fValid = CFGMR3AreValuesValid(pCurNode,
1313 "Format\0Path\0"
1314 "ReadOnly\0TempReadOnly\0HonorZeroWrites\0"
1315 "HostIPStack\0UseNewIo\0"
1316 "SetupMerge\0MergeSource\0MergeTarget\0");
1317 }
1318 else
1319 {
1320 /* All other image configurations only contain image name and
1321 * the format information. */
1322 fValid = CFGMR3AreValuesValid(pCurNode, "Format\0Path\0"
1323 "MergeSource\0MergeTarget\0");
1324 }
1325 if (!fValid)
1326 {
1327 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
1328 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
1329 break;
1330 }
1331
1332 if (pCurNode == pCfg)
1333 {
1334 rc = CFGMR3QueryBoolDef(pCurNode, "HostIPStack", &fHostIP, true);
1335 if (RT_FAILURE(rc))
1336 {
1337 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1338 N_("DrvVD: Configuration error: Querying \"HostIPStack\" as boolean failed"));
1339 break;
1340 }
1341
1342 rc = CFGMR3QueryBoolDef(pCurNode, "HonorZeroWrites", &fHonorZeroWrites, false);
1343 if (RT_FAILURE(rc))
1344 {
1345 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1346 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
1347 break;
1348 }
1349
1350 rc = CFGMR3QueryBoolDef(pCurNode, "ReadOnly", &fReadOnly, false);
1351 if (RT_FAILURE(rc))
1352 {
1353 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1354 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
1355 break;
1356 }
1357
1358 rc = CFGMR3QueryBoolDef(pCurNode, "TempReadOnly", &pThis->fTempReadOnly, false);
1359 if (RT_FAILURE(rc))
1360 {
1361 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1362 N_("DrvVD: Configuration error: Querying \"TempReadOnly\" as boolean failed"));
1363 break;
1364 }
1365 if (fReadOnly && pThis->fTempReadOnly)
1366 {
1367 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
1368 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"TempReadOnly\" are set"));
1369 break;
1370 }
1371 rc = CFGMR3QueryBoolDef(pCurNode, "UseNewIo", &fUseNewIo, false);
1372 if (RT_FAILURE(rc))
1373 {
1374 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1375 N_("DrvVD: Configuration error: Querying \"UseNewIo\" as boolean failed"));
1376 break;
1377 }
1378 rc = CFGMR3QueryBoolDef(pCurNode, "SetupMerge", &pThis->fMergePending, false);
1379 if (RT_FAILURE(rc))
1380 {
1381 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1382 N_("DrvVD: Configuration error: Querying \"SetupMerge\" as boolean failed"));
1383 break;
1384 }
1385 if (fReadOnly && pThis->fMergePending)
1386 {
1387 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
1388 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"MergePending\" are set"));
1389 break;
1390 }
1391 }
1392
1393 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
1394 if (!pParent)
1395 break;
1396 pCurNode = pParent;
1397 iLevel++;
1398 }
1399
1400 /*
1401 * Create the image container and the necessary interfaces.
1402 */
1403 if (RT_SUCCESS(rc))
1404 {
1405 /* First of all figure out what kind of TCP networking stack interface
1406 * to use. This is done unconditionally, as backends which don't need
1407 * it will just ignore it. */
1408 if (fHostIP)
1409 {
1410 pThis->VDITcpNetCallbacks.cbSize = sizeof(VDINTERFACETCPNET);
1411 pThis->VDITcpNetCallbacks.enmInterface = VDINTERFACETYPE_TCPNET;
1412 pThis->VDITcpNetCallbacks.pfnClientConnect = RTTcpClientConnect;
1413 pThis->VDITcpNetCallbacks.pfnClientClose = RTTcpClientClose;
1414 pThis->VDITcpNetCallbacks.pfnSelectOne = RTTcpSelectOne;
1415 pThis->VDITcpNetCallbacks.pfnRead = RTTcpRead;
1416 pThis->VDITcpNetCallbacks.pfnWrite = RTTcpWrite;
1417 pThis->VDITcpNetCallbacks.pfnSgWrite = RTTcpSgWrite;
1418 pThis->VDITcpNetCallbacks.pfnFlush = RTTcpFlush;
1419 pThis->VDITcpNetCallbacks.pfnSetSendCoalescing = RTTcpSetSendCoalescing;
1420 pThis->VDITcpNetCallbacks.pfnGetLocalAddress = RTTcpGetLocalAddress;
1421 pThis->VDITcpNetCallbacks.pfnGetPeerAddress = RTTcpGetPeerAddress;
1422 }
1423 else
1424 {
1425#ifndef VBOX_WITH_INIP
1426 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
1427 RT_SRC_POS, N_("DrvVD: Configuration error: TCP over Internal Networking not compiled in"));
1428#else /* VBOX_WITH_INIP */
1429 pThis->VDITcpNetCallbacks.cbSize = sizeof(VDINTERFACETCPNET);
1430 pThis->VDITcpNetCallbacks.enmInterface = VDINTERFACETYPE_TCPNET;
1431 pThis->VDITcpNetCallbacks.pfnClientConnect = drvvdINIPClientConnect;
1432 pThis->VDITcpNetCallbacks.pfnClientClose = drvvdINIPClientClose;
1433 pThis->VDITcpNetCallbacks.pfnSelectOne = drvvdINIPSelectOne;
1434 pThis->VDITcpNetCallbacks.pfnRead = drvvdINIPRead;
1435 pThis->VDITcpNetCallbacks.pfnWrite = drvvdINIPWrite;
1436 pThis->VDITcpNetCallbacks.pfnSgWrite = drvvdINIPSgWrite;
1437 pThis->VDITcpNetCallbacks.pfnFlush = drvvdINIPFlush;
1438 pThis->VDITcpNetCallbacks.pfnSetSendCoalescing = drvvdINIPSetSendCoalescing;
1439 pThis->VDITcpNetCallbacks.pfnGetLocalAddress = drvvdINIPGetLocalAddress;
1440 pThis->VDITcpNetCallbacks.pfnGetPeerAddress = drvvdINIPGetPeerAddress;
1441#endif /* VBOX_WITH_INIP */
1442 }
1443 if (RT_SUCCESS(rc))
1444 {
1445 rc = VDInterfaceAdd(&pThis->VDITcpNet, "DrvVD_INIP",
1446 VDINTERFACETYPE_TCPNET,
1447 &pThis->VDITcpNetCallbacks, NULL,
1448 &pThis->pVDIfsDisk);
1449 }
1450
1451 /** @todo quick hack to work around problems in the async I/O
1452 * implementation (rw semaphore thread ownership problem)
1453 * while a merge is running. Remove once this is fixed. */
1454 if (pThis->fMergePending)
1455 fUseNewIo = false;
1456
1457 if (RT_SUCCESS(rc) && fUseNewIo)
1458 {
1459#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
1460 pThis->VDIAsyncIOCallbacks.cbSize = sizeof(VDINTERFACEASYNCIO);
1461 pThis->VDIAsyncIOCallbacks.enmInterface = VDINTERFACETYPE_ASYNCIO;
1462 pThis->VDIAsyncIOCallbacks.pfnOpen = drvvdAsyncIOOpen;
1463 pThis->VDIAsyncIOCallbacks.pfnClose = drvvdAsyncIOClose;
1464 pThis->VDIAsyncIOCallbacks.pfnGetSize = drvvdAsyncIOGetSize;
1465 pThis->VDIAsyncIOCallbacks.pfnSetSize = drvvdAsyncIOSetSize;
1466 pThis->VDIAsyncIOCallbacks.pfnReadSync = drvvdAsyncIOReadSync;
1467 pThis->VDIAsyncIOCallbacks.pfnWriteSync = drvvdAsyncIOWriteSync;
1468 pThis->VDIAsyncIOCallbacks.pfnFlushSync = drvvdAsyncIOFlushSync;
1469 pThis->VDIAsyncIOCallbacks.pfnReadAsync = drvvdAsyncIOReadAsync;
1470 pThis->VDIAsyncIOCallbacks.pfnWriteAsync = drvvdAsyncIOWriteAsync;
1471 pThis->VDIAsyncIOCallbacks.pfnFlushAsync = drvvdAsyncIOFlushAsync;
1472
1473 rc = VDInterfaceAdd(&pThis->VDIAsyncIO, "DrvVD_AsyncIO", VDINTERFACETYPE_ASYNCIO,
1474 &pThis->VDIAsyncIOCallbacks, pThis, &pThis->pVDIfsDisk);
1475#else /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
1476 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
1477 RT_SRC_POS, N_("DrvVD: Configuration error: Async Completion Framework not compiled in"));
1478#endif /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
1479 }
1480
1481 if (RT_SUCCESS(rc) && pThis->fMergePending)
1482 {
1483 rc = RTSemFastMutexCreate(&pThis->MergeCompleteMutex);
1484 if (RT_SUCCESS(rc))
1485 rc = RTSemRWCreate(&pThis->MergeLock);
1486 if (RT_SUCCESS(rc))
1487 {
1488 pThis->VDIThreadSyncCallbacks.cbSize = sizeof(VDINTERFACETHREADSYNC);
1489 pThis->VDIThreadSyncCallbacks.enmInterface = VDINTERFACETYPE_THREADSYNC;
1490 pThis->VDIThreadSyncCallbacks.pfnStartRead = drvvdThreadStartRead;
1491 pThis->VDIThreadSyncCallbacks.pfnFinishRead = drvvdThreadFinishRead;
1492 pThis->VDIThreadSyncCallbacks.pfnStartWrite = drvvdThreadStartWrite;
1493 pThis->VDIThreadSyncCallbacks.pfnFinishWrite = drvvdThreadFinishWrite;
1494
1495 rc = VDInterfaceAdd(&pThis->VDIThreadSync, "DrvVD_ThreadSync", VDINTERFACETYPE_THREADSYNC,
1496 &pThis->VDIThreadSyncCallbacks, pThis, &pThis->pVDIfsDisk);
1497 }
1498 else
1499 {
1500 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1501 N_("DrvVD: Failed to create semaphores for \"MergePending\""));
1502 }
1503 }
1504
1505 if (RT_SUCCESS(rc))
1506 {
1507 rc = VDCreate(pThis->pVDIfsDisk, &pThis->pDisk);
1508 /* Error message is already set correctly. */
1509 }
1510 }
1511
1512 if (pThis->pDrvMediaAsyncPort && fUseNewIo)
1513 pThis->fAsyncIOSupported = true;
1514
1515 unsigned iImageIdx = 0;
1516 while (pCurNode && RT_SUCCESS(rc))
1517 {
1518 /* Allocate per-image data. */
1519 PVBOXIMAGE pImage = drvvdNewImage(pThis);
1520 if (!pImage)
1521 {
1522 rc = VERR_NO_MEMORY;
1523 break;
1524 }
1525
1526 /*
1527 * Read the image configuration.
1528 */
1529 rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
1530 if (RT_FAILURE(rc))
1531 {
1532 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1533 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
1534 break;
1535 }
1536
1537 rc = CFGMR3QueryStringAlloc(pCurNode, "Format", &pszFormat);
1538 if (RT_FAILURE(rc))
1539 {
1540 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1541 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
1542 break;
1543 }
1544
1545 bool fMergeSource;
1546 rc = CFGMR3QueryBoolDef(pCurNode, "MergeSource", &fMergeSource, false);
1547 if (RT_FAILURE(rc))
1548 {
1549 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1550 N_("DrvVD: Configuration error: Querying \"MergeSource\" as boolean failed"));
1551 break;
1552 }
1553 if (fMergeSource)
1554 {
1555 if (pThis->uMergeSource == VD_LAST_IMAGE)
1556 pThis->uMergeSource = iImageIdx;
1557 else
1558 {
1559 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
1560 N_("DrvVD: Configuration error: Multiple \"MergeSource\" occurrences"));
1561 break;
1562 }
1563 }
1564
1565 bool fMergeTarget;
1566 rc = CFGMR3QueryBoolDef(pCurNode, "MergeTarget", &fMergeTarget, false);
1567 if (RT_FAILURE(rc))
1568 {
1569 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
1570 N_("DrvVD: Configuration error: Querying \"MergeTarget\" as boolean failed"));
1571 break;
1572 }
1573 if (fMergeTarget)
1574 {
1575 if (pThis->uMergeTarget == VD_LAST_IMAGE)
1576 pThis->uMergeTarget = iImageIdx;
1577 else
1578 {
1579 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
1580 N_("DrvVD: Configuration error: Multiple \"MergeTarget\" occurrences"));
1581 break;
1582 }
1583 }
1584
1585 PCFGMNODE pCfgVDConfig = CFGMR3GetChild(pCurNode, "VDConfig");
1586 rc = VDInterfaceAdd(&pImage->VDIConfig, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
1587 &pThis->VDIConfigCallbacks, pCfgVDConfig, &pImage->pVDIfsImage);
1588 AssertRC(rc);
1589
1590 /*
1591 * Open the image.
1592 */
1593 unsigned uOpenFlags;
1594 if (fReadOnly || pThis->fTempReadOnly || iLevel != 0)
1595 uOpenFlags = VD_OPEN_FLAGS_READONLY;
1596 else
1597 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
1598 if (fHonorZeroWrites)
1599 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
1600 if (pThis->fAsyncIOSupported)
1601 uOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
1602
1603 /* Try to open backend in async I/O mode first. */
1604 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
1605 if (rc == VERR_NOT_SUPPORTED)
1606 {
1607 pThis->fAsyncIOSupported = false;
1608 uOpenFlags &= ~VD_OPEN_FLAGS_ASYNC_IO;
1609 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
1610 }
1611
1612 if (RT_SUCCESS(rc))
1613 {
1614 Log(("%s: %d - Opened '%s' in %s mode\n", __FUNCTION__,
1615 iLevel, pszName,
1616 VDIsReadOnly(pThis->pDisk) ? "read-only" : "read-write"));
1617 if ( VDIsReadOnly(pThis->pDisk)
1618 && !fReadOnly
1619 && !pThis->fTempReadOnly
1620 && iLevel == 0)
1621 {
1622 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_VD_IMAGE_READ_ONLY, RT_SRC_POS,
1623 N_("Failed to open image '%s' for writing due to wrong permissions"),
1624 pszName);
1625 break;
1626 }
1627 }
1628 else
1629 {
1630 rc = PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1631 N_("Failed to open image '%s' in %s mode rc=%Rrc"), pszName,
1632 (uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "read-only" : "read-write", rc);
1633 break;
1634 }
1635
1636
1637 MMR3HeapFree(pszName);
1638 pszName = NULL;
1639 MMR3HeapFree(pszFormat);
1640 pszFormat = NULL;
1641
1642 /* next */
1643 iLevel--;
1644 iImageIdx++;
1645 pCurNode = CFGMR3GetParent(pCurNode);
1646 }
1647
1648 if ( RT_SUCCESS(rc)
1649 && pThis->fMergePending
1650 && ( pThis->uMergeSource == VD_LAST_IMAGE
1651 || pThis->uMergeTarget == VD_LAST_IMAGE))
1652 {
1653 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
1654 N_("DrvVD: Configuration error: Inconsistent image merge data"));
1655 }
1656
1657 /*
1658 * Register a load-done callback so we can undo TempReadOnly config before
1659 * we get to drvvdResume. Autoamtically deregistered upon destruction.
1660 */
1661 if (RT_SUCCESS(rc))
1662 rc = PDMDrvHlpSSMRegisterEx(pDrvIns, 0 /* version */, 0 /* cbGuess */,
1663 NULL /*pfnLivePrep*/, NULL /*pfnLiveExec*/, NULL /*pfnLiveVote*/,
1664 NULL /*pfnSavePrep*/, NULL /*pfnSaveExec*/, NULL /*pfnSaveDone*/,
1665 NULL /*pfnDonePrep*/, NULL /*pfnLoadExec*/, drvvdLoadDone);
1666
1667
1668 if (RT_FAILURE(rc))
1669 {
1670 if (VALID_PTR(pszName))
1671 MMR3HeapFree(pszName);
1672 if (VALID_PTR(pszFormat))
1673 MMR3HeapFree(pszFormat);
1674 /* drvvdDestruct does the rest. */
1675 }
1676
1677 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
1678 return rc;
1679}
1680
1681/**
1682 * VBox disk container media driver registration record.
1683 */
1684const PDMDRVREG g_DrvVD =
1685{
1686 /* u32Version */
1687 PDM_DRVREG_VERSION,
1688 /* szName */
1689 "VD",
1690 /* szRCMod */
1691 "",
1692 /* szR0Mod */
1693 "",
1694 /* pszDescription */
1695 "Generic VBox disk media driver.",
1696 /* fFlags */
1697 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1698 /* fClass. */
1699 PDM_DRVREG_CLASS_MEDIA,
1700 /* cMaxInstances */
1701 ~0,
1702 /* cbInstance */
1703 sizeof(VBOXDISK),
1704 /* pfnConstruct */
1705 drvvdConstruct,
1706 /* pfnDestruct */
1707 drvvdDestruct,
1708 /* pfnRelocate */
1709 NULL,
1710 /* pfnIOCtl */
1711 NULL,
1712 /* pfnPowerOn */
1713 drvvdPowerOn,
1714 /* pfnReset */
1715 NULL,
1716 /* pfnSuspend */
1717 drvvdSuspend,
1718 /* pfnResume */
1719 drvvdResume,
1720 /* pfnAttach */
1721 NULL,
1722 /* pfnDetach */
1723 NULL,
1724 /* pfnPowerOff */
1725 drvvdPowerOff,
1726 /* pfnSoftReset */
1727 NULL,
1728 /* u32EndVersion */
1729 PDM_DRVREG_VERSION
1730};
1731
Note: See TracBrowser for help on using the repository browser.

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