VirtualBox

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

Last change on this file since 2981 was 2981, checked in by vboxsync, 18 years ago

InnoTek -> innotek: all the headers and comments.

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