VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvHostRawDisk.cpp@ 5419

Last change on this file since 5419 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: 15.1 KB
Line 
1/** @file
2 *
3 * VBox storage devices:
4 * Host Hard Disk media driver
5 */
6
7/*
8 * Copyright (C) 2006-2007 innotek 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
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#define LOG_GROUP LOG_GROUP_DRV_HOST_HDD
24#include <VBox/pdmdrv.h>
25#include <iprt/assert.h>
26#include <iprt/file.h>
27#include <iprt/string.h>
28
29#ifdef RT_OS_WINDOWS
30#include <windows.h>
31#include <winioctl.h>
32#elif RT_OS_LINUX
33#include <errno.h>
34#include <sys/ioctl.h>
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <unistd.h>
38#include <linux/hdreg.h>
39#include <linux/fs.h>
40#endif /* !RT_OS_WINDOWS && !RT_OS_LINUX */
41
42#include "Builtins.h"
43
44
45typedef struct DRVHOSTHDD *PDRVHOSTHDD;
46
47typedef struct DRVHOSTHDD
48{
49 /** File name for the host hard disk. */
50 char *pszPath;
51 /** Size of the disk. */
52 uint64_t cbSize;
53 /** Disk geometry information, cylinders. */
54 uint32_t cCylinders;
55 /** Disk geometry information, heads. */
56 uint32_t cHeads;
57 /** Disk geometry information, sectors. */
58 uint32_t cSectors;
59 /** Translation mode. */
60 PDMBIOSTRANSLATION enmTranslation;
61 /** Flag if this drive should refuse write operations. */
62 bool fReadOnly;
63 /** File handle for the host hard disk. */
64 RTFILE HostDiskFile;
65
66 /** The media interface. */
67 PDMIMEDIA IMedia;
68 /** Pointer to the driver instance. */
69 PPDMDRVINS pDrvIns;
70} DRVHOSTHDD;
71
72
73/** Converts a pointer to DRVHOSTHDD::IMedia to a PDRVHOSTHDD. */
74#define PDMIMEDIA_2_DRVHOSTHDD(pInterface) ( (PDRVHOSTHDD)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTHDD, IMedia)) )
75
76
77/**
78 * Queries an interface to the driver.
79 *
80 * @returns Pointer to interface.
81 * @returns NULL if the interface was not supported by the driver.
82 * @param pInterface Pointer to this interface structure.
83 * @param enmInterface The requested interface identification.
84 */
85static DECLCALLBACK(void *) drvHostHDDQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
86{
87 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
88 PDRVHOSTHDD pThis = PDMINS2DATA(pDrvIns, PDRVHOSTHDD);
89 switch (enmInterface)
90 {
91 case PDMINTERFACE_BASE:
92 return &pDrvIns->IBase;
93 case PDMINTERFACE_MEDIA:
94 return &pThis->IMedia;
95 default:
96 return NULL;
97 }
98}
99
100
101/** @copydoc PDMIMEDIA::pfnRead */
102static DECLCALLBACK(int) drvHostHDDRead(PPDMIMEDIA pInterface, uint64_t off, void *pvBuf, size_t cbRead)
103{
104 int rc = VINF_SUCCESS;
105 uint64_t uLBA;
106 uint32_t cSectors;
107 PDRVHOSTHDD pThis = PDMIMEDIA_2_DRVHOSTHDD(pInterface);
108
109 LogFlow(("%s: off=%lld pvBuf=%p cbRead=%lld\n", __FUNCTION__, off, pvBuf, cbRead));
110 Assert(off % 512 == 0);
111 Assert(cbRead % 512 == 0);
112 uLBA = off / 512;
113 cSectors = cbRead / 512;
114 /** @todo add partition filtering */
115 rc = RTFileReadAt(pThis->HostDiskFile, off, pvBuf, cbRead, NULL);
116 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
117 return rc;
118}
119
120
121/** @copydoc PDMIMEDIA::pfnWrite */
122static DECLCALLBACK(int) drvHostHDDWrite(PPDMIMEDIA pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
123{
124 int rc = VINF_SUCCESS;
125 uint64_t uLBA;
126 uint32_t cSectors;
127 PDRVHOSTHDD pThis = PDMIMEDIA_2_DRVHOSTHDD(pInterface);
128
129 LogFlow(("%s: off=%lld pvBuf=%p cbWrite=%lld\n", __FUNCTION__, off, pvBuf, cbWrite));
130 Assert(off % 512 == 0);
131 Assert(cbWrite % 512 == 0);
132 uLBA = off / 512;
133 cSectors = cbWrite / 512;
134 /** @todo add partition filtering */
135 rc = RTFileWriteAt(pThis->HostDiskFile, off, pvBuf, cbWrite, NULL);
136 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
137 return rc;
138}
139
140
141/** @copydoc PDMIMEDIA::pfnFlush */
142static DECLCALLBACK(int) drvHostHDDFlush(PPDMIMEDIA pInterface)
143{
144 int rc = VINF_SUCCESS;
145 PDRVHOSTHDD pThis = PDMIMEDIA_2_DRVHOSTHDD(pInterface);
146 LogFlow(("%s:\n", __FUNCTION__));
147 rc = RTFileFlush(pThis->HostDiskFile);
148 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
149 return rc;
150}
151
152
153/** @copydoc PDMIMEDIA::pfnGetSize */
154static DECLCALLBACK(uint64_t) drvHostHDDGetSize(PPDMIMEDIA pInterface)
155{
156 PDRVHOSTHDD pThis = PDMIMEDIA_2_DRVHOSTHDD(pInterface);
157 uint64_t cbSize;
158 LogFlow(("%s:\n", __FUNCTION__));
159 cbSize = pThis->cbSize;
160 LogFlow(("%s: returns %lld\n", __FUNCTION__, cbSize));
161 return cbSize;
162}
163
164
165/** @copydoc PDMIMEDIA::pfnIsReadOnly */
166static DECLCALLBACK(bool) drvHostHDDIsReadOnly(PPDMIMEDIA pInterface)
167{
168 PDRVHOSTHDD pThis = PDMIMEDIA_2_DRVHOSTHDD(pInterface);
169 bool fReadOnly;
170 LogFlow(("%s:\n", __FUNCTION__));
171 fReadOnly = pThis->fReadOnly;
172 LogFlow(("%s: returns %d\n", __FUNCTION__, fReadOnly));
173 return fReadOnly;
174}
175
176
177/** @copydoc PDMIMEDIA::pfnBiosGetGeometry */
178static DECLCALLBACK(int) drvHostHDDBiosGetGeometry(PPDMIMEDIA pInterface, uint32_t *pcCylinders, uint32_t *pcHeads, uint32_t *pcSectors)
179{
180 int rc = VINF_SUCCESS;
181 PDRVHOSTHDD pThis = PDMIMEDIA_2_DRVHOSTHDD(pInterface);
182
183 LogFlow(("%s:\n", __FUNCTION__));
184 if (pThis->cCylinders != 0)
185 {
186 *pcCylinders = pThis->cCylinders;
187 *pcHeads = pThis->cHeads;
188 *pcSectors = pThis->cSectors;
189 }
190 else
191 rc = VERR_PDM_GEOMETRY_NOT_SET;
192 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
193 return rc;
194}
195
196
197/** @copydoc PDMIMEDIA::pfnBiosSetGeometry */
198static DECLCALLBACK(int) drvHostHDDBiosSetGeometry(PPDMIMEDIA pInterface, uint32_t cCylinders, uint32_t cHeads, uint32_t cSectors)
199{
200 int rc = VINF_SUCCESS;
201 PDRVHOSTHDD pThis = PDMIMEDIA_2_DRVHOSTHDD(pInterface);
202
203 LogFlow(("%s:\n", __FUNCTION__));
204 pThis->cCylinders = cCylinders;
205 pThis->cHeads = cHeads;
206 pThis->cSectors = cSectors;
207 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
208 return rc;
209}
210
211
212/** @copydoc PDMIMEDIA::pfnBiosGetTranslation */
213static DECLCALLBACK(int) drvHostHDDBiosGetTranslation(PPDMIMEDIA pInterface, PPDMBIOSTRANSLATION penmTranslation)
214{
215 int rc = VINF_SUCCESS;
216 PDRVHOSTHDD pThis = PDMIMEDIA_2_DRVHOSTHDD(pInterface);
217
218 LogFlow(("%s:\n", __FUNCTION__));
219 *penmTranslation = pThis->enmTranslation;
220 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
221 return rc;
222}
223
224
225/** @copydoc PDMIMEDIA::pfnBiosSetTranslation */
226static DECLCALLBACK(int) drvHostHDDBiosSetTranslation(PPDMIMEDIA pInterface, PDMBIOSTRANSLATION enmTranslation)
227{
228 int rc = VINF_SUCCESS;
229 PDRVHOSTHDD pThis = PDMIMEDIA_2_DRVHOSTHDD(pInterface);
230
231 LogFlow(("%s:\n", __FUNCTION__));
232 pThis->enmTranslation = enmTranslation;
233 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
234 return rc;
235}
236
237
238/** @copydoc PDMIMEDIA::pfnGetUUID */
239static DECLCALLBACK(int) drvHostHDDGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
240{
241 int rc = VINF_SUCCESS;
242// PDRVHOSTHDD pThis = PDMIMEDIA_2_DRVHOSTHDD(pInterface);
243
244 LogFlow(("%s:\n", __FUNCTION__));
245 memset(pUuid, '\0', sizeof(*pUuid));
246 rc = VERR_NOT_IMPLEMENTED;
247 LogFlow(("%s: returns %Vrc ({%Vuuid})\n", rc, pUuid));
248 return rc;
249}
250
251
252
253/* -=-=-=-=- driver interface -=-=-=-=- */
254
255
256/**
257 * Construct a host hard disk (raw partition) media driver instance.
258 *
259 * @returns VBox status.
260 * @param pDrvIns The driver instance data.
261 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
262 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
263 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
264 * iInstance it's expected to be used a bit in this function.
265 */
266static DECLCALLBACK(int) drvHostHDDConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
267{
268 int rc = VINF_SUCCESS;
269 PDRVHOSTHDD pThis = PDMINS2DATA(pDrvIns, PDRVHOSTHDD);
270 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
271
272 /*
273 * Validate configuration.
274 */
275 if (!CFGMR3AreValuesValid(pCfgHandle, "Path\0ReadOnly\0"))
276 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS, N_("RawHDD#%d: configuration keys other than \"Path\" and \"ReadOnly\" present"));
277
278 /*
279 * Init instance data.
280 */
281 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Path", &pThis->pszPath);
282 if (VBOX_FAILURE(rc))
283 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("RawHDD#%d: configuration query for \"Path\" string returned %Vra"), rc);
284
285 rc = CFGMR3QueryBool(pCfgHandle, "ReadOnly", &pThis->fReadOnly);
286 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
287 {
288 pThis->fReadOnly = false;
289 rc = VINF_SUCCESS;
290 }
291 else if (VBOX_FAILURE(rc))
292 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("RawHDD#%d: configuration query for \"ReadOnly\" boolean returned %Vra"), rc);
293
294 pDrvIns->IBase.pfnQueryInterface = drvHostHDDQueryInterface;
295 pThis->IMedia.pfnRead = drvHostHDDRead;
296 pThis->IMedia.pfnWrite = drvHostHDDWrite;
297 pThis->IMedia.pfnFlush = drvHostHDDFlush;
298 pThis->IMedia.pfnGetSize = drvHostHDDGetSize;
299 pThis->IMedia.pfnIsReadOnly = drvHostHDDIsReadOnly;
300 pThis->IMedia.pfnBiosGetGeometry = drvHostHDDBiosGetGeometry;
301 pThis->IMedia.pfnBiosSetGeometry = drvHostHDDBiosSetGeometry;
302 pThis->IMedia.pfnBiosGetTranslation = drvHostHDDBiosGetTranslation;
303 pThis->IMedia.pfnBiosSetTranslation = drvHostHDDBiosSetTranslation;
304 pThis->IMedia.pfnGetUuid = drvHostHDDGetUuid;
305
306 pThis->cbSize = 0;
307
308 if (pThis->fReadOnly)
309 rc = RTFileOpen(&pThis->HostDiskFile, pThis->pszPath, RTFILE_O_READ);
310 else
311 rc = RTFileOpen(&pThis->HostDiskFile, pThis->pszPath, RTFILE_O_READWRITE);
312 if (VBOX_SUCCESS(rc))
313 {
314 /* Get disk size (in case it's an image file). */
315 rc = RTFileGetSize(pThis->HostDiskFile, &pThis->cbSize);
316 if (VBOX_FAILURE(rc))
317 pThis->cbSize = 0;
318 }
319 else
320 {
321 /* Failed to open the raw disk. Specify a proper error message. */
322 rc = PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("RawHDD#%d: cannot open file \"%s\", error code %Vrc"), pDrvIns->iInstance, pThis->pszPath, rc);
323 }
324
325 if (VBOX_SUCCESS(rc))
326 {
327 pThis->cCylinders = 0;
328 pThis->cHeads = 0;
329 pThis->cSectors = 0;
330 pThis->enmTranslation = PDMBIOSTRANSLATION_AUTO;
331#ifdef RT_OS_WINDOWS
332 DISK_GEOMETRY DriveGeo;
333 DWORD cbDriveGeo;
334 if (DeviceIoControl((HANDLE)pThis->HostDiskFile,
335 IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
336 &DriveGeo, sizeof(DriveGeo), &cbDriveGeo, NULL))
337 {
338 if (DriveGeo.MediaType == FixedMedia)
339 {
340 pThis->cCylinders = DriveGeo.Cylinders.QuadPart;
341 pThis->cHeads = DriveGeo.TracksPerCylinder;
342 pThis->cSectors = DriveGeo.SectorsPerTrack;
343 if (!pThis->cbSize)
344 {
345 /* Windows NT has no IOCTL_DISK_GET_LENGTH_INFORMATION
346 * ioctl. This was added to Windows XP, so use the
347 * available info from DriveGeo. */
348 pThis->cbSize = DriveGeo.Cylinders.QuadPart
349 * DriveGeo.TracksPerCylinder
350 * DriveGeo.SectorsPerTrack
351 * DriveGeo.BytesPerSector;
352 }
353 }
354 else
355 rc = VERR_MEDIA_NOT_RECOGNIZED;
356 }
357 else
358 rc = RTErrConvertFromWin32(GetLastError());
359#elif defined(RT_OS_LINUX)
360 struct stat DevStat;
361 if (!fstat(pThis->HostDiskFile, &DevStat) && S_ISBLK(DevStat.st_mode))
362 {
363 struct hd_geometry DriveGeo;
364 if (!ioctl(pThis->HostDiskFile, HDIO_GETGEO, &DriveGeo))
365 {
366 pThis->cCylinders = DriveGeo.cylinders;
367 pThis->cHeads = DriveGeo.heads;
368 pThis->cSectors = DriveGeo.sectors;
369 if (!pThis->cbSize)
370 {
371 long cBlocks;
372 if (!ioctl(pThis->HostDiskFile, BLKGETSIZE, &cBlocks))
373 pThis->cbSize = (uint64_t)cBlocks * 512;
374 else
375 rc = RTErrConvertFromErrno(errno);
376 }
377 }
378 else
379 rc = RTErrConvertFromErrno(errno);
380 }
381#else
382 /** @todo add further host OS geometry detection mechanisms. */
383 AssertMsgFailed("Host disk support for this host is unimplemented.\n");
384 rc = VERR_NOT_IMPLEMENTED;
385#endif
386 /* Do geometry cleanup common to all host operating systems.
387 * Very important, as Windows guests are very sensitive to odd
388 * PCHS settings, and for big disks they consider anything
389 * except the standard mapping as odd. */
390 if (pThis->cCylinders != 0)
391 {
392 if (pThis->cSectors == 63 && (pThis->cHeads != 16 || pThis->cCylinders >= 1024))
393 {
394 /* For big disks, use dummy PCHS values and let the BIOS
395 * select an appropriate LCHS mapping. */
396 pThis->cCylinders = pThis->cbSize / 512 / 63 / 16;
397 pThis->cHeads = 16;
398 pThis->cSectors = 63;
399 pThis->enmTranslation = PDMBIOSTRANSLATION_LBA;
400 }
401 else
402 pThis->enmTranslation = PDMBIOSTRANSLATION_NONE;
403 }
404 }
405
406 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
407 return rc;
408}
409
410
411/**
412 * Destruct a driver instance.
413 *
414 * Most VM resources are freed by the VM. This callback is provided so that
415 * any non-VM resources can be freed correctly.
416 *
417 * @param pDrvIns The driver instance data.
418 */
419static DECLCALLBACK(void) drvHostHDDDestruct(PPDMDRVINS pDrvIns)
420{
421 PDRVHOSTHDD pThis = PDMINS2DATA(pDrvIns, PDRVHOSTHDD);
422 LogFlow(("%s: '%s'\n", __FUNCTION__, pThis->pszPath));
423
424 if (pThis->HostDiskFile != NIL_RTFILE)
425 {
426 RTFileClose(pThis->HostDiskFile);
427 pThis->HostDiskFile = NIL_RTFILE;
428 }
429 if (pThis->pszPath)
430 MMR3HeapFree(pThis->pszPath);
431}
432
433
434/**
435 * Block driver registration record.
436 */
437const PDMDRVREG g_DrvHostHDD =
438{
439 /* u32Version */
440 PDM_DRVREG_VERSION,
441 /* szDriverName */
442 "HostHDD",
443 /* pszDescription */
444 "Host Hard Disk Media Driver.",
445 /* fFlags */
446 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
447 /* fClass. */
448 PDM_DRVREG_CLASS_MEDIA,
449 /* cMaxInstances */
450 ~0,
451 /* cbInstance */
452 sizeof(DRVHOSTHDD),
453 /* pfnConstruct */
454 drvHostHDDConstruct,
455 /* pfnDestruct */
456 drvHostHDDDestruct,
457 /* pfnIOCtl */
458 NULL,
459 /* pfnPowerOn */
460 NULL,
461 /* pfnReset */
462 NULL,
463 /* pfnSuspend */
464 NULL,
465 /* pfnResume */
466 NULL,
467 /* pfnDetach */
468 NULL
469};
470
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