VirtualBox

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

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

DrvVD: Added a LoadDone hook for implementing better handling of TempReadOnly errors (for teleportation).

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

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