VirtualBox

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

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

New VMDK code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.7 KB
Line 
1/** $Id: VBoxHDD.cpp 2358 2007-04-26 16:53:00Z vboxsync $ */
2/** @file
3 *
4 * VBox storage devices:
5 * VBox HDD container implementation
6 */
7
8/*
9 * Copyright (C) 2006 InnoTek Systemberatung 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* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_DRV_VBOXHDD
28#include <VBox/VBoxHDD.h>
29#include <VBox/pdm.h>
30#include <VBox/mm.h>
31#include <VBox/err.h>
32
33#include <VBox/log.h>
34#include <iprt/alloc.h>
35#include <iprt/assert.h>
36#include <iprt/uuid.h>
37#include <iprt/file.h>
38#include <iprt/string.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(pCurNode, "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