VirtualBox

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

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

Biggest check-in ever. New source code headers for all (C) innotek files.

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