VirtualBox

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

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

Storage/VBoxHDD+DrvVD: implement framework for providing thread synchronization. Additionally some cleanup to resolve a few minor long-standing todos.

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