VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvBlock.cpp@ 18437

Last change on this file since 18437 was 18127, checked in by vboxsync, 16 years ago

DrvVD: fail if a VD image was forced to be opened in read-only mode

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.8 KB
Line 
1/** @file
2 *
3 * VBox storage devices:
4 * Generic block driver
5 */
6
7/*
8 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_DRV_BLOCK
28#include <VBox/pdmdrv.h>
29#include <iprt/assert.h>
30#include <iprt/uuid.h>
31
32#include <string.h>
33
34#include "Builtins.h"
35
36
37/** @def VBOX_PERIODIC_FLUSH
38 * Enable support for periodically flushing the VDI to disk. This may prove
39 * useful for those nasty problems with the ultra-slow host filesystems.
40 * If this is enabled, it can be configured via the CFGM key
41 * "VBoxInternal/Devices/piix3ide/0/LUN#<x>/Config/FlushInterval". <x>
42 * must be replaced with the correct LUN number of the disk that should
43 * do the periodic flushes. The value of the key is the number of bytes
44 * written between flushes. A value of 0 (the default) denotes no flushes. */
45#define VBOX_PERIODIC_FLUSH
46
47/** @def VBOX_IGNORE_FLUSH
48 * Enable support for ignoring VDI flush requests. This can be useful for
49 * filesystems that show bad guest IDE write performance (especially with
50 * Windows guests). NOTE that this does not disable the flushes caused by
51 * the periodic flush cache feature above.
52 * If this feature is enabled, it can be configured via the CFGM key
53 * "VBoxInternal/Devices/piix3ide/0/LUN#<x>/Config/IgnoreFlush". <x>
54 * must be replaced with the correct LUN number of the disk that should
55 * ignore flush requests. The value of the key is a boolean. The default
56 * is to ignore flushes, i.e. true. */
57#define VBOX_IGNORE_FLUSH
58
59/*******************************************************************************
60* Structures and Typedefs *
61*******************************************************************************/
62/**
63 * Block driver instance data.
64 */
65typedef struct DRVBLOCK
66{
67 /** Pointer driver instance. */
68 PPDMDRVINS pDrvIns;
69 /** Drive type. */
70 PDMBLOCKTYPE enmType;
71 /** Locked indicator. */
72 bool fLocked;
73 /** Mountable indicator. */
74 bool fMountable;
75 /** Visible to the BIOS. */
76 bool fBiosVisible;
77#ifdef VBOX_PERIODIC_FLUSH
78 /** HACK: Configuration value for number of bytes written after which to flush. */
79 uint32_t cbFlushInterval;
80 /** HACK: Current count for the number of bytes written since the last flush. */
81 uint32_t cbDataWritten;
82#endif /* VBOX_PERIODIC_FLUSH */
83#ifdef VBOX_IGNORE_FLUSH
84 /** HACK: Disable flushes for this drive. */
85 bool fIgnoreFlush;
86#endif /* VBOX_IGNORE_FLUSH */
87 /** Pointer to the media driver below us.
88 * This is NULL if the media is not mounted. */
89 PPDMIMEDIA pDrvMedia;
90 /** Pointer to the block port interface above us. */
91 PPDMIBLOCKPORT pDrvBlockPort;
92 /** Pointer to the mount notify interface above us. */
93 PPDMIMOUNTNOTIFY pDrvMountNotify;
94 /** Our block interface. */
95 PDMIBLOCK IBlock;
96 /** Our block interface. */
97 PDMIBLOCKBIOS IBlockBios;
98 /** Our mountable interface. */
99 PDMIMOUNT IMount;
100
101 /** Pointer to the async media driver below us.
102 * This is NULL if the media is not mounted. */
103 PPDMIMEDIAASYNC pDrvMediaAsync;
104 /** Our media async port. */
105 PDMIMEDIAASYNCPORT IMediaAsyncPort;
106 /** Pointer to the async block port interface above us. */
107 PPDMIBLOCKASYNCPORT pDrvBlockAsyncPort;
108 /** Our async block interface. */
109 PDMIBLOCKASYNC IBlockAsync;
110
111 /** Uuid of the drive. */
112 RTUUID Uuid;
113
114 /** BIOS PCHS Geometry. */
115 PDMMEDIAGEOMETRY PCHSGeometry;
116 /** BIOS LCHS Geometry. */
117 PDMMEDIAGEOMETRY LCHSGeometry;
118} DRVBLOCK, *PDRVBLOCK;
119
120
121/* -=-=-=-=- IBlock -=-=-=-=- */
122
123/** Makes a PDRVBLOCK out of a PPDMIBLOCK. */
124#define PDMIBLOCK_2_DRVBLOCK(pInterface) ( (PDRVBLOCK)((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IBlock)) )
125
126/** @copydoc PDMIBLOCK::pfnRead */
127static DECLCALLBACK(int) drvblockRead(PPDMIBLOCK pInterface, uint64_t off, void *pvBuf, size_t cbRead)
128{
129 PDRVBLOCK pThis = PDMIBLOCK_2_DRVBLOCK(pInterface);
130
131 /*
132 * Check the state.
133 */
134 if (!pThis->pDrvMedia)
135 {
136 AssertMsgFailed(("Invalid state! Not mounted!\n"));
137 return VERR_PDM_MEDIA_NOT_MOUNTED;
138 }
139
140 int rc = pThis->pDrvMedia->pfnRead(pThis->pDrvMedia, off, pvBuf, cbRead);
141 return rc;
142}
143
144
145/** @copydoc PDMIBLOCK::pfnWrite */
146static DECLCALLBACK(int) drvblockWrite(PPDMIBLOCK pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
147{
148 PDRVBLOCK pThis = PDMIBLOCK_2_DRVBLOCK(pInterface);
149
150 /*
151 * Check the state.
152 */
153 if (!pThis->pDrvMedia)
154 {
155 AssertMsgFailed(("Invalid state! Not mounted!\n"));
156 return VERR_PDM_MEDIA_NOT_MOUNTED;
157 }
158
159 int rc = pThis->pDrvMedia->pfnWrite(pThis->pDrvMedia, off, pvBuf, cbWrite);
160#ifdef VBOX_PERIODIC_FLUSH
161 if (pThis->cbFlushInterval)
162 {
163 pThis->cbDataWritten += cbWrite;
164 if (pThis->cbDataWritten > pThis->cbFlushInterval)
165 {
166 pThis->cbDataWritten = 0;
167 pThis->pDrvMedia->pfnFlush(pThis->pDrvMedia);
168 }
169 }
170#endif /* VBOX_PERIODIC_FLUSH */
171
172 return rc;
173}
174
175
176/** @copydoc PDMIBLOCK::pfnFlush */
177static DECLCALLBACK(int) drvblockFlush(PPDMIBLOCK pInterface)
178{
179 PDRVBLOCK pThis = PDMIBLOCK_2_DRVBLOCK(pInterface);
180
181 /*
182 * Check the state.
183 */
184 if (!pThis->pDrvMedia)
185 {
186 AssertMsgFailed(("Invalid state! Not mounted!\n"));
187 return VERR_PDM_MEDIA_NOT_MOUNTED;
188 }
189
190#ifdef VBOX_IGNORE_FLUSH
191 if (pThis->fIgnoreFlush)
192 return VINF_SUCCESS;
193#endif /* VBOX_IGNORE_FLUSH */
194
195 int rc = pThis->pDrvMedia->pfnFlush(pThis->pDrvMedia);
196 if (rc == VERR_NOT_IMPLEMENTED)
197 rc = VINF_SUCCESS;
198 return rc;
199}
200
201
202/** @copydoc PDMIBLOCK::pfnIsReadOnly */
203static DECLCALLBACK(bool) drvblockIsReadOnly(PPDMIBLOCK pInterface)
204{
205 PDRVBLOCK pThis = PDMIBLOCK_2_DRVBLOCK(pInterface);
206
207 /*
208 * Check the state.
209 */
210 if (!pThis->pDrvMedia)
211 return false;
212
213 bool fRc = pThis->pDrvMedia->pfnIsReadOnly(pThis->pDrvMedia);
214 return fRc;
215}
216
217
218/** @copydoc PDMIBLOCK::pfnGetSize */
219static DECLCALLBACK(uint64_t) drvblockGetSize(PPDMIBLOCK pInterface)
220{
221 PDRVBLOCK pThis = PDMIBLOCK_2_DRVBLOCK(pInterface);
222
223 /*
224 * Check the state.
225 */
226 if (!pThis->pDrvMedia)
227 return 0;
228
229 uint64_t cb = pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
230 LogFlow(("drvblockGetSize: returns %llu\n", cb));
231 return cb;
232}
233
234
235/** @copydoc PDMIBLOCK::pfnGetType */
236static DECLCALLBACK(PDMBLOCKTYPE) drvblockGetType(PPDMIBLOCK pInterface)
237{
238 PDRVBLOCK pThis = PDMIBLOCK_2_DRVBLOCK(pInterface);
239 LogFlow(("drvblockGetType: returns %d\n", pThis->enmType));
240 return pThis->enmType;
241}
242
243
244/** @copydoc PDMIBLOCK::pfnGetUuid */
245static DECLCALLBACK(int) drvblockGetUuid(PPDMIBLOCK pInterface, PRTUUID pUuid)
246{
247 PDRVBLOCK pThis = PDMIBLOCK_2_DRVBLOCK(pInterface);
248
249 /*
250 * Copy the uuid.
251 */
252 *pUuid = pThis->Uuid;
253 return VINF_SUCCESS;
254}
255
256/* -=-=-=-=- IBlockAsync -=-=-=-=- */
257
258/** Makes a PDRVBLOCK out of a PPDMIBLOCKASYNC. */
259#define PDMIBLOCKASYNC_2_DRVBLOCK(pInterface) ( (PDRVBLOCK)((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IBlockAsync)) )
260
261/** @copydoc PDMIBLOCKASYNC::pfnStartRead */
262static DECLCALLBACK(int) drvblockAsyncReadStart(PPDMIBLOCKASYNC pInterface, uint64_t off, PPDMDATASEG pSeg, unsigned cSeg, size_t cbRead, void *pvUser)
263{
264 PDRVBLOCK pThis = PDMIBLOCKASYNC_2_DRVBLOCK(pInterface);
265
266 /*
267 * Check the state.
268 */
269 if (!pThis->pDrvMediaAsync)
270 {
271 AssertMsgFailed(("Invalid state! Not mounted!\n"));
272 return VERR_PDM_MEDIA_NOT_MOUNTED;
273 }
274
275 int rc = pThis->pDrvMediaAsync->pfnStartRead(pThis->pDrvMediaAsync, off, pSeg, cSeg, cbRead, pvUser);
276 return rc;
277}
278
279
280/** @copydoc PDMIBLOCKASYNC::pfnStartWrite */
281static DECLCALLBACK(int) drvblockAsyncWriteStart(PPDMIBLOCKASYNC pInterface, uint64_t off, PPDMDATASEG pSeg, unsigned cSeg, size_t cbWrite, void *pvUser)
282{
283 PDRVBLOCK pThis = PDMIBLOCKASYNC_2_DRVBLOCK(pInterface);
284
285 /*
286 * Check the state.
287 */
288 if (!pThis->pDrvMediaAsync)
289 {
290 AssertMsgFailed(("Invalid state! Not mounted!\n"));
291 return VERR_PDM_MEDIA_NOT_MOUNTED;
292 }
293
294 int rc = pThis->pDrvMediaAsync->pfnStartWrite(pThis->pDrvMediaAsync, off, pSeg, cSeg, cbWrite, pvUser);
295
296 return rc;
297}
298
299/* -=-=-=-=- IMediaAsyncPort -=-=-=-=- */
300
301/** Makes a PDRVBLOCKASYNC out of a PPDMIMEDIAASYNCPORT. */
302#define PDMIMEDIAASYNCPORT_2_DRVBLOCK(pInterface) ( (PDRVBLOCK((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IMediaAsyncPort))) )
303
304static DECLCALLBACK(int) drvblockAsyncTransferCompleteNotify(PPDMIMEDIAASYNCPORT pInterface, void *pvUser)
305{
306 PDRVBLOCK pThis = PDMIMEDIAASYNCPORT_2_DRVBLOCK(pInterface);
307
308 return pThis->pDrvBlockAsyncPort->pfnTransferCompleteNotify(pThis->pDrvBlockAsyncPort, pvUser);
309}
310
311/* -=-=-=-=- IBlockBios -=-=-=-=- */
312
313/** Makes a PDRVBLOCK out of a PPDMIBLOCKBIOS. */
314#define PDMIBLOCKBIOS_2_DRVBLOCK(pInterface) ( (PDRVBLOCK((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IBlockBios))) )
315
316
317/** @copydoc PDMIBLOCKBIOS::pfnGetPCHSGeometry */
318static DECLCALLBACK(int) drvblockGetPCHSGeometry(PPDMIBLOCKBIOS pInterface, PPDMMEDIAGEOMETRY pPCHSGeometry)
319{
320 PDRVBLOCK pThis = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
321
322 /*
323 * Check the state.
324 */
325 if (!pThis->pDrvMedia)
326 return VERR_PDM_MEDIA_NOT_MOUNTED;
327
328 /*
329 * Use configured/cached values if present.
330 */
331 if ( pThis->PCHSGeometry.cCylinders > 0
332 && pThis->PCHSGeometry.cHeads > 0
333 && pThis->PCHSGeometry.cSectors > 0)
334 {
335 *pPCHSGeometry = pThis->PCHSGeometry;
336 LogFlow(("%s: returns VINF_SUCCESS {%d,%d,%d}\n", __FUNCTION__, pThis->PCHSGeometry.cCylinders, pThis->PCHSGeometry.cHeads, pThis->PCHSGeometry.cSectors));
337 return VINF_SUCCESS;
338 }
339
340 /*
341 * Call media.
342 */
343 int rc = pThis->pDrvMedia->pfnBiosGetPCHSGeometry(pThis->pDrvMedia, &pThis->PCHSGeometry);
344
345 if (RT_SUCCESS(rc))
346 {
347 *pPCHSGeometry = pThis->PCHSGeometry;
348 LogFlow(("%s: returns %Rrc {%d,%d,%d}\n", __FUNCTION__, rc, pThis->PCHSGeometry.cCylinders, pThis->PCHSGeometry.cHeads, pThis->PCHSGeometry.cSectors));
349 }
350 else if (rc == VERR_NOT_IMPLEMENTED)
351 {
352 rc = VERR_PDM_GEOMETRY_NOT_SET;
353 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
354 }
355 return rc;
356}
357
358
359/** @copydoc PDMIBLOCKBIOS::pfnSetPCHSGeometry */
360static DECLCALLBACK(int) drvblockSetPCHSGeometry(PPDMIBLOCKBIOS pInterface, PCPDMMEDIAGEOMETRY pPCHSGeometry)
361{
362 LogFlow(("%s: cCylinders=%d cHeads=%d cSectors=%d\n", __FUNCTION__, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
363 PDRVBLOCK pThis = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
364
365 /*
366 * Check the state.
367 */
368 if (!pThis->pDrvMedia)
369 {
370 AssertMsgFailed(("Invalid state! Not mounted!\n"));
371 return VERR_PDM_MEDIA_NOT_MOUNTED;
372 }
373
374 /*
375 * Call media. Ignore the not implemented return code.
376 */
377 int rc = pThis->pDrvMedia->pfnBiosSetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
378
379 if ( RT_SUCCESS(rc)
380 || rc == VERR_NOT_IMPLEMENTED)
381 {
382 pThis->PCHSGeometry = *pPCHSGeometry;
383 rc = VINF_SUCCESS;
384 }
385 return rc;
386}
387
388
389/** @copydoc PDMIBLOCKBIOS::pfnGetLCHSGeometry */
390static DECLCALLBACK(int) drvblockGetLCHSGeometry(PPDMIBLOCKBIOS pInterface, PPDMMEDIAGEOMETRY pLCHSGeometry)
391{
392 PDRVBLOCK pThis = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
393
394 /*
395 * Check the state.
396 */
397 if (!pThis->pDrvMedia)
398 return VERR_PDM_MEDIA_NOT_MOUNTED;
399
400 /*
401 * Use configured/cached values if present.
402 */
403 if ( pThis->LCHSGeometry.cCylinders > 0
404 && pThis->LCHSGeometry.cHeads > 0
405 && pThis->LCHSGeometry.cSectors > 0)
406 {
407 *pLCHSGeometry = pThis->LCHSGeometry;
408 LogFlow(("%s: returns VINF_SUCCESS {%d,%d,%d}\n", __FUNCTION__, pThis->LCHSGeometry.cCylinders, pThis->LCHSGeometry.cHeads, pThis->LCHSGeometry.cSectors));
409 return VINF_SUCCESS;
410 }
411
412 /*
413 * Call media.
414 */
415 int rc = pThis->pDrvMedia->pfnBiosGetLCHSGeometry(pThis->pDrvMedia, &pThis->LCHSGeometry);
416
417 if (RT_SUCCESS(rc))
418 {
419 *pLCHSGeometry = pThis->LCHSGeometry;
420 LogFlow(("%s: returns %Rrc {%d,%d,%d}\n", __FUNCTION__, rc, pThis->LCHSGeometry.cCylinders, pThis->LCHSGeometry.cHeads, pThis->LCHSGeometry.cSectors));
421 }
422 else if (rc == VERR_NOT_IMPLEMENTED)
423 {
424 rc = VERR_PDM_GEOMETRY_NOT_SET;
425 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
426 }
427 return rc;
428}
429
430
431/** @copydoc PDMIBLOCKBIOS::pfnSetLCHSGeometry */
432static DECLCALLBACK(int) drvblockSetLCHSGeometry(PPDMIBLOCKBIOS pInterface, PCPDMMEDIAGEOMETRY pLCHSGeometry)
433{
434 LogFlow(("%s: cCylinders=%d cHeads=%d cSectors=%d\n", __FUNCTION__, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
435 PDRVBLOCK pThis = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
436
437 /*
438 * Check the state.
439 */
440 if (!pThis->pDrvMedia)
441 {
442 AssertMsgFailed(("Invalid state! Not mounted!\n"));
443 return VERR_PDM_MEDIA_NOT_MOUNTED;
444 }
445
446 /*
447 * Call media. Ignore the not implemented return code.
448 */
449 int rc = pThis->pDrvMedia->pfnBiosSetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
450
451 if ( RT_SUCCESS(rc)
452 || rc == VERR_NOT_IMPLEMENTED)
453 {
454 pThis->LCHSGeometry = *pLCHSGeometry;
455 rc = VINF_SUCCESS;
456 }
457 return rc;
458}
459
460
461/** @copydoc PDMIBLOCKBIOS::pfnIsVisible */
462static DECLCALLBACK(bool) drvblockIsVisible(PPDMIBLOCKBIOS pInterface)
463{
464 PDRVBLOCK pThis = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
465 LogFlow(("drvblockIsVisible: returns %d\n", pThis->fBiosVisible));
466 return pThis->fBiosVisible;
467}
468
469
470/** @copydoc PDMIBLOCKBIOS::pfnGetType */
471static DECLCALLBACK(PDMBLOCKTYPE) drvblockBiosGetType(PPDMIBLOCKBIOS pInterface)
472{
473 PDRVBLOCK pThis = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
474 LogFlow(("drvblockBiosGetType: returns %d\n", pThis->enmType));
475 return pThis->enmType;
476}
477
478
479
480/* -=-=-=-=- IMount -=-=-=-=- */
481
482/** Makes a PDRVBLOCK out of a PPDMIMOUNT. */
483#define PDMIMOUNT_2_DRVBLOCK(pInterface) ( (PDRVBLOCK)((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IMount)) )
484
485
486/** @copydoc PDMIMOUNT::pfnMount */
487static DECLCALLBACK(int) drvblockMount(PPDMIMOUNT pInterface, const char *pszFilename, const char *pszCoreDriver)
488{
489 LogFlow(("drvblockMount: pszFilename=%p:{%s} pszCoreDriver=%p:{%s}\n", pszFilename, pszFilename, pszCoreDriver, pszCoreDriver));
490 PDRVBLOCK pThis = PDMIMOUNT_2_DRVBLOCK(pInterface);
491
492 /*
493 * Validate state.
494 */
495 if (pThis->pDrvMedia)
496 {
497 AssertMsgFailed(("Already mounted\n"));
498 return VERR_PDM_MEDIA_MOUNTED;
499 }
500
501 /*
502 * Prepare configuration.
503 */
504 if (pszFilename)
505 {
506 int rc = pThis->pDrvIns->pDrvHlp->pfnMountPrepare(pThis->pDrvIns, pszFilename, pszCoreDriver);
507 if (RT_FAILURE(rc))
508 {
509 Log(("drvblockMount: Prepare failed for \"%s\" rc=%Rrc\n", pszFilename, rc));
510 return rc;
511 }
512 }
513
514 /*
515 * Attach the media driver and query it's interface.
516 */
517 PPDMIBASE pBase;
518 int rc = pThis->pDrvIns->pDrvHlp->pfnAttach(pThis->pDrvIns, &pBase);
519 if (RT_FAILURE(rc))
520 {
521 Log(("drvblockMount: Attach failed rc=%Rrc\n", rc));
522 return rc;
523 }
524
525 pThis->pDrvMedia = (PPDMIMEDIA)pBase->pfnQueryInterface(pBase, PDMINTERFACE_MEDIA);
526 if (pThis->pDrvMedia)
527 {
528 /*
529 * Initialize state.
530 */
531 pThis->fLocked = false;
532 pThis->PCHSGeometry.cCylinders = 0;
533 pThis->PCHSGeometry.cHeads = 0;
534 pThis->PCHSGeometry.cSectors = 0;
535 pThis->LCHSGeometry.cCylinders = 0;
536 pThis->LCHSGeometry.cHeads = 0;
537 pThis->LCHSGeometry.cSectors = 0;
538#ifdef VBOX_PERIODIC_FLUSH
539 pThis->cbDataWritten = 0;
540#endif /* VBOX_PERIODIC_FLUSH */
541
542 /*
543 * Notify driver/device above us.
544 */
545 if (pThis->pDrvMountNotify)
546 pThis->pDrvMountNotify->pfnMountNotify(pThis->pDrvMountNotify);
547 Log(("drvblockMount: Success\n"));
548 return VINF_SUCCESS;
549 }
550 else
551 rc = VERR_PDM_MISSING_INTERFACE_BELOW;
552
553 /*
554 * Failed, detatch the media driver.
555 */
556 AssertMsgFailed(("No media interface!\n"));
557 int rc2 = pThis->pDrvIns->pDrvHlp->pfnDetach(pThis->pDrvIns);
558 AssertRC(rc2);
559 pThis->pDrvMedia = NULL;
560 return rc;
561}
562
563
564/** @copydoc PDMIMOUNT::pfnUnmount */
565static DECLCALLBACK(int) drvblockUnmount(PPDMIMOUNT pInterface, bool fForce)
566{
567 PDRVBLOCK pThis = PDMIMOUNT_2_DRVBLOCK(pInterface);
568
569 /*
570 * Validate state.
571 */
572 if (!pThis->pDrvMedia)
573 {
574 Log(("drvblockUmount: Not mounted\n"));
575 return VERR_PDM_MEDIA_NOT_MOUNTED;
576 }
577 if (pThis->fLocked && !fForce)
578 {
579 Log(("drvblockUmount: Locked\n"));
580 return VERR_PDM_MEDIA_LOCKED;
581 }
582
583 /* Media is no longer locked even if it was previously. */
584 pThis->fLocked = false;
585
586 /*
587 * Detach the media driver and query it's interface.
588 */
589 int rc = pThis->pDrvIns->pDrvHlp->pfnDetach(pThis->pDrvIns);
590 if (RT_FAILURE(rc))
591 {
592 Log(("drvblockUnmount: Detach failed rc=%Rrc\n", rc));
593 return rc;
594 }
595 Assert(!pThis->pDrvMedia);
596
597 /*
598 * Notify driver/device above us.
599 */
600 if (pThis->pDrvMountNotify)
601 pThis->pDrvMountNotify->pfnUnmountNotify(pThis->pDrvMountNotify);
602 Log(("drvblockUnmount: success\n"));
603 return VINF_SUCCESS;
604}
605
606
607/** @copydoc PDMIMOUNT::pfnIsMounted */
608static DECLCALLBACK(bool) drvblockIsMounted(PPDMIMOUNT pInterface)
609{
610 PDRVBLOCK pThis = PDMIMOUNT_2_DRVBLOCK(pInterface);
611 return pThis->pDrvMedia != NULL;
612}
613
614/** @copydoc PDMIMOUNT::pfnLock */
615static DECLCALLBACK(int) drvblockLock(PPDMIMOUNT pInterface)
616{
617 PDRVBLOCK pThis = PDMIMOUNT_2_DRVBLOCK(pInterface);
618 Log(("drvblockLock: %d -> %d\n", pThis->fLocked, true));
619 pThis->fLocked = true;
620 return VINF_SUCCESS;
621}
622
623/** @copydoc PDMIMOUNT::pfnUnlock */
624static DECLCALLBACK(int) drvblockUnlock(PPDMIMOUNT pInterface)
625{
626 PDRVBLOCK pThis = PDMIMOUNT_2_DRVBLOCK(pInterface);
627 Log(("drvblockUnlock: %d -> %d\n", pThis->fLocked, false));
628 pThis->fLocked = false;
629 return VINF_SUCCESS;
630}
631
632/** @copydoc PDMIMOUNT::pfnIsLocked */
633static DECLCALLBACK(bool) drvblockIsLocked(PPDMIMOUNT pInterface)
634{
635 PDRVBLOCK pThis = PDMIMOUNT_2_DRVBLOCK(pInterface);
636 return pThis->fLocked;
637}
638
639
640/* -=-=-=-=- IBase -=-=-=-=- */
641
642/** @copydoc PDMIBASE::pfnQueryInterface. */
643static DECLCALLBACK(void *) drvblockQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
644{
645 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
646 PDRVBLOCK pThis = PDMINS_2_DATA(pDrvIns, PDRVBLOCK);
647 switch (enmInterface)
648 {
649 case PDMINTERFACE_BASE:
650 return &pDrvIns->IBase;
651 case PDMINTERFACE_BLOCK:
652 return &pThis->IBlock;
653 case PDMINTERFACE_BLOCK_BIOS:
654 return pThis->fBiosVisible ? &pThis->IBlockBios : NULL;
655 case PDMINTERFACE_MOUNT:
656 return pThis->fMountable ? &pThis->IMount : NULL;
657 case PDMINTERFACE_BLOCK_ASYNC:
658 return pThis->pDrvMediaAsync ? &pThis->IBlockAsync : NULL;
659 case PDMINTERFACE_MEDIA_ASYNC_PORT:
660 return pThis->pDrvBlockAsyncPort ? &pThis->IMediaAsyncPort : NULL;
661 default:
662 return NULL;
663 }
664}
665
666
667/* -=-=-=-=- driver interface -=-=-=-=- */
668
669/** @copydoc FNPDMDRVDETACH. */
670static DECLCALLBACK(void) drvblockDetach(PPDMDRVINS pDrvIns)
671{
672 PDRVBLOCK pThis = PDMINS_2_DATA(pDrvIns, PDRVBLOCK);
673 pThis->pDrvMedia = NULL;
674 pThis->pDrvMediaAsync = NULL;
675}
676
677/**
678 * Reset notification.
679 *
680 * @returns VBox status.
681 * @param pDevIns The driver instance data.
682 */
683static DECLCALLBACK(void) drvblockReset(PPDMDRVINS pDrvIns)
684{
685 PDRVBLOCK pThis = PDMINS_2_DATA(pDrvIns, PDRVBLOCK);
686
687 pThis->fLocked = false;
688}
689
690/**
691 * Construct a block driver instance.
692 *
693 * @returns VBox status.
694 * @param pDrvIns The driver instance data.
695 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
696 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
697 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
698 * iInstance it's expected to be used a bit in this function.
699 */
700static DECLCALLBACK(int) drvblockConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
701{
702 PDRVBLOCK pThis = PDMINS_2_DATA(pDrvIns, PDRVBLOCK);
703 LogFlow(("drvblockConstruct: iInstance=%d\n", pDrvIns->iInstance));
704
705 /*
706 * Validate configuration.
707 */
708#if defined(VBOX_PERIODIC_FLUSH) || defined(VBOX_IGNORE_FLUSH)
709 if (!CFGMR3AreValuesValid(pCfgHandle, "Type\0Locked\0BIOSVisible\0AttachFailError\0Cylinders\0Heads\0Sectors\0Mountable\0FlushInterval\0IgnoreFlush\0"))
710#else /* !(VBOX_PERIODIC_FLUSH || VBOX_IGNORE_FLUSH) */
711 if (!CFGMR3AreValuesValid(pCfgHandle, "Type\0Locked\0BIOSVisible\0AttachFailError\0Cylinders\0Heads\0Sectors\0Mountable\0"))
712#endif /* !(VBOX_PERIODIC_FLUSH || VBOX_IGNORE_FLUSH) */
713 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
714
715 /*
716 * Initialize most of the data members.
717 */
718 pThis->pDrvIns = pDrvIns;
719
720 /* IBase. */
721 pDrvIns->IBase.pfnQueryInterface = drvblockQueryInterface;
722
723 /* IBlock. */
724 pThis->IBlock.pfnRead = drvblockRead;
725 pThis->IBlock.pfnWrite = drvblockWrite;
726 pThis->IBlock.pfnFlush = drvblockFlush;
727 pThis->IBlock.pfnIsReadOnly = drvblockIsReadOnly;
728 pThis->IBlock.pfnGetSize = drvblockGetSize;
729 pThis->IBlock.pfnGetType = drvblockGetType;
730 pThis->IBlock.pfnGetUuid = drvblockGetUuid;
731
732 /* IBlockBios. */
733 pThis->IBlockBios.pfnGetPCHSGeometry = drvblockGetPCHSGeometry;
734 pThis->IBlockBios.pfnSetPCHSGeometry = drvblockSetPCHSGeometry;
735 pThis->IBlockBios.pfnGetLCHSGeometry = drvblockGetLCHSGeometry;
736 pThis->IBlockBios.pfnSetLCHSGeometry = drvblockSetLCHSGeometry;
737 pThis->IBlockBios.pfnIsVisible = drvblockIsVisible;
738 pThis->IBlockBios.pfnGetType = drvblockBiosGetType;
739
740 /* IMount. */
741 pThis->IMount.pfnMount = drvblockMount;
742 pThis->IMount.pfnUnmount = drvblockUnmount;
743 pThis->IMount.pfnIsMounted = drvblockIsMounted;
744 pThis->IMount.pfnLock = drvblockLock;
745 pThis->IMount.pfnUnlock = drvblockUnlock;
746 pThis->IMount.pfnIsLocked = drvblockIsLocked;
747
748 /* IBlockAsync. */
749 pThis->IBlockAsync.pfnStartRead = drvblockAsyncReadStart;
750 pThis->IBlockAsync.pfnStartWrite = drvblockAsyncWriteStart;
751
752 /* IMediaAsyncPort. */
753 pThis->IMediaAsyncPort.pfnTransferCompleteNotify = drvblockAsyncTransferCompleteNotify;
754
755 /*
756 * Get the IBlockPort & IMountNotify interfaces of the above driver/device.
757 */
758 pThis->pDrvBlockPort = (PPDMIBLOCKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_BLOCK_PORT);
759 if (!pThis->pDrvBlockPort)
760 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
761 N_("No block port interface above"));
762
763 /* Try to get the optional async block port interface above. */
764 pThis->pDrvBlockAsyncPort = (PPDMIBLOCKASYNCPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_BLOCK_ASYNC_PORT);
765
766 pThis->pDrvMountNotify = (PPDMIMOUNTNOTIFY)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_MOUNT_NOTIFY);
767
768 /*
769 * Query configuration.
770 */
771 /* type */
772 char *psz;
773 int rc = CFGMR3QueryStringAlloc(pCfgHandle, "Type", &psz);
774 if (RT_FAILURE(rc))
775 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_BLOCK_NO_TYPE, N_("Failed to obtain the type"));
776 if (!strcmp(psz, "HardDisk"))
777 pThis->enmType = PDMBLOCKTYPE_HARD_DISK;
778 else if (!strcmp(psz, "DVD"))
779 pThis->enmType = PDMBLOCKTYPE_DVD;
780 else if (!strcmp(psz, "CDROM"))
781 pThis->enmType = PDMBLOCKTYPE_CDROM;
782 else if (!strcmp(psz, "Floppy 2.88"))
783 pThis->enmType = PDMBLOCKTYPE_FLOPPY_2_88;
784 else if (!strcmp(psz, "Floppy 1.44"))
785 pThis->enmType = PDMBLOCKTYPE_FLOPPY_1_44;
786 else if (!strcmp(psz, "Floppy 1.20"))
787 pThis->enmType = PDMBLOCKTYPE_FLOPPY_1_20;
788 else if (!strcmp(psz, "Floppy 720"))
789 pThis->enmType = PDMBLOCKTYPE_FLOPPY_720;
790 else if (!strcmp(psz, "Floppy 360"))
791 pThis->enmType = PDMBLOCKTYPE_FLOPPY_360;
792 else
793 {
794 PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_BLOCK_UNKNOWN_TYPE, RT_SRC_POS,
795 N_("Unknown type \"%s\""), psz);
796 MMR3HeapFree(psz);
797 return VERR_PDM_BLOCK_UNKNOWN_TYPE;
798 }
799 Log2(("drvblockConstruct: enmType=%d\n", pThis->enmType));
800 MMR3HeapFree(psz); psz = NULL;
801
802 /* Mountable */
803 rc = CFGMR3QueryBoolDef(pCfgHandle, "Mountable", &pThis->fMountable, false);
804 if (RT_FAILURE(rc))
805 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Mountable\" from the config"));
806
807 /* Locked */
808 rc = CFGMR3QueryBoolDef(pCfgHandle, "Locked", &pThis->fLocked, false);
809 if (RT_FAILURE(rc))
810 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Locked\" from the config"));
811
812 /* BIOS visible */
813 rc = CFGMR3QueryBoolDef(pCfgHandle, "BIOSVisible", &pThis->fBiosVisible, true);
814 if (RT_FAILURE(rc))
815 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"BIOSVisible\" from the config"));
816
817 /** @todo AttachFailError is currently completely ignored. */
818
819 /* Cylinders */
820 rc = CFGMR3QueryU32Def(pCfgHandle, "Cylinders", &pThis->LCHSGeometry.cCylinders, 0);
821 if (RT_FAILURE(rc))
822 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Cylinders\" from the config"));
823
824 /* Heads */
825 rc = CFGMR3QueryU32Def(pCfgHandle, "Heads", &pThis->LCHSGeometry.cHeads, 0);
826 if (RT_FAILURE(rc))
827 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Heads\" from the config"));
828
829 /* Sectors */
830 rc = CFGMR3QueryU32Def(pCfgHandle, "Sectors", &pThis->LCHSGeometry.cSectors, 0);
831 if (RT_FAILURE(rc))
832 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Sectors\" from the config"));
833
834 /* Uuid */
835 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Uuid", &psz);
836 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
837 RTUuidClear(&pThis->Uuid);
838 else if (RT_SUCCESS(rc))
839 {
840 rc = RTUuidFromStr(&pThis->Uuid, psz);
841 if (RT_FAILURE(rc))
842 {
843 PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, "%s",
844 N_("Uuid from string failed on \"%s\""), psz);
845 MMR3HeapFree(psz);
846 return rc;
847 }
848 MMR3HeapFree(psz); psz = NULL;
849 }
850 else
851 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Uuid\" from the config"));
852
853#ifdef VBOX_PERIODIC_FLUSH
854 rc = CFGMR3QueryU32Def(pCfgHandle, "FlushInterval", &pThis->cbFlushInterval, 0);
855 if (RT_FAILURE(rc))
856 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"FlushInterval\" from the config"));
857#endif /* VBOX_PERIODIC_FLUSH */
858
859#ifdef VBOX_IGNORE_FLUSH
860 rc = CFGMR3QueryBoolDef(pCfgHandle, "IgnoreFlush", &pThis->fIgnoreFlush, true);
861 if (RT_FAILURE(rc))
862 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"IgnoreFlush\" from the config"));
863#endif /* VBOX_IGNORE_FLUSH */
864
865 /*
866 * Try attach driver below and query it's media interface.
867 */
868 PPDMIBASE pBase;
869 rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
870 if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
871 && pThis->enmType != PDMBLOCKTYPE_HARD_DISK)
872 return VINF_SUCCESS;
873 if (RT_FAILURE(rc))
874 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
875 N_("Failed to attach driver below us! %Rrf"), rc);
876
877 pThis->pDrvMedia = (PPDMIMEDIA)pBase->pfnQueryInterface(pBase, PDMINTERFACE_MEDIA);
878 if (!pThis->pDrvMedia)
879 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
880 N_("No media or async media interface below"));
881
882 /* Try to get the optional async interface. */
883 pThis->pDrvMediaAsync = (PPDMIMEDIAASYNC)pBase->pfnQueryInterface(pBase, PDMINTERFACE_MEDIA_ASYNC);
884
885 if (RTUuidIsNull(&pThis->Uuid))
886 {
887 if (pThis->enmType == PDMBLOCKTYPE_HARD_DISK)
888 pThis->pDrvMedia->pfnGetUuid(pThis->pDrvMedia, &pThis->Uuid);
889 }
890
891 return VINF_SUCCESS;
892}
893
894
895/**
896 * Block driver registration record.
897 */
898const PDMDRVREG g_DrvBlock =
899{
900 /* u32Version */
901 PDM_DRVREG_VERSION,
902 /* szDriverName */
903 "Block",
904 /* pszDescription */
905 "Generic block driver.",
906 /* fFlags */
907 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
908 /* fClass. */
909 PDM_DRVREG_CLASS_BLOCK,
910 /* cMaxInstances */
911 ~0,
912 /* cbInstance */
913 sizeof(DRVBLOCK),
914 /* pfnConstruct */
915 drvblockConstruct,
916 /* pfnDestruct */
917 NULL,
918 /* pfnIOCtl */
919 NULL,
920 /* pfnPowerOn */
921 NULL,
922 /* pfnReset */
923 drvblockReset,
924 /* pfnSuspend */
925 NULL,
926 /* pfnResume */
927 NULL,
928 /* pfnDetach */
929 drvblockDetach
930};
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