VirtualBox

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

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

VBoxHDD: First part of the async I/O support

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