VirtualBox

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

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

VBoxHDD: async I/O updates. Mostly complete except for bug fixes

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