VirtualBox

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

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

InnoTek -> innotek: all the headers and comments.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.3 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 * 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 PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS, N_("RawHDD#%d: configuration keys other than \"Path\" and \"ReadOnly\" present"));
287
288 /*
289 * Init instance data.
290 */
291 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Path", &pThis->pszPath);
292 if (VBOX_FAILURE(rc))
293 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("RawHDD#%d: configuration query for \"Path\" string returned %Vra"), rc);
294
295 rc = CFGMR3QueryBool(pCfgHandle, "ReadOnly", &pThis->fReadOnly);
296 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
297 {
298 pThis->fReadOnly = false;
299 rc = VINF_SUCCESS;
300 }
301 else if (VBOX_FAILURE(rc))
302 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("RawHDD#%d: configuration query for \"ReadOnly\" boolean returned %Vra"), rc);
303
304 pDrvIns->IBase.pfnQueryInterface = drvHostHDDQueryInterface;
305 pThis->IMedia.pfnRead = drvHostHDDRead;
306 pThis->IMedia.pfnWrite = drvHostHDDWrite;
307 pThis->IMedia.pfnFlush = drvHostHDDFlush;
308 pThis->IMedia.pfnGetSize = drvHostHDDGetSize;
309 pThis->IMedia.pfnIsReadOnly = drvHostHDDIsReadOnly;
310 pThis->IMedia.pfnBiosGetGeometry = drvHostHDDBiosGetGeometry;
311 pThis->IMedia.pfnBiosSetGeometry = drvHostHDDBiosSetGeometry;
312 pThis->IMedia.pfnBiosGetTranslation = drvHostHDDBiosGetTranslation;
313 pThis->IMedia.pfnBiosSetTranslation = drvHostHDDBiosSetTranslation;
314 pThis->IMedia.pfnGetUuid = drvHostHDDGetUuid;
315
316 pThis->cbSize = 0;
317
318 if (pThis->fReadOnly)
319 rc = RTFileOpen(&pThis->HostDiskFile, pThis->pszPath, RTFILE_O_READ);
320 else
321 rc = RTFileOpen(&pThis->HostDiskFile, pThis->pszPath, RTFILE_O_READWRITE);
322 if (VBOX_SUCCESS(rc))
323 {
324 /* Get disk size (in case it's an image file). */
325 rc = RTFileGetSize(pThis->HostDiskFile, &pThis->cbSize);
326 if (VBOX_FAILURE(rc))
327 pThis->cbSize = 0;
328 }
329 else
330 {
331 /* Failed to open the raw disk. Specify a proper error message. */
332 rc = PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("RawHDD#%d: cannot open file \"%s\", error code %Vrc"), pDrvIns->iInstance, pThis->pszPath, rc);
333 }
334
335 if (VBOX_SUCCESS(rc))
336 {
337 pThis->cCylinders = 0;
338 pThis->cHeads = 0;
339 pThis->cSectors = 0;
340 pThis->enmTranslation = PDMBIOSTRANSLATION_AUTO;
341#ifdef __WIN__
342 DISK_GEOMETRY DriveGeo;
343 DWORD cbDriveGeo;
344 if (DeviceIoControl((HANDLE)pThis->HostDiskFile,
345 IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
346 &DriveGeo, sizeof(DriveGeo), &cbDriveGeo, NULL))
347 {
348 if (DriveGeo.MediaType == FixedMedia)
349 {
350 pThis->cCylinders = DriveGeo.Cylinders.QuadPart;
351 pThis->cHeads = DriveGeo.TracksPerCylinder;
352 pThis->cSectors = DriveGeo.SectorsPerTrack;
353 if (!pThis->cbSize)
354 {
355 /* Windows NT has no IOCTL_DISK_GET_LENGTH_INFORMATION
356 * ioctl. This was added to Windows XP, so use the
357 * available info from DriveGeo. */
358 pThis->cbSize = DriveGeo.Cylinders.QuadPart
359 * DriveGeo.TracksPerCylinder
360 * DriveGeo.SectorsPerTrack
361 * DriveGeo.BytesPerSector;
362 }
363 }
364 else
365 rc = VERR_MEDIA_NOT_RECOGNIZED;
366 }
367 else
368 rc = RTErrConvertFromWin32(GetLastError());
369#elif defined(__LINUX__)
370 struct stat DevStat;
371 if (!fstat(pThis->HostDiskFile, &DevStat) && S_ISBLK(DevStat.st_mode))
372 {
373 struct hd_geometry DriveGeo;
374 if (!ioctl(pThis->HostDiskFile, HDIO_GETGEO, &DriveGeo))
375 {
376 pThis->cCylinders = DriveGeo.cylinders;
377 pThis->cHeads = DriveGeo.heads;
378 pThis->cSectors = DriveGeo.sectors;
379 if (!pThis->cbSize)
380 {
381 long cBlocks;
382 if (!ioctl(pThis->HostDiskFile, BLKGETSIZE, &cBlocks))
383 pThis->cbSize = (uint64_t)cBlocks * 512;
384 else
385 rc = RTErrConvertFromErrno(errno);
386 }
387 }
388 else
389 rc = RTErrConvertFromErrno(errno);
390 }
391#else
392 /** @todo add further host OS geometry detection mechanisms. */
393 AssertMsgFailed("Host disk support for this host is unimplemented.\n");
394 rc = VERR_NOT_IMPLEMENTED;
395#endif
396 /* Do geometry cleanup common to all host operating systems.
397 * Very important, as Windows guests are very sensitive to odd
398 * PCHS settings, and for big disks they consider anything
399 * except the standard mapping as odd. */
400 if (pThis->cCylinders != 0)
401 {
402 if (pThis->cSectors == 63 && (pThis->cHeads != 16 || pThis->cCylinders >= 1024))
403 {
404 /* For big disks, use dummy PCHS values and let the BIOS
405 * select an appropriate LCHS mapping. */
406 pThis->cCylinders = pThis->cbSize / 512 / 63 / 16;
407 pThis->cHeads = 16;
408 pThis->cSectors = 63;
409 pThis->enmTranslation = PDMBIOSTRANSLATION_LBA;
410 }
411 else
412 pThis->enmTranslation = PDMBIOSTRANSLATION_NONE;
413 }
414 }
415
416 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
417 return rc;
418}
419
420
421/**
422 * Destruct a driver instance.
423 *
424 * Most VM resources are freed by the VM. This callback is provided so that
425 * any non-VM resources can be freed correctly.
426 *
427 * @param pDrvIns The driver instance data.
428 */
429static DECLCALLBACK(void) drvHostHDDDestruct(PPDMDRVINS pDrvIns)
430{
431 PDRVHOSTHDD pThis = PDMINS2DATA(pDrvIns, PDRVHOSTHDD);
432 LogFlow(("%s: '%s'\n", __FUNCTION__, pThis->pszPath));
433
434 if (pThis->HostDiskFile != NIL_RTFILE)
435 {
436 RTFileClose(pThis->HostDiskFile);
437 pThis->HostDiskFile = NIL_RTFILE;
438 }
439 if (pThis->pszPath)
440 MMR3HeapFree(pThis->pszPath);
441}
442
443
444/**
445 * Block driver registration record.
446 */
447const PDMDRVREG g_DrvHostHDD =
448{
449 /* u32Version */
450 PDM_DRVREG_VERSION,
451 /* szDriverName */
452 "HostHDD",
453 /* pszDescription */
454 "Host Hard Disk Media Driver.",
455 /* fFlags */
456 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
457 /* fClass. */
458 PDM_DRVREG_CLASS_MEDIA,
459 /* cMaxInstances */
460 ~0,
461 /* cbInstance */
462 sizeof(DRVHOSTHDD),
463 /* pfnConstruct */
464 drvHostHDDConstruct,
465 /* pfnDestruct */
466 drvHostHDDDestruct,
467 /* pfnIOCtl */
468 NULL,
469 /* pfnPowerOn */
470 NULL,
471 /* pfnReset */
472 NULL,
473 /* pfnSuspend */
474 NULL,
475 /* pfnResume */
476 NULL,
477 /* pfnDetach */
478 NULL
479};
480
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