VirtualBox

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

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

Fixed small problem with iSCSI translation detection (returns unknown
geometry). Also fixed small problem with LBA selection for disks between
512MB and 8GB (not triggered for VDIs).

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