VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VBoxHDD.cpp@ 1565

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

Split out the bulk of the VDI code from VBoxDD and put it in VBoxDDU (devices and drivers utilities).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.7 KB
Line 
1/** @file
2 *
3 * VBox storage devices:
4 * VBox HDD container implementation
5 */
6
7/*
8 * Copyright (C) 2006 InnoTek Systemberatung GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
21 */
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DRV_VBOXHDD
27#include <VBox/VBoxHDD.h>
28#include <VBox/pdm.h>
29#include <VBox/mm.h>
30#include <VBox/err.h>
31
32#include <VBox/log.h>
33#include <iprt/alloc.h>
34#include <iprt/assert.h>
35#include <iprt/uuid.h>
36#include <iprt/file.h>
37#include <iprt/string.h>
38#include <iprt/asm.h>
39
40#include "VDICore.h"
41#include "Builtins.h"
42
43
44/*******************************************************************************
45* Defined Constants And Macros *
46*******************************************************************************/
47/** Converts a pointer to VDIDISK::IMedia to a PVDIDISK. */
48#define PDMIMEDIA_2_VDIDISK(pInterface) ( (PVDIDISK)((uintptr_t)pInterface - RT_OFFSETOF(VDIDISK, IMedia)) )
49
50/** Converts a pointer to PDMDRVINS::IBase to a PPDMDRVINS. */
51#define PDMIBASE_2_DRVINS(pInterface) ( (PPDMDRVINS)((uintptr_t)pInterface - RT_OFFSETOF(PDMDRVINS, IBase)) )
52
53/** Converts a pointer to PDMDRVINS::IBase to a PVDIDISK. */
54#define PDMIBASE_2_VDIDISK(pInterface) ( PDMINS2DATA(PDMIBASE_2_DRVINS(pInterface), PVDIDISK) )
55
56
57
58
59/** @copydoc PDMIMEDIA::pfnGetSize */
60static DECLCALLBACK(uint64_t) vdiGetSize(PPDMIMEDIA pInterface)
61{
62 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
63 uint64_t cb = VDIDiskGetSize(pData);
64 LogFlow(("vdiGetSize: returns %#llx (%llu)\n", cb, cb));
65 return cb;
66}
67
68
69/**
70 * Get stored media geometry - BIOS property.
71 *
72 * @see PDMIMEDIA::pfnBiosGetGeometry for details.
73 */
74static DECLCALLBACK(int) vdiBiosGetGeometry(PPDMIMEDIA pInterface, uint32_t *pcCylinders, uint32_t *pcHeads, uint32_t *pcSectors)
75{
76 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
77 int rc = VDIDiskGetGeometry(pData, pcCylinders, pcHeads, pcSectors);
78 if (VBOX_SUCCESS(rc))
79 {
80 LogFlow(("vdiBiosGetGeometry: returns VINF_SUCCESS\n"));
81 return VINF_SUCCESS;
82 }
83 Log(("vdiBiosGetGeometry: The Bios geometry data was not available.\n"));
84 return VERR_PDM_GEOMETRY_NOT_SET;
85}
86
87
88/**
89 * Set stored media geometry - BIOS property.
90 *
91 * @see PDMIMEDIA::pfnBiosSetGeometry for details.
92 */
93static DECLCALLBACK(int) vdiBiosSetGeometry(PPDMIMEDIA pInterface, uint32_t cCylinders, uint32_t cHeads, uint32_t cSectors)
94{
95 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
96 int rc = VDIDiskSetGeometry(pData, cCylinders, cHeads, cSectors);
97 LogFlow(("vdiBiosSetGeometry: returns %Vrc (%d,%d,%d)\n", rc, cCylinders, cHeads, cSectors));
98 return rc;
99}
100
101
102/**
103 * Read bits.
104 *
105 * @see PDMIMEDIA::pfnRead for details.
106 */
107static DECLCALLBACK(int) vdiRead(PPDMIMEDIA pInterface, uint64_t off, void *pvBuf, size_t cbRead)
108{
109 LogFlow(("vdiRead: off=%#llx pvBuf=%p cbRead=%d\n", off, pvBuf, cbRead));
110 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
111 int rc = VDIDiskRead(pData, off, pvBuf, cbRead);
112 if (VBOX_SUCCESS(rc))
113 Log2(("vdiRead: off=%#llx pvBuf=%p cbRead=%d\n"
114 "%.*Vhxd\n",
115 off, pvBuf, cbRead, cbRead, pvBuf));
116 LogFlow(("vdiRead: returns %Vrc\n", rc));
117 return rc;
118}
119
120
121/**
122 * Write bits.
123 *
124 * @see PDMIMEDIA::pfnWrite for details.
125 */
126static DECLCALLBACK(int) vdiWrite(PPDMIMEDIA pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
127{
128 LogFlow(("vdiWrite: off=%#llx pvBuf=%p cbWrite=%d\n", off, pvBuf, cbWrite));
129 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
130 Log2(("vdiWrite: off=%#llx pvBuf=%p cbWrite=%d\n"
131 "%.*Vhxd\n",
132 off, pvBuf, cbWrite, cbWrite, pvBuf));
133 int rc = VDIDiskWrite(pData, off, pvBuf, cbWrite);
134 LogFlow(("vdiWrite: returns %Vrc\n", rc));
135 return rc;
136}
137
138
139/**
140 * Flush bits to media.
141 *
142 * @see PDMIMEDIA::pfnFlush for details.
143 */
144static DECLCALLBACK(int) vdiFlush(PPDMIMEDIA pInterface)
145{
146 LogFlow(("vdiFlush:\n"));
147 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
148 vdiFlushImage(pData->pLast);
149 int rc = VINF_SUCCESS;
150 LogFlow(("vdiFlush: returns %Vrc\n", rc));
151 return rc;
152}
153
154
155/** @copydoc PDMIMEDIA::pfnGetUuid */
156static DECLCALLBACK(int) vdiGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
157{
158 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
159 int rc = VDIDiskGetImageUuid(pData, 0, pUuid);
160 LogFlow(("vdiGetUuid: returns %Vrc ({%Vuuid})\n", rc, pUuid));
161 return rc;
162}
163
164
165/** @copydoc PDMIMEDIA::pfnIsReadOnly */
166static DECLCALLBACK(bool) vdiIsReadOnly(PPDMIMEDIA pInterface)
167{
168 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
169 LogFlow(("vdiIsReadOnly: returns %d\n", VDIDiskIsReadOnly(pData)));
170 return VDIDiskIsReadOnly(pData);
171}
172
173
174/** @copydoc PDMIMEDIA::pfnBiosGetTranslation */
175static DECLCALLBACK(int) vdiBiosGetTranslation(PPDMIMEDIA pInterface,
176 PPDMBIOSTRANSLATION penmTranslation)
177{
178 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
179 int rc = VDIDiskGetTranslation(pData, penmTranslation);
180 LogFlow(("vdiBiosGetTranslation: returns %Vrc (%d)\n", rc, *penmTranslation));
181 return rc;
182}
183
184
185/** @copydoc PDMIMEDIA::pfnBiosSetTranslation */
186static DECLCALLBACK(int) vdiBiosSetTranslation(PPDMIMEDIA pInterface,
187 PDMBIOSTRANSLATION enmTranslation)
188{
189 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
190 int rc = VDIDiskSetTranslation(pData, enmTranslation);
191 LogFlow(("vdiBiosSetTranslation: returns %Vrc (%d)\n", rc, enmTranslation));
192 return rc;
193}
194
195
196/**
197 * Queries an interface to the driver.
198 *
199 * @returns Pointer to interface.
200 * @returns NULL if the interface was not supported by the driver.
201 * @param pInterface Pointer to this interface structure.
202 * @param enmInterface The requested interface identification.
203 * @thread Any thread.
204 */
205static DECLCALLBACK(void *) vdiQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
206{
207 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
208 PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
209 switch (enmInterface)
210 {
211 case PDMINTERFACE_BASE:
212 return &pDrvIns->IBase;
213 case PDMINTERFACE_MEDIA:
214 return &pData->IMedia;
215 default:
216 return NULL;
217 }
218}
219
220
221/**
222 * Before the VM resumes we'll have to undo the read-only mode change
223 * done in vdiSuspend.
224 *
225 * @param pDrvIns The driver instance data.
226 */
227static DECLCALLBACK(void) vdiResume(PPDMDRVINS pDrvIns)
228{
229 LogFlow(("vdiSuspend:\n"));
230#if 1 //#ifdef DEBUG_dmik
231 PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
232 if (!(pData->pLast->fOpen & VDI_OPEN_FLAGS_READONLY))
233 {
234 int rc = vdiChangeImageMode(pData->pLast, false);
235 AssertRC(rc);
236 }
237#endif
238}
239
240
241/**
242 * When the VM has been suspended we'll change the image mode to read-only
243 * so that main and others can read the VDIs. This is important when
244 * saving state and so forth.
245 *
246 * @param pDrvIns The driver instance data.
247 */
248static DECLCALLBACK(void) vdiSuspend(PPDMDRVINS pDrvIns)
249{
250 LogFlow(("vdiSuspend:\n"));
251#if 1 // #ifdef DEBUG_dmik
252 PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
253 if (!(pData->pLast->fOpen & VDI_OPEN_FLAGS_READONLY))
254 {
255 int rc = vdiChangeImageMode(pData->pLast, true);
256 AssertRC(rc);
257 }
258#endif
259}
260
261
262/**
263 * Destruct a driver instance.
264 *
265 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
266 * resources can be freed correctly.
267 *
268 * @param pDrvIns The driver instance data.
269 */
270static DECLCALLBACK(void) vdiDestruct(PPDMDRVINS pDrvIns)
271{
272 LogFlow(("vdiDestruct:\n"));
273 PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
274 VDIDiskCloseAllImages(pData);
275}
276
277
278/**
279 * Construct a VBox HDD media driver instance.
280 *
281 * @returns VBox status.
282 * @param pDrvIns The driver instance data.
283 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
284 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
285 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
286 * iInstance it's expected to be used a bit in this function.
287 */
288static DECLCALLBACK(int) vdiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
289{
290 LogFlow(("vdiConstruct:\n"));
291 PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
292 char *pszName; /**< The path of the disk image file. */
293 bool fReadOnly; /**< True if the media is readonly. */
294 bool fHonorZeroWrites = false;
295
296 /*
297 * Init the static parts.
298 */
299 pDrvIns->IBase.pfnQueryInterface = vdiQueryInterface;
300 pData->pDrvIns = pDrvIns;
301
302 vdiInitVDIDisk(pData);
303
304 /* IMedia */
305 pData->IMedia.pfnRead = vdiRead;
306 pData->IMedia.pfnWrite = vdiWrite;
307 pData->IMedia.pfnFlush = vdiFlush;
308 pData->IMedia.pfnGetSize = vdiGetSize;
309 pData->IMedia.pfnGetUuid = vdiGetUuid;
310 pData->IMedia.pfnIsReadOnly = vdiIsReadOnly;
311 pData->IMedia.pfnBiosGetGeometry = vdiBiosGetGeometry;
312 pData->IMedia.pfnBiosSetGeometry = vdiBiosSetGeometry;
313 pData->IMedia.pfnBiosGetTranslation = vdiBiosGetTranslation;
314 pData->IMedia.pfnBiosSetTranslation = vdiBiosSetTranslation;
315
316 /*
317 * Validate configuration and find the great to the level of umpteen grandparent.
318 * The parents are found in the 'Parent' subtree, so it's sorta up side down
319 * from the image dependency tree.
320 */
321 unsigned iLevel = 0;
322 PCFGMNODE pCurNode = pCfgHandle;
323 for (;;)
324 {
325 if (!CFGMR3AreValuesValid(pCfgHandle, "Path\0ReadOnly\0HonorZeroWrites\0"))
326 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
327
328 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
329 if (!pParent)
330 break;
331 pCurNode = pParent;
332 iLevel++;
333 }
334
335 /*
336 * Open the images.
337 */
338 int rc = VINF_SUCCESS;
339 while (pCurNode && VBOX_SUCCESS(rc))
340 {
341 /*
342 * Read the image configuration.
343 */
344 int rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
345 if (VBOX_FAILURE(rc))
346 return PDMDRV_SET_ERROR(pDrvIns, rc,
347 N_("VHDD: Configuration error: Querying \"Path\" as string failed"));
348
349 rc = CFGMR3QueryBool(pCfgHandle, "ReadOnly", &fReadOnly);
350 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
351 fReadOnly = false;
352 else if (VBOX_FAILURE(rc))
353 {
354 MMR3HeapFree(pszName);
355 return PDMDRV_SET_ERROR(pDrvIns, rc,
356 N_("VHDD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
357 }
358
359 if (!fHonorZeroWrites)
360 {
361 rc = CFGMR3QueryBool(pCfgHandle, "HonorZeroWrites", &fHonorZeroWrites);
362 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
363 fHonorZeroWrites = false;
364 else if (VBOX_FAILURE(rc))
365 {
366 MMR3HeapFree(pszName);
367 return PDMDRV_SET_ERROR(pDrvIns, rc,
368 N_("VHDD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
369 }
370 }
371
372 /*
373 * Open the image.
374 */
375 rc = VDIDiskOpenImage(pData, pszName, fReadOnly ? VDI_OPEN_FLAGS_READONLY
376 : VDI_OPEN_FLAGS_NORMAL);
377 if (VBOX_SUCCESS(rc))
378 Log(("vdiConstruct: %d - Opened '%s' in %s mode\n",
379 iLevel, pszName, VDIDiskIsReadOnly(pData) ? "read-only" : "read-write"));
380 else
381 AssertMsgFailed(("Failed to open image '%s' rc=%Vrc\n", pszName, rc));
382 MMR3HeapFree(pszName);
383
384 /* next */
385 iLevel--;
386 pCurNode = CFGMR3GetParent(pCurNode);
387 }
388
389 /* If any of the images has the flag set, handle zero writes like normal. */
390 if (VBOX_SUCCESS(rc))
391 pData->fHonorZeroWrites = fHonorZeroWrites;
392
393 /* On failure, vdiDestruct will be called, so no need to clean up here. */
394
395 if (rc == VERR_ACCESS_DENIED)
396 /* This should never happen here since this case is covered by Console::PowerUp */
397 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
398 N_("Cannot open virtual disk image '%s' for %s access"),
399 pszName, fReadOnly ? "readonly" : "read/write");
400
401 return rc;
402}
403
404
405/**
406 * VBox HDD driver registration record.
407 */
408const PDMDRVREG g_DrvVBoxHDD =
409{
410 /* u32Version */
411 PDM_DRVREG_VERSION,
412 /* szDriverName */
413 "VBoxHDD",
414 /* pszDescription */
415 "VBoxHDD media driver.",
416 /* fFlags */
417 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
418 /* fClass. */
419 PDM_DRVREG_CLASS_MEDIA,
420 /* cMaxInstances */
421 ~0,
422 /* cbInstance */
423 sizeof(VDIDISK),
424 /* pfnConstruct */
425 vdiConstruct,
426 /* pfnDestruct */
427 vdiDestruct,
428 /* pfnIOCtl */
429 NULL,
430 /* pfnPowerOn */
431 NULL,
432 /* pfnReset */
433 NULL,
434 /* pfnSuspend */
435 vdiSuspend,
436 /* pfnResume */
437 vdiResume,
438 /* pfnDetach */
439 NULL
440};
441
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