VirtualBox

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

Last change on this file since 2558 was 2534, checked in by vboxsync, 18 years ago

Provide error message (only for the release log, as the IDE device
issues another error message).

  • 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 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 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