VirtualBox

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

Last change on this file since 7653 was 7277, checked in by vboxsync, 17 years ago

Make the backend type a per-image property and get away from the per container property. Required e.g. for snapshotting iSCSI disks (whenever we get there).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.6 KB
Line 
1/** $Id: DrvVD.cpp 7277 2008-03-04 14:12:17Z vboxsync $ */
2/** @file
3 *
4 * VBox storage devices:
5 * Media implementation for VBox disk container
6 */
7
8/*
9 * Copyright (C) 2006-2008 innotek GmbH
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20
21/*******************************************************************************
22* Header files *
23*******************************************************************************/
24#define LOG_GROUP LOG_GROUP_DRV_VD
25#include <VBox/VBoxHDD-new.h>
26#include <VBox/pdmdrv.h>
27#include <iprt/alloc.h>
28#include <iprt/assert.h>
29#include <iprt/uuid.h>
30#include <iprt/file.h>
31#include <iprt/string.h>
32
33#include "Builtins.h"
34
35
36/*******************************************************************************
37* Defined types, constants and macros *
38*******************************************************************************/
39
40/** Converts a pointer to VDIDISK::IMedia to a PVBOXDISK. */
41#define PDMIMEDIA_2_VBOXDISK(pInterface) \
42 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMedia)) )
43
44/** Converts a pointer to PDMDRVINS::IBase to a PPDMDRVINS. */
45#define PDMIBASE_2_DRVINS(pInterface) \
46 ( (PPDMDRVINS)((uintptr_t)pInterface - RT_OFFSETOF(PDMDRVINS, IBase)) )
47
48/** Converts a pointer to PDMDRVINS::IBase to a PVBOXDISK. */
49#define PDMIBASE_2_VBOXDISK(pInterface) \
50 ( PDMINS2DATA(PDMIBASE_2_DRVINS(pInterface), PVBOXDISK) )
51
52
53/**
54 * VBox disk container media main structure, private part.
55 */
56typedef struct VBOXDISK
57{
58 /** The VBox disk container. */
59 PVBOXHDD pDisk;
60 /** The media interface. */
61 PDMIMEDIA IMedia;
62 /** Pointer to the driver instance. */
63 PPDMDRVINS pDrvIns;
64 /** Flag whether suspend has changed image open mode to read only. */
65 bool fTempReadOnly;
66} VBOXDISK, *PVBOXDISK;
67
68/*******************************************************************************
69* Error reporting callback *
70*******************************************************************************/
71
72static void drvvdErrorCallback(void *pvUser, int rc, RT_SRC_POS_DECL,
73 const char *pszFormat, va_list va)
74{
75 PPDMDRVINS pDrvIns = (PPDMDRVINS)pvUser;
76 pDrvIns->pDrvHlp->pfnVMSetErrorV(pDrvIns, rc, RT_SRC_POS_ARGS, pszFormat, va);
77}
78
79/*******************************************************************************
80* Media interface methods *
81*******************************************************************************/
82
83/** @copydoc PDMIMEDIA::pfnRead */
84static DECLCALLBACK(int) drvvdRead(PPDMIMEDIA pInterface,
85 uint64_t off, void *pvBuf, size_t cbRead)
86{
87 LogFlow(("%s: off=%#llx pvBuf=%p cbRead=%d\n", __FUNCTION__,
88 off, pvBuf, cbRead));
89 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
90 int rc = VDRead(pData->pDisk, off, pvBuf, cbRead);
91 if (VBOX_SUCCESS(rc))
92 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d %.*Vhxd\n", __FUNCTION__,
93 off, pvBuf, cbRead, cbRead, pvBuf));
94 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
95 return rc;
96}
97
98/** @copydoc PDMIMEDIA::pfnWrite */
99static DECLCALLBACK(int) drvvdWrite(PPDMIMEDIA pInterface,
100 uint64_t off, const void *pvBuf,
101 size_t cbWrite)
102{
103 LogFlow(("%s: off=%#llx pvBuf=%p cbWrite=%d\n", __FUNCTION__,
104 off, pvBuf, cbWrite));
105 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
106 Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d %.*Vhxd\n", __FUNCTION__,
107 off, pvBuf, cbWrite, cbWrite, pvBuf));
108 int rc = VDWrite(pData->pDisk, off, pvBuf, cbWrite);
109 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
110 return rc;
111}
112
113/** @copydoc PDMIMEDIA::pfnFlush */
114static DECLCALLBACK(int) drvvdFlush(PPDMIMEDIA pInterface)
115{
116 LogFlow(("%s:\n", __FUNCTION__));
117 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
118 int rc = VDFlush(pData->pDisk);
119 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
120 return rc;
121}
122
123/** @copydoc PDMIMEDIA::pfnGetSize */
124static DECLCALLBACK(uint64_t) drvvdGetSize(PPDMIMEDIA pInterface)
125{
126 LogFlow(("%s:\n", __FUNCTION__));
127 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
128 uint64_t cb = VDGetSize(pData->pDisk, VD_LAST_IMAGE);
129 LogFlow(("%s: returns %#llx (%llu)\n", __FUNCTION__, cb, cb));
130 return cb;
131}
132
133/** @copydoc PDMIMEDIA::pfnIsReadOnly */
134static DECLCALLBACK(bool) drvvdIsReadOnly(PPDMIMEDIA pInterface)
135{
136 LogFlow(("%s:\n", __FUNCTION__));
137 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
138 bool f = VDIsReadOnly(pData->pDisk);
139 LogFlow(("%s: returns %d\n", __FUNCTION__, f));
140 return f;
141}
142
143/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
144static DECLCALLBACK(int) drvvdBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
145 PPDMMEDIAGEOMETRY pPCHSGeometry)
146{
147 LogFlow(("%s:\n", __FUNCTION__));
148 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
149 int rc = VDGetPCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
150 if (VBOX_FAILURE(rc))
151 {
152 Log(("%s: geometry not available.\n", __FUNCTION__));
153 rc = VERR_PDM_GEOMETRY_NOT_SET;
154 }
155 LogFlow(("%s: returns %Vrc (CHS=%d/%d/%d)\n", __FUNCTION__,
156 rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
157 return rc;
158}
159
160/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
161static DECLCALLBACK(int) drvvdBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
162 PCPDMMEDIAGEOMETRY pPCHSGeometry)
163{
164 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
165 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
166 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
167 int rc = VDSetPCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
168 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
169 return rc;
170}
171
172/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
173static DECLCALLBACK(int) drvvdBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
174 PPDMMEDIAGEOMETRY pLCHSGeometry)
175{
176 LogFlow(("%s:\n", __FUNCTION__));
177 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
178 int rc = VDGetLCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
179 if (VBOX_FAILURE(rc))
180 {
181 Log(("%s: geometry not available.\n", __FUNCTION__));
182 rc = VERR_PDM_GEOMETRY_NOT_SET;
183 }
184 LogFlow(("%s: returns %Vrc (CHS=%d/%d/%d)\n", __FUNCTION__,
185 rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
186 return rc;
187}
188
189/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
190static DECLCALLBACK(int) drvvdBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
191 PCPDMMEDIAGEOMETRY pLCHSGeometry)
192{
193 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
194 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
195 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
196 int rc = VDSetLCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
197 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
198 return rc;
199}
200
201/** @copydoc PDMIMEDIA::pfnGetUuid */
202static DECLCALLBACK(int) drvvdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
203{
204 LogFlow(("%s:\n", __FUNCTION__));
205 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
206 int rc = VDGetUuid(pData->pDisk, 0, pUuid);
207 LogFlow(("%s: returns %Vrc ({%Vuuid})\n", __FUNCTION__, rc, pUuid));
208 return rc;
209}
210
211
212/*******************************************************************************
213* Base interface methods *
214*******************************************************************************/
215
216/** @copydoc PDMIBASE::pfnQueryInterface */
217static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface,
218 PDMINTERFACE enmInterface)
219{
220 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
221 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
222 switch (enmInterface)
223 {
224 case PDMINTERFACE_BASE:
225 return &pDrvIns->IBase;
226 case PDMINTERFACE_MEDIA:
227 return &pData->IMedia;
228 default:
229 return NULL;
230 }
231}
232
233
234/*******************************************************************************
235* Driver methods *
236*******************************************************************************/
237
238
239/**
240 * Construct a VBox disk media driver instance.
241 *
242 * @returns VBox status.
243 * @param pDrvIns The driver instance data.
244 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
245 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
246 * of the driver instance. It's also found in pDrvIns->pCfgHandle as it's expected
247 * to be used frequently in this function.
248 */
249static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
250 PCFGMNODE pCfgHandle)
251{
252 LogFlow(("%s:\n", __FUNCTION__));
253 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
254 int rc = VINF_SUCCESS;
255 char *pszName = NULL; /**< The path of the disk image file. */
256 char *pszFormat = NULL; /**< The format backed to use for this image. */
257 bool fReadOnly; /**< True if the media is readonly. */
258 bool fHonorZeroWrites; /**< True if zero blocks should be written. */
259
260 /*
261 * Init the static parts.
262 */
263 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
264 pData->pDrvIns = pDrvIns;
265 pData->fTempReadOnly = false;
266 pData->pDisk = NULL;
267
268 /* IMedia */
269 pData->IMedia.pfnRead = drvvdRead;
270 pData->IMedia.pfnWrite = drvvdWrite;
271 pData->IMedia.pfnFlush = drvvdFlush;
272 pData->IMedia.pfnGetSize = drvvdGetSize;
273 pData->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
274 pData->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
275 pData->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
276 pData->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
277 pData->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
278 pData->IMedia.pfnGetUuid = drvvdGetUuid;
279
280 /*
281 * Validate configuration and find all parent images.
282 * It's sort of up side down from the image dependency tree.
283 */
284 unsigned iLevel = 0;
285 PCFGMNODE pCurNode = pCfgHandle;
286 for (;;)
287 {
288 bool fValid;
289
290 if (pCurNode == pCfgHandle)
291 {
292 /* Toplevel configuration additionally contains the global image
293 * open flags. Some might be converted to per-image flags later. */
294 fValid = CFGMR3AreValuesValid(pCurNode,
295 "Format\0Path\0"
296 "ReadOnly\0HonorZeroWrites\0");
297 }
298 else
299 {
300 /* All other image configurations only contain image name and
301 * the format information. */
302 fValid = CFGMR3AreValuesValid(pCurNode, "Format\0Path\0");
303 }
304 if (!fValid)
305 {
306 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
307 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
308 break;
309 }
310
311 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
312 if (!pParent)
313 break;
314 pCurNode = pParent;
315 iLevel++;
316 }
317
318 /*
319 * Open the images.
320 */
321 if (VBOX_SUCCESS(rc))
322 {
323 rc = VDCreate(drvvdErrorCallback, pDrvIns, &pData->pDisk);
324 /* Error message is already set correctly. */
325 }
326
327 while (pCurNode && VBOX_SUCCESS(rc))
328 {
329 /*
330 * Read the image configuration.
331 */
332 rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
333 if (VBOX_FAILURE(rc))
334 {
335 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
336 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
337 break;
338 }
339
340 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Format", &pszFormat);
341 if (VBOX_FAILURE(rc))
342 {
343 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
344 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
345 break;
346 }
347
348 if (iLevel == 0)
349 {
350 rc = CFGMR3QueryBool(pCurNode, "ReadOnly", &fReadOnly);
351 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
352 fReadOnly = false;
353 else if (VBOX_FAILURE(rc))
354 {
355 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
356 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
357 break;
358 }
359
360 rc = CFGMR3QueryBool(pCfgHandle, "HonorZeroWrites", &fHonorZeroWrites);
361 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
362 fHonorZeroWrites = false;
363 else if (VBOX_FAILURE(rc))
364 {
365 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
366 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
367 break;
368 }
369 }
370 else
371 {
372 fReadOnly = true;
373 fHonorZeroWrites = false;
374 }
375
376 /*
377 * Open the image.
378 */
379 unsigned uOpenFlags;
380 if (fReadOnly)
381 uOpenFlags = VD_OPEN_FLAGS_READONLY;
382 else
383 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
384 if (fHonorZeroWrites)
385 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
386 rc = VDOpen(pData->pDisk, pszFormat, pszName, uOpenFlags);
387 if (VBOX_SUCCESS(rc))
388 Log(("%s: %d - Opened '%s' in %s mode\n", __FUNCTION__,
389 iLevel, pszName,
390 VDIsReadOnly(pData->pDisk) ? "read-only" : "read-write"));
391 else
392 {
393 AssertMsgFailed(("Failed to open image '%s' rc=%Vrc\n", pszName, rc));
394 break;
395 }
396 MMR3HeapFree(pszName);
397 pszName = NULL;
398 MMR3HeapFree(pszFormat);
399 pszFormat = NULL;
400
401 /* next */
402 iLevel--;
403 pCurNode = CFGMR3GetParent(pCurNode);
404 }
405
406 if (VBOX_FAILURE(rc))
407 {
408 if (VALID_PTR(pData->pDisk))
409 {
410 VDDestroy(pData->pDisk);
411 pData->pDisk = NULL;
412 }
413 if (VALID_PTR(pszName))
414 MMR3HeapFree(pszName);
415 if (VALID_PTR(pszFormat))
416 MMR3HeapFree(pszFormat);
417 }
418
419 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
420 return rc;
421}
422
423/**
424 * Destruct a driver instance.
425 *
426 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
427 * resources can be freed correctly.
428 *
429 * @param pDrvIns The driver instance data.
430 */
431static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
432{
433 LogFlow(("%s:\n", __FUNCTION__));
434 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
435 int rc = VDCloseAll(pData->pDisk);
436 AssertRC(rc);
437}
438
439
440/**
441 * When the VM has been suspended we'll change the image mode to read-only
442 * so that main and others can read the VDIs. This is important when
443 * saving state and so forth.
444 *
445 * @param pDrvIns The driver instance data.
446 */
447static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
448{
449 LogFlow(("%s:\n", __FUNCTION__));
450 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
451 if (!VDIsReadOnly(pData->pDisk))
452 {
453 unsigned uOpenFlags;
454 int rc = VDGetOpenFlags(pData->pDisk, VD_LAST_IMAGE, &uOpenFlags);
455 AssertRC(rc);
456 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
457 rc = VDSetOpenFlags(pData->pDisk, VD_LAST_IMAGE, uOpenFlags);
458 AssertRC(rc);
459 pData->fTempReadOnly = true;
460 }
461}
462
463/**
464 * Before the VM resumes we'll have to undo the read-only mode change
465 * done in drvvdSuspend.
466 *
467 * @param pDrvIns The driver instance data.
468 */
469static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
470{
471 LogFlow(("%s:\n", __FUNCTION__));
472 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
473 if (pData->fTempReadOnly)
474 {
475 unsigned uOpenFlags;
476 int rc = VDGetOpenFlags(pData->pDisk, VD_LAST_IMAGE, &uOpenFlags);
477 AssertRC(rc);
478 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
479 rc = VDSetOpenFlags(pData->pDisk, VD_LAST_IMAGE, uOpenFlags);
480 AssertRC(rc);
481 pData->fTempReadOnly = false;
482 }
483}
484
485
486
487/**
488 * VBox disk container media driver registration record.
489 */
490const PDMDRVREG g_DrvVD =
491{
492 /* u32Version */
493 PDM_DRVREG_VERSION,
494 /* szDriverName */
495 "VD",
496 /* pszDescription */
497 "Generic VBox disk media driver.",
498 /* fFlags */
499 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
500 /* fClass. */
501 PDM_DRVREG_CLASS_MEDIA,
502 /* cMaxInstances */
503 ~0,
504 /* cbInstance */
505 sizeof(VBOXDISK),
506 /* pfnConstruct */
507 drvvdConstruct,
508 /* pfnDestruct */
509 drvvdDestruct,
510 /* pfnIOCtl */
511 NULL,
512 /* pfnPowerOn */
513 NULL,
514 /* pfnReset */
515 NULL,
516 /* pfnSuspend */
517 drvvdSuspend,
518 /* pfnResume */
519 drvvdResume,
520 /* pfnDetach */
521 NULL
522};
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