VirtualBox

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

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

The Big Sun Rebranding Header Change

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