VirtualBox

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

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

The Giant CDDL Dual-License Header Change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.1 KB
Line 
1/** $Id: DrvVD.cpp 5999 2007-12-07 15:05:06Z vboxsync $ */
2/** @file
3 *
4 * VBox storage devices:
5 * Media implementation for VBox disk container
6 */
7
8/*
9 * Copyright (C) 2006-2007 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 /** Name of the image format backend. */
65 char szFormat[16];
66 /** Flag whether suspend has changed image open mode to read only. */
67 bool fTempReadOnly;
68} VBOXDISK, *PVBOXDISK;
69
70/*******************************************************************************
71* Error reporting callback *
72*******************************************************************************/
73
74static void vdErrorCallback(void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va)
75{
76 PPDMDRVINS pDrvIns = (PPDMDRVINS)pvUser;
77 pDrvIns->pDrvHlp->pfnVMSetErrorV(pDrvIns, rc, RT_SRC_POS_ARGS, pszFormat, va);
78}
79
80/*******************************************************************************
81* Media interface methods *
82*******************************************************************************/
83
84/** @copydoc PDMIMEDIA::pfnRead */
85static DECLCALLBACK(int) vdRead(PPDMIMEDIA pInterface,
86 uint64_t off, void *pvBuf, size_t cbRead)
87{
88 LogFlow(("%s: off=%#llx pvBuf=%p cbRead=%d\n", __FUNCTION__,
89 off, pvBuf, cbRead));
90 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
91 int rc = VDRead(pData->pDisk, off, pvBuf, cbRead);
92 if (VBOX_SUCCESS(rc))
93 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d %.*Vhxd\n", __FUNCTION__,
94 off, pvBuf, cbRead, cbRead, pvBuf));
95 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
96 return rc;
97}
98
99/** @copydoc PDMIMEDIA::pfnWrite */
100static DECLCALLBACK(int) vdWrite(PPDMIMEDIA pInterface,
101 uint64_t off, const void *pvBuf,
102 size_t cbWrite)
103{
104 LogFlow(("%s: off=%#llx pvBuf=%p cbWrite=%d\n", __FUNCTION__,
105 off, pvBuf, cbWrite));
106 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
107 Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d %.*Vhxd\n", __FUNCTION__,
108 off, pvBuf, cbWrite, cbWrite, pvBuf));
109 int rc = VDWrite(pData->pDisk, off, pvBuf, cbWrite);
110 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
111 return rc;
112}
113
114/** @copydoc PDMIMEDIA::pfnFlush */
115static DECLCALLBACK(int) vdFlush(PPDMIMEDIA pInterface)
116{
117 LogFlow(("%s:\n", __FUNCTION__));
118 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
119 int rc = VDFlush(pData->pDisk);
120 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
121 return rc;
122}
123
124/** @copydoc PDMIMEDIA::pfnGetSize */
125static DECLCALLBACK(uint64_t) vdGetSize(PPDMIMEDIA pInterface)
126{
127 LogFlow(("%s:\n", __FUNCTION__));
128 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
129 uint64_t cb = VDGetSize(pData->pDisk);
130 LogFlow(("%s: returns %#llx (%llu)\n", __FUNCTION__, cb, cb));
131 return cb;
132}
133
134/** @copydoc PDMIMEDIA::pfnIsReadOnly */
135static DECLCALLBACK(bool) vdIsReadOnly(PPDMIMEDIA pInterface)
136{
137 LogFlow(("%s:\n", __FUNCTION__));
138 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
139 bool f = VDIsReadOnly(pData->pDisk);
140 LogFlow(("%s: returns %d\n", __FUNCTION__, f));
141 return f;
142}
143
144/** @copydoc PDMIMEDIA::pfnBiosGetGeometry */
145static DECLCALLBACK(int) vdBiosGetGeometry(PPDMIMEDIA pInterface,
146 uint32_t *pcCylinders,
147 uint32_t *pcHeads,
148 uint32_t *pcSectors)
149{
150 LogFlow(("%s:\n", __FUNCTION__));
151 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
152 int rc = VDGetGeometry(pData->pDisk, pcCylinders, pcHeads, pcSectors);
153 if (VBOX_FAILURE(rc))
154 {
155 Log(("%s: geometry not available.\n", __FUNCTION__));
156 rc = VERR_PDM_GEOMETRY_NOT_SET;
157 }
158 LogFlow(("%s: returns %Vrc (CHS=%d/%d/%d)\n", __FUNCTION__,
159 rc, *pcCylinders, *pcHeads, *pcSectors));
160 return rc;
161}
162
163/** @copydoc PDMIMEDIA::pfnBiosSetGeometry */
164static DECLCALLBACK(int) vdBiosSetGeometry(PPDMIMEDIA pInterface,
165 uint32_t cCylinders,
166 uint32_t cHeads,
167 uint32_t cSectors)
168{
169 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
170 cCylinders, cHeads, cSectors));
171 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
172 int rc = VDSetGeometry(pData->pDisk, cCylinders, cHeads, cSectors);
173 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
174 return rc;
175}
176
177/** @copydoc PDMIMEDIA::pfnBiosGetTranslation */
178static DECLCALLBACK(int) vdBiosGetTranslation(PPDMIMEDIA pInterface,
179 PPDMBIOSTRANSLATION penmTranslation)
180{
181 LogFlow(("%s:\n", __FUNCTION__));
182 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
183 int rc = VDGetTranslation(pData->pDisk, penmTranslation);
184 LogFlow(("%s: returns %Vrc (%d)\n", __FUNCTION__, rc, *penmTranslation));
185 return rc;
186}
187
188/** @copydoc PDMIMEDIA::pfnBiosSetTranslation */
189static DECLCALLBACK(int) vdBiosSetTranslation(PPDMIMEDIA pInterface,
190 PDMBIOSTRANSLATION enmTranslation)
191{
192 LogFlow(("%s:\n", __FUNCTION__));
193 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
194 int rc = VDSetTranslation(pData->pDisk, enmTranslation);
195 LogFlow(("%s: returns %Vrc (%d)\n", __FUNCTION__, rc, enmTranslation));
196 return rc;
197}
198
199/** @copydoc PDMIMEDIA::pfnGetUuid */
200static DECLCALLBACK(int) vdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
201{
202 LogFlow(("%s:\n", __FUNCTION__));
203 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
204 int rc = VDGetUuid(pData->pDisk, 0, pUuid);
205 LogFlow(("%s: returns %Vrc ({%Vuuid})\n", __FUNCTION__, rc, pUuid));
206 return rc;
207}
208
209
210/*******************************************************************************
211* Base interface methods *
212*******************************************************************************/
213
214/** @copydoc PDMIBASE::pfnQueryInterface */
215static DECLCALLBACK(void *) vdQueryInterface(PPDMIBASE pInterface,
216 PDMINTERFACE enmInterface)
217{
218 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
219 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
220 switch (enmInterface)
221 {
222 case PDMINTERFACE_BASE:
223 return &pDrvIns->IBase;
224 case PDMINTERFACE_MEDIA:
225 return &pData->IMedia;
226 default:
227 return NULL;
228 }
229}
230
231
232/*******************************************************************************
233* Driver methods *
234*******************************************************************************/
235
236
237/**
238 * Construct a VBox disk media driver instance.
239 *
240 * @returns VBox status.
241 * @param pDrvIns The driver instance data.
242 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
243 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
244 * of the driver instance. It's also found in pDrvIns->pCfgHandle as it's expected
245 * to be used frequently in this function.
246 */
247static DECLCALLBACK(int) vdConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
248{
249 LogFlow(("%s:\n", __FUNCTION__));
250 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
251 int rc = VINF_SUCCESS;
252 char *pszName; /**< The path of the disk image file. */
253 bool fReadOnly; /**< True if the media is readonly. */
254 bool fHonorZeroWrites; /**< True if zero blocks should be written. */
255
256 /*
257 * Init the static parts.
258 */
259 pDrvIns->IBase.pfnQueryInterface = vdQueryInterface;
260 pData->pDrvIns = pDrvIns;
261 pData->fTempReadOnly = false;
262
263 /* IMedia */
264 pData->IMedia.pfnRead = vdRead;
265 pData->IMedia.pfnWrite = vdWrite;
266 pData->IMedia.pfnFlush = vdFlush;
267 pData->IMedia.pfnGetSize = vdGetSize;
268 pData->IMedia.pfnIsReadOnly = vdIsReadOnly;
269 pData->IMedia.pfnBiosGetGeometry = vdBiosGetGeometry;
270 pData->IMedia.pfnBiosSetGeometry = vdBiosSetGeometry;
271 pData->IMedia.pfnBiosGetTranslation = vdBiosGetTranslation;
272 pData->IMedia.pfnBiosSetTranslation = vdBiosSetTranslation;
273 pData->IMedia.pfnGetUuid = vdGetUuid;
274
275 /*
276 * Validate configuration and find all parent images.
277 * It's sort of up side down from the image dependency tree.
278 */
279 unsigned iLevel = 0;
280 PCFGMNODE pCurNode = pCfgHandle;
281 for (;;)
282 {
283 bool fValid;
284
285 if (pCurNode == pCfgHandle)
286 {
287 /* Toplevel configuration contains the format backend name and
288 * full image open information. */
289 fValid = CFGMR3AreValuesValid(pCurNode,
290 "Format\0"
291 "Path\0ReadOnly\0HonorZeroWrites\0");
292 }
293 else
294 {
295 /* All other image configurations only contain image name. */
296 fValid = CFGMR3AreValuesValid(pCurNode, "Path\0");
297 }
298 if (!fValid)
299 {
300 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
301 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
302 break;
303 }
304
305 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
306 if (!pParent)
307 break;
308 pCurNode = pParent;
309 iLevel++;
310 }
311
312 /*
313 * Open the images.
314 */
315 if (VBOX_SUCCESS(rc))
316 {
317 rc = CFGMR3QueryString(pCfgHandle, "Format", &pData->szFormat[0],
318 sizeof(pData->szFormat));
319 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
320 {
321 /* Default disk image format is VMDK. */
322 rc = VINF_SUCCESS;
323 strncpy(&pData->szFormat[0], "VMDK", sizeof(pData->szFormat));
324 pData->szFormat[sizeof(pData->szFormat) - 1] = '\0';
325 }
326 if (VBOX_SUCCESS(rc))
327 {
328 rc = VDCreate(pData->szFormat, vdErrorCallback, pDrvIns, &pData->pDisk);
329 /* Error message is already set correctly. */
330 }
331 else
332 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
333 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
334 }
335
336 while (pCurNode && VBOX_SUCCESS(rc))
337 {
338 /*
339 * Read the image configuration.
340 */
341 rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
342 if (VBOX_FAILURE(rc))
343 {
344 VDDestroy(pData->pDisk);
345 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
346 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
347 break;
348 }
349
350 if (iLevel == 0)
351 {
352 rc = CFGMR3QueryBool(pCurNode, "ReadOnly", &fReadOnly);
353 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
354 fReadOnly = false;
355 else if (VBOX_FAILURE(rc))
356 {
357 MMR3HeapFree(pszName);
358 VDDestroy(pData->pDisk);
359 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
360 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
361 break;
362 }
363
364 rc = CFGMR3QueryBool(pCfgHandle, "HonorZeroWrites", &fHonorZeroWrites);
365 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
366 fHonorZeroWrites = false;
367 else if (VBOX_FAILURE(rc))
368 {
369 MMR3HeapFree(pszName);
370 VDDestroy(pData->pDisk);
371 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
372 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
373 break;
374 }
375 }
376 else
377 {
378 fReadOnly = true;
379 fHonorZeroWrites = false;
380 }
381
382 /*
383 * Open the image.
384 */
385 unsigned uOpenFlags;
386 if (fReadOnly)
387 uOpenFlags = VD_OPEN_FLAGS_READONLY;
388 else
389 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
390 if (fHonorZeroWrites)
391 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
392 rc = VDOpen(pData->pDisk, pszName, uOpenFlags);
393 if (VBOX_SUCCESS(rc))
394 Log(("%s: %d - Opened '%s' in %s mode\n", __FUNCTION__,
395 iLevel, pszName,
396 VDIsReadOnly(pData->pDisk) ? "read-only" : "read-write"));
397 else
398 {
399 AssertMsgFailed(("Failed to open image '%s' rc=%Vrc\n", pszName, rc));
400 VDDestroy(pData->pDisk);
401 break;
402 }
403 MMR3HeapFree(pszName);
404
405 /* next */
406 iLevel--;
407 pCurNode = CFGMR3GetParent(pCurNode);
408 }
409
410 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
411 return rc;
412}
413
414/**
415 * Destruct a driver instance.
416 *
417 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
418 * resources can be freed correctly.
419 *
420 * @param pDrvIns The driver instance data.
421 */
422static DECLCALLBACK(void) vdDestruct(PPDMDRVINS pDrvIns)
423{
424 LogFlow(("%s:\n", __FUNCTION__));
425 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
426 int rc = VDCloseAll(pData->pDisk);
427 AssertRC(rc);
428}
429
430
431/**
432 * When the VM has been suspended we'll change the image mode to read-only
433 * so that main and others can read the VDIs. This is important when
434 * saving state and so forth.
435 *
436 * @param pDrvIns The driver instance data.
437 */
438static DECLCALLBACK(void) vdSuspend(PPDMDRVINS pDrvIns)
439{
440 LogFlow(("%s:\n", __FUNCTION__));
441 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
442 if (!VDIsReadOnly(pData->pDisk))
443 {
444 unsigned uOpenFlags;
445 int rc = VDGetOpenFlags(pData->pDisk, &uOpenFlags);
446 AssertRC(rc);
447 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
448 rc = VDSetOpenFlags(pData->pDisk, uOpenFlags);
449 AssertRC(rc);
450 pData->fTempReadOnly = true;
451 }
452}
453
454/**
455 * Before the VM resumes we'll have to undo the read-only mode change
456 * done in vdSuspend.
457 *
458 * @param pDrvIns The driver instance data.
459 */
460static DECLCALLBACK(void) vdResume(PPDMDRVINS pDrvIns)
461{
462 LogFlow(("%s:\n", __FUNCTION__));
463 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
464 if (pData->fTempReadOnly)
465 {
466 unsigned uOpenFlags;
467 int rc = VDGetOpenFlags(pData->pDisk, &uOpenFlags);
468 AssertRC(rc);
469 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
470 rc = VDSetOpenFlags(pData->pDisk, uOpenFlags);
471 AssertRC(rc);
472 pData->fTempReadOnly = false;
473 }
474}
475
476
477
478/**
479 * VBox disk container media driver registration record.
480 */
481const PDMDRVREG g_DrvVD =
482{
483 /* u32Version */
484 PDM_DRVREG_VERSION,
485 /* szDriverName */
486 "VD",
487 /* pszDescription */
488 "Generic VBox disk media driver.",
489 /* fFlags */
490 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
491 /* fClass. */
492 PDM_DRVREG_CLASS_MEDIA,
493 /* cMaxInstances */
494 ~0,
495 /* cbInstance */
496 sizeof(VBOXDISK),
497 /* pfnConstruct */
498 vdConstruct,
499 /* pfnDestruct */
500 vdDestruct,
501 /* pfnIOCtl */
502 NULL,
503 /* pfnPowerOn */
504 NULL,
505 /* pfnReset */
506 NULL,
507 /* pfnSuspend */
508 vdSuspend,
509 /* pfnResume */
510 vdResume,
511 /* pfnDetach */
512 NULL
513};
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