VirtualBox

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

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

Big virtual disk changeset containing several modifications

  • remove the always buggy translation setting and replace it with two sets of geometries, physical and logical
  • complete vmdk creation (fixed/dynamic variants, both split in 2G chunks and single file)
  • implemented VBoxHDD-new generic snapshot support, i.e. diff image creation and image merging (completely untested, I'm pretty sure there are bugs)
  • assorted changes which generalize the VBoxHDD-new interfaces (both externally and internally)
  • 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 6291 2008-01-09 10:57:05Z 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 /** 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 drvvdErrorCallback(void *pvUser, int rc, RT_SRC_POS_DECL,
75 const char *pszFormat, va_list va)
76{
77 PPDMDRVINS pDrvIns = (PPDMDRVINS)pvUser;
78 pDrvIns->pDrvHlp->pfnVMSetErrorV(pDrvIns, rc, RT_SRC_POS_ARGS, pszFormat, va);
79}
80
81/*******************************************************************************
82* Media interface methods *
83*******************************************************************************/
84
85/** @copydoc PDMIMEDIA::pfnRead */
86static DECLCALLBACK(int) drvvdRead(PPDMIMEDIA pInterface,
87 uint64_t off, void *pvBuf, size_t cbRead)
88{
89 LogFlow(("%s: off=%#llx pvBuf=%p cbRead=%d\n", __FUNCTION__,
90 off, pvBuf, cbRead));
91 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
92 int rc = VDRead(pData->pDisk, off, pvBuf, cbRead);
93 if (VBOX_SUCCESS(rc))
94 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d %.*Vhxd\n", __FUNCTION__,
95 off, pvBuf, cbRead, cbRead, pvBuf));
96 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
97 return rc;
98}
99
100/** @copydoc PDMIMEDIA::pfnWrite */
101static DECLCALLBACK(int) drvvdWrite(PPDMIMEDIA pInterface,
102 uint64_t off, const void *pvBuf,
103 size_t cbWrite)
104{
105 LogFlow(("%s: off=%#llx pvBuf=%p cbWrite=%d\n", __FUNCTION__,
106 off, pvBuf, cbWrite));
107 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
108 Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d %.*Vhxd\n", __FUNCTION__,
109 off, pvBuf, cbWrite, cbWrite, pvBuf));
110 int rc = VDWrite(pData->pDisk, off, pvBuf, cbWrite);
111 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
112 return rc;
113}
114
115/** @copydoc PDMIMEDIA::pfnFlush */
116static DECLCALLBACK(int) drvvdFlush(PPDMIMEDIA pInterface)
117{
118 LogFlow(("%s:\n", __FUNCTION__));
119 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
120 int rc = VDFlush(pData->pDisk);
121 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
122 return rc;
123}
124
125/** @copydoc PDMIMEDIA::pfnGetSize */
126static DECLCALLBACK(uint64_t) drvvdGetSize(PPDMIMEDIA pInterface)
127{
128 LogFlow(("%s:\n", __FUNCTION__));
129 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
130 uint64_t cb = VDGetSize(pData->pDisk, VD_LAST_IMAGE);
131 LogFlow(("%s: returns %#llx (%llu)\n", __FUNCTION__, cb, cb));
132 return cb;
133}
134
135/** @copydoc PDMIMEDIA::pfnIsReadOnly */
136static DECLCALLBACK(bool) drvvdIsReadOnly(PPDMIMEDIA pInterface)
137{
138 LogFlow(("%s:\n", __FUNCTION__));
139 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
140 bool f = VDIsReadOnly(pData->pDisk);
141 LogFlow(("%s: returns %d\n", __FUNCTION__, f));
142 return f;
143}
144
145/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
146static DECLCALLBACK(int) drvvdBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
147 PPDMMEDIAGEOMETRY pPCHSGeometry)
148{
149 LogFlow(("%s:\n", __FUNCTION__));
150 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
151 int rc = VDGetPCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
152 if (VBOX_FAILURE(rc))
153 {
154 Log(("%s: geometry not available.\n", __FUNCTION__));
155 rc = VERR_PDM_GEOMETRY_NOT_SET;
156 }
157 LogFlow(("%s: returns %Vrc (CHS=%d/%d/%d)\n", __FUNCTION__,
158 rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
159 return rc;
160}
161
162/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
163static DECLCALLBACK(int) drvvdBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
164 PCPDMMEDIAGEOMETRY pPCHSGeometry)
165{
166 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
167 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
168 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
169 int rc = VDSetPCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
170 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
171 return rc;
172}
173
174/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
175static DECLCALLBACK(int) drvvdBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
176 PPDMMEDIAGEOMETRY pLCHSGeometry)
177{
178 LogFlow(("%s:\n", __FUNCTION__));
179 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
180 int rc = VDGetLCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
181 if (VBOX_FAILURE(rc))
182 {
183 Log(("%s: geometry not available.\n", __FUNCTION__));
184 rc = VERR_PDM_GEOMETRY_NOT_SET;
185 }
186 LogFlow(("%s: returns %Vrc (CHS=%d/%d/%d)\n", __FUNCTION__,
187 rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
188 return rc;
189}
190
191/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
192static DECLCALLBACK(int) drvvdBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
193 PCPDMMEDIAGEOMETRY pLCHSGeometry)
194{
195 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
196 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
197 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
198 int rc = VDSetLCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
199 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
200 return rc;
201}
202
203/** @copydoc PDMIMEDIA::pfnGetUuid */
204static DECLCALLBACK(int) drvvdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
205{
206 LogFlow(("%s:\n", __FUNCTION__));
207 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
208 int rc = VDGetUuid(pData->pDisk, 0, pUuid);
209 LogFlow(("%s: returns %Vrc ({%Vuuid})\n", __FUNCTION__, rc, pUuid));
210 return rc;
211}
212
213
214/*******************************************************************************
215* Base interface methods *
216*******************************************************************************/
217
218/** @copydoc PDMIBASE::pfnQueryInterface */
219static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface,
220 PDMINTERFACE enmInterface)
221{
222 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
223 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
224 switch (enmInterface)
225 {
226 case PDMINTERFACE_BASE:
227 return &pDrvIns->IBase;
228 case PDMINTERFACE_MEDIA:
229 return &pData->IMedia;
230 default:
231 return NULL;
232 }
233}
234
235
236/*******************************************************************************
237* Driver methods *
238*******************************************************************************/
239
240
241/**
242 * Construct a VBox disk media driver instance.
243 *
244 * @returns VBox status.
245 * @param pDrvIns The driver instance data.
246 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
247 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
248 * of the driver instance. It's also found in pDrvIns->pCfgHandle as it's expected
249 * to be used frequently in this function.
250 */
251static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
252 PCFGMNODE pCfgHandle)
253{
254 LogFlow(("%s:\n", __FUNCTION__));
255 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
256 int rc = VINF_SUCCESS;
257 char *pszName; /**< The path of the disk image file. */
258 bool fReadOnly; /**< True if the media is readonly. */
259 bool fHonorZeroWrites; /**< True if zero blocks should be written. */
260
261 /*
262 * Init the static parts.
263 */
264 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
265 pData->pDrvIns = pDrvIns;
266 pData->fTempReadOnly = false;
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 contains the format backend name and
293 * full image open information. */
294 fValid = CFGMR3AreValuesValid(pCurNode,
295 "Format\0"
296 "Path\0ReadOnly\0HonorZeroWrites\0");
297 }
298 else
299 {
300 /* All other image configurations only contain image name. */
301 fValid = CFGMR3AreValuesValid(pCurNode, "Path\0");
302 }
303 if (!fValid)
304 {
305 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
306 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
307 break;
308 }
309
310 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
311 if (!pParent)
312 break;
313 pCurNode = pParent;
314 iLevel++;
315 }
316
317 /*
318 * Open the images.
319 */
320 if (VBOX_SUCCESS(rc))
321 {
322 rc = CFGMR3QueryString(pCfgHandle, "Format", &pData->szFormat[0],
323 sizeof(pData->szFormat));
324 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
325 {
326 /* Default disk image format is VMDK. */
327 rc = VINF_SUCCESS;
328 strncpy(&pData->szFormat[0], "VMDK", sizeof(pData->szFormat));
329 pData->szFormat[sizeof(pData->szFormat) - 1] = '\0';
330 }
331 if (VBOX_SUCCESS(rc))
332 {
333 rc = VDCreate(pData->szFormat, drvvdErrorCallback, pDrvIns, &pData->pDisk);
334 /* Error message is already set correctly. */
335 }
336 else
337 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
338 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
339 }
340
341 while (pCurNode && VBOX_SUCCESS(rc))
342 {
343 /*
344 * Read the image configuration.
345 */
346 rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
347 if (VBOX_FAILURE(rc))
348 {
349 VDDestroy(pData->pDisk);
350 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
351 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
352 break;
353 }
354
355 if (iLevel == 0)
356 {
357 rc = CFGMR3QueryBool(pCurNode, "ReadOnly", &fReadOnly);
358 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
359 fReadOnly = false;
360 else if (VBOX_FAILURE(rc))
361 {
362 MMR3HeapFree(pszName);
363 VDDestroy(pData->pDisk);
364 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
365 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
366 break;
367 }
368
369 rc = CFGMR3QueryBool(pCfgHandle, "HonorZeroWrites", &fHonorZeroWrites);
370 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
371 fHonorZeroWrites = false;
372 else if (VBOX_FAILURE(rc))
373 {
374 MMR3HeapFree(pszName);
375 VDDestroy(pData->pDisk);
376 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
377 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
378 break;
379 }
380 }
381 else
382 {
383 fReadOnly = true;
384 fHonorZeroWrites = false;
385 }
386
387 /*
388 * Open the image.
389 */
390 unsigned uOpenFlags;
391 if (fReadOnly)
392 uOpenFlags = VD_OPEN_FLAGS_READONLY;
393 else
394 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
395 if (fHonorZeroWrites)
396 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
397 rc = VDOpen(pData->pDisk, pszName, uOpenFlags);
398 if (VBOX_SUCCESS(rc))
399 Log(("%s: %d - Opened '%s' in %s mode\n", __FUNCTION__,
400 iLevel, pszName,
401 VDIsReadOnly(pData->pDisk) ? "read-only" : "read-write"));
402 else
403 {
404 AssertMsgFailed(("Failed to open image '%s' rc=%Vrc\n", pszName, rc));
405 VDDestroy(pData->pDisk);
406 break;
407 }
408 MMR3HeapFree(pszName);
409
410 /* next */
411 iLevel--;
412 pCurNode = CFGMR3GetParent(pCurNode);
413 }
414
415 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
416 return rc;
417}
418
419/**
420 * Destruct a driver instance.
421 *
422 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
423 * resources can be freed correctly.
424 *
425 * @param pDrvIns The driver instance data.
426 */
427static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
428{
429 LogFlow(("%s:\n", __FUNCTION__));
430 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
431 int rc = VDCloseAll(pData->pDisk);
432 AssertRC(rc);
433}
434
435
436/**
437 * When the VM has been suspended we'll change the image mode to read-only
438 * so that main and others can read the VDIs. This is important when
439 * saving state and so forth.
440 *
441 * @param pDrvIns The driver instance data.
442 */
443static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
444{
445 LogFlow(("%s:\n", __FUNCTION__));
446 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
447 if (!VDIsReadOnly(pData->pDisk))
448 {
449 unsigned uOpenFlags;
450 int rc = VDGetOpenFlags(pData->pDisk, VD_LAST_IMAGE, &uOpenFlags);
451 AssertRC(rc);
452 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
453 rc = VDSetOpenFlags(pData->pDisk, VD_LAST_IMAGE, uOpenFlags);
454 AssertRC(rc);
455 pData->fTempReadOnly = true;
456 }
457}
458
459/**
460 * Before the VM resumes we'll have to undo the read-only mode change
461 * done in drvvdSuspend.
462 *
463 * @param pDrvIns The driver instance data.
464 */
465static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
466{
467 LogFlow(("%s:\n", __FUNCTION__));
468 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
469 if (pData->fTempReadOnly)
470 {
471 unsigned uOpenFlags;
472 int rc = VDGetOpenFlags(pData->pDisk, VD_LAST_IMAGE, &uOpenFlags);
473 AssertRC(rc);
474 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
475 rc = VDSetOpenFlags(pData->pDisk, VD_LAST_IMAGE, uOpenFlags);
476 AssertRC(rc);
477 pData->fTempReadOnly = false;
478 }
479}
480
481
482
483/**
484 * VBox disk container media driver registration record.
485 */
486const PDMDRVREG g_DrvVD =
487{
488 /* u32Version */
489 PDM_DRVREG_VERSION,
490 /* szDriverName */
491 "DrvVD",
492 /* pszDescription */
493 "Generic VBox disk media driver.",
494 /* fFlags */
495 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
496 /* fClass. */
497 PDM_DRVREG_CLASS_MEDIA,
498 /* cMaxInstances */
499 ~0,
500 /* cbInstance */
501 sizeof(VBOXDISK),
502 /* pfnConstruct */
503 drvvdConstruct,
504 /* pfnDestruct */
505 drvvdDestruct,
506 /* pfnIOCtl */
507 NULL,
508 /* pfnPowerOn */
509 NULL,
510 /* pfnReset */
511 NULL,
512 /* pfnSuspend */
513 drvvdSuspend,
514 /* pfnResume */
515 drvvdResume,
516 /* pfnDetach */
517 NULL
518};
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