VirtualBox

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

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

Use pdmdrv.h and pdmdev.h where appropirate.

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