VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvHostBase.cpp@ 52505

Last change on this file since 52505 was 52505, checked in by vboxsync, 10 years ago

DrvHostBase: Provide I/O buffer allocator, fixes crash if a host DVD drive is attached to the AHCI controller without passthrough enabled and a medium inserted

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 77.1 KB
Line 
1/* $Id: DrvHostBase.cpp 52505 2014-08-27 13:13:37Z vboxsync $ */
2/** @file
3 * DrvHostBase - Host base drive access driver.
4 */
5
6/*
7 * Copyright (C) 2006-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_HOST_BASE
23#ifdef RT_OS_DARWIN
24# include <mach/mach.h>
25# include <Carbon/Carbon.h>
26# include <IOKit/IOKitLib.h>
27# include <IOKit/storage/IOStorageDeviceCharacteristics.h>
28# include <IOKit/scsi/SCSITaskLib.h>
29# include <IOKit/scsi/SCSICommandOperationCodes.h>
30# include <IOKit/IOBSD.h>
31# include <DiskArbitration/DiskArbitration.h>
32# include <mach/mach_error.h>
33# include <VBox/scsi.h>
34
35#elif defined(RT_OS_L4)
36 /* Nothing special requires... yeah, right. */
37
38#elif defined(RT_OS_LINUX)
39# include <sys/ioctl.h>
40# include <sys/fcntl.h>
41# include <errno.h>
42
43#elif defined(RT_OS_SOLARIS)
44# include <fcntl.h>
45# include <errno.h>
46# include <stropts.h>
47# include <malloc.h>
48# include <sys/dkio.h>
49extern "C" char *getfullblkname(char *);
50
51#elif defined(RT_OS_WINDOWS)
52# define WIN32_NO_STATUS
53# include <Windows.h>
54# include <dbt.h>
55# undef WIN32_NO_STATUS
56# include <ntstatus.h>
57
58/* from ntdef.h */
59typedef LONG NTSTATUS;
60
61/* from ntddk.h */
62typedef struct _IO_STATUS_BLOCK {
63 union {
64 NTSTATUS Status;
65 PVOID Pointer;
66 };
67 ULONG_PTR Information;
68} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
69
70
71/* from ntinternals.com */
72typedef enum _FS_INFORMATION_CLASS {
73 FileFsVolumeInformation=1,
74 FileFsLabelInformation,
75 FileFsSizeInformation,
76 FileFsDeviceInformation,
77 FileFsAttributeInformation,
78 FileFsControlInformation,
79 FileFsFullSizeInformation,
80 FileFsObjectIdInformation,
81 FileFsMaximumInformation
82} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS;
83
84typedef struct _FILE_FS_SIZE_INFORMATION {
85 LARGE_INTEGER TotalAllocationUnits;
86 LARGE_INTEGER AvailableAllocationUnits;
87 ULONG SectorsPerAllocationUnit;
88 ULONG BytesPerSector;
89} FILE_FS_SIZE_INFORMATION, *PFILE_FS_SIZE_INFORMATION;
90
91extern "C"
92NTSTATUS __stdcall NtQueryVolumeInformationFile(
93 /*IN*/ HANDLE FileHandle,
94 /*OUT*/ PIO_STATUS_BLOCK IoStatusBlock,
95 /*OUT*/ PVOID FileSystemInformation,
96 /*IN*/ ULONG Length,
97 /*IN*/ FS_INFORMATION_CLASS FileSystemInformationClass );
98
99#elif defined(RT_OS_FREEBSD)
100# include <sys/cdefs.h>
101# include <sys/param.h>
102# include <errno.h>
103# include <stdio.h>
104# include <cam/cam.h>
105# include <cam/cam_ccb.h>
106# include <cam/scsi/scsi_message.h>
107# include <cam/scsi/scsi_pass.h>
108# include <VBox/scsi.h>
109# include <iprt/log.h>
110#else
111# error "Unsupported Platform."
112#endif
113
114#include <VBox/vmm/pdmdrv.h>
115#include <iprt/assert.h>
116#include <iprt/file.h>
117#include <iprt/path.h>
118#include <iprt/string.h>
119#include <iprt/thread.h>
120#include <iprt/semaphore.h>
121#include <iprt/uuid.h>
122#include <iprt/asm.h>
123#include <iprt/critsect.h>
124#include <iprt/ctype.h>
125#include <iprt/mem.h>
126
127#include "DrvHostBase.h"
128
129
130
131
132/* -=-=-=-=- IBlock -=-=-=-=- */
133
134/** @copydoc PDMIBLOCK::pfnRead */
135static DECLCALLBACK(int) drvHostBaseRead(PPDMIBLOCK pInterface, uint64_t off, void *pvBuf, size_t cbRead)
136{
137 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
138 LogFlow(("%s-%d: drvHostBaseRead: off=%#llx pvBuf=%p cbRead=%#x (%s)\n",
139 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, off, pvBuf, cbRead, pThis->pszDevice));
140 RTCritSectEnter(&pThis->CritSect);
141
142 /*
143 * Check the state.
144 */
145 int rc;
146#ifdef RT_OS_DARWIN
147 if ( pThis->fMediaPresent
148 && pThis->ppScsiTaskDI
149 && pThis->cbBlock)
150#elif RT_OS_FREEBSD
151 if ( pThis->fMediaPresent
152 && pThis->cbBlock)
153#else
154 if (pThis->fMediaPresent)
155#endif
156 {
157#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
158 /*
159 * Issue a READ(12) request.
160 */
161 do
162 {
163 const uint32_t LBA = off / pThis->cbBlock;
164 AssertReturn(!(off % pThis->cbBlock), VERR_INVALID_PARAMETER);
165 uint32_t cbRead32 = cbRead > SCSI_MAX_BUFFER_SIZE
166 ? SCSI_MAX_BUFFER_SIZE
167 : (uint32_t)cbRead;
168 const uint32_t cBlocks = cbRead32 / pThis->cbBlock;
169 AssertReturn(!(cbRead % pThis->cbBlock), VERR_INVALID_PARAMETER);
170 uint8_t abCmd[16] =
171 {
172 SCSI_READ_12, 0,
173 RT_BYTE4(LBA), RT_BYTE3(LBA), RT_BYTE2(LBA), RT_BYTE1(LBA),
174 RT_BYTE4(cBlocks), RT_BYTE3(cBlocks), RT_BYTE2(cBlocks), RT_BYTE1(cBlocks),
175 0, 0, 0, 0, 0
176 };
177 rc = DRVHostBaseScsiCmd(pThis, abCmd, 12, PDMBLOCKTXDIR_FROM_DEVICE, pvBuf, &cbRead32, NULL, 0, 0);
178
179 off += cbRead32;
180 cbRead -= cbRead32;
181 pvBuf = (uint8_t *)pvBuf + cbRead32;
182 } while ((cbRead > 0) && RT_SUCCESS(rc));
183
184#else
185 /*
186 * Seek and read.
187 */
188 rc = RTFileReadAt(pThis->hFileDevice, off, pvBuf, cbRead, NULL);
189 if (RT_SUCCESS(rc))
190 {
191 Log2(("%s-%d: drvHostBaseRead: off=%#llx cbRead=%#x\n"
192 "%16.*Rhxd\n",
193 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, off, cbRead, cbRead, pvBuf));
194 }
195 else
196 Log(("%s-%d: drvHostBaseRead: RTFileReadAt(%RTfile, %#llx, %p, %#x) -> %Rrc ('%s')\n",
197 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->hFileDevice,
198 off, pvBuf, cbRead, rc, pThis->pszDevice));
199#endif
200 }
201 else
202 rc = VERR_MEDIA_NOT_PRESENT;
203
204 RTCritSectLeave(&pThis->CritSect);
205 LogFlow(("%s-%d: drvHostBaseRead: returns %Rrc\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, rc));
206 return rc;
207}
208
209
210/** @copydoc PDMIBLOCK::pfnWrite */
211static DECLCALLBACK(int) drvHostBaseWrite(PPDMIBLOCK pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
212{
213 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
214 LogFlow(("%s-%d: drvHostBaseWrite: off=%#llx pvBuf=%p cbWrite=%#x (%s)\n",
215 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, off, pvBuf, cbWrite, pThis->pszDevice));
216 Log2(("%s-%d: drvHostBaseWrite: off=%#llx cbWrite=%#x\n"
217 "%16.*Rhxd\n",
218 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, off, cbWrite, cbWrite, pvBuf));
219 RTCritSectEnter(&pThis->CritSect);
220
221 /*
222 * Check the state.
223 */
224 int rc;
225 if (!pThis->fReadOnly)
226 {
227 if (pThis->fMediaPresent)
228 {
229#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
230 /** @todo write support... */
231 rc = VERR_WRITE_PROTECT;
232
233#else
234 /*
235 * Seek and write.
236 */
237 rc = RTFileWriteAt(pThis->hFileDevice, off, pvBuf, cbWrite, NULL);
238 if (RT_FAILURE(rc))
239 Log(("%s-%d: drvHostBaseWrite: RTFileWriteAt(%RTfile, %#llx, %p, %#x) -> %Rrc ('%s')\n",
240 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->hFileDevice,
241 off, pvBuf, cbWrite, rc, pThis->pszDevice));
242#endif
243 }
244 else
245 rc = VERR_MEDIA_NOT_PRESENT;
246 }
247 else
248 rc = VERR_WRITE_PROTECT;
249
250 RTCritSectLeave(&pThis->CritSect);
251 LogFlow(("%s-%d: drvHostBaseWrite: returns %Rrc\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, rc));
252 return rc;
253}
254
255
256/** @copydoc PDMIBLOCK::pfnFlush */
257static DECLCALLBACK(int) drvHostBaseFlush(PPDMIBLOCK pInterface)
258{
259 int rc;
260 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
261 LogFlow(("%s-%d: drvHostBaseFlush: (%s)\n",
262 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->pszDevice));
263 RTCritSectEnter(&pThis->CritSect);
264
265 if (pThis->fMediaPresent)
266 {
267#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
268 rc = VINF_SUCCESS;
269 /** @todo scsi device buffer flush... */
270#else
271 rc = RTFileFlush(pThis->hFileDevice);
272#endif
273 }
274 else
275 rc = VERR_MEDIA_NOT_PRESENT;
276
277 RTCritSectLeave(&pThis->CritSect);
278 LogFlow(("%s-%d: drvHostBaseFlush: returns %Rrc\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, rc));
279 return rc;
280}
281
282
283/** @copydoc PDMIBLOCK::pfnIsReadOnly */
284static DECLCALLBACK(bool) drvHostBaseIsReadOnly(PPDMIBLOCK pInterface)
285{
286 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
287 return pThis->fReadOnly;
288}
289
290
291/** @copydoc PDMIBLOCK::pfnGetSize */
292static DECLCALLBACK(uint64_t) drvHostBaseGetSize(PPDMIBLOCK pInterface)
293{
294 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
295 RTCritSectEnter(&pThis->CritSect);
296
297 uint64_t cb = 0;
298 if (pThis->fMediaPresent)
299 cb = pThis->cbSize;
300
301 RTCritSectLeave(&pThis->CritSect);
302 LogFlow(("%s-%d: drvHostBaseGetSize: returns %llu\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, cb));
303 return cb;
304}
305
306
307/** @copydoc PDMIBLOCK::pfnGetType */
308static DECLCALLBACK(PDMBLOCKTYPE) drvHostBaseGetType(PPDMIBLOCK pInterface)
309{
310 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
311 LogFlow(("%s-%d: drvHostBaseGetType: returns %d\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->enmType));
312 return pThis->enmType;
313}
314
315
316/** @copydoc PDMIBLOCK::pfnGetUuid */
317static DECLCALLBACK(int) drvHostBaseGetUuid(PPDMIBLOCK pInterface, PRTUUID pUuid)
318{
319 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
320
321 *pUuid = pThis->Uuid;
322
323 LogFlow(("%s-%d: drvHostBaseGetUuid: returns VINF_SUCCESS *pUuid=%RTuuid\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pUuid));
324 return VINF_SUCCESS;
325}
326
327
328/** @copydoc PDMIBLOCK::pfnIoBufAlloc */
329static DECLCALLBACK(int) drvHostBaseIoBufAlloc(PPDMIBLOCK pInterface, size_t cb, void **ppvNew)
330{
331 LogFlowFunc(("\n"));
332 int rc;
333 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
334
335 void *pvNew = RTMemAlloc(cb);
336 if (RT_LIKELY(pvNew))
337 {
338 *ppvNew = pvNew;
339 rc = VINF_SUCCESS;
340 }
341 else
342 rc = VERR_NO_MEMORY;
343
344 LogFlowFunc(("returns %Rrc\n", rc));
345 return rc;
346}
347
348/** @copydoc PDMIBLOCK::pfnIoBufFree */
349static DECLCALLBACK(int) drvHostBaseIoBufFree(PPDMIBLOCK pInterface, void *pv, size_t cb)
350{
351 LogFlowFunc(("\n"));
352 int rc = VINF_SUCCESS;
353 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
354
355 NOREF(cb);
356 RTMemFree(pv);
357
358 LogFlowFunc(("returns %Rrc\n", rc));
359 return rc;
360}
361
362
363
364/* -=-=-=-=- IBlockBios -=-=-=-=- */
365
366/** Makes a PDRVHOSTBASE out of a PPDMIBLOCKBIOS. */
367#define PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface) ( (PDRVHOSTBASE((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTBASE, IBlockBios))) )
368
369
370/** @copydoc PDMIBLOCKBIOS::pfnGetPCHSGeometry */
371static DECLCALLBACK(int) drvHostBaseGetPCHSGeometry(PPDMIBLOCKBIOS pInterface, PPDMMEDIAGEOMETRY pPCHSGeometry)
372{
373 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
374 RTCritSectEnter(&pThis->CritSect);
375
376 int rc = VINF_SUCCESS;
377 if (pThis->fMediaPresent)
378 {
379 if ( pThis->PCHSGeometry.cCylinders > 0
380 && pThis->PCHSGeometry.cHeads > 0
381 && pThis->PCHSGeometry.cSectors > 0)
382 {
383 *pPCHSGeometry = pThis->PCHSGeometry;
384 }
385 else
386 rc = VERR_PDM_GEOMETRY_NOT_SET;
387 }
388 else
389 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
390
391 RTCritSectLeave(&pThis->CritSect);
392 LogFlow(("%s-%d: %s: returns %Rrc CHS={%d,%d,%d}\n",
393 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, __FUNCTION__, rc, pThis->PCHSGeometry.cCylinders, pThis->PCHSGeometry.cHeads, pThis->PCHSGeometry.cSectors));
394 return rc;
395}
396
397
398/** @copydoc PDMIBLOCKBIOS::pfnSetPCHSGeometry */
399static DECLCALLBACK(int) drvHostBaseSetPCHSGeometry(PPDMIBLOCKBIOS pInterface, PCPDMMEDIAGEOMETRY pPCHSGeometry)
400{
401 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
402 LogFlow(("%s-%d: %s: cCylinders=%d cHeads=%d cSectors=%d\n",
403 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, __FUNCTION__, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
404 RTCritSectEnter(&pThis->CritSect);
405
406 int rc = VINF_SUCCESS;
407 if (pThis->fMediaPresent)
408 {
409 pThis->PCHSGeometry = *pPCHSGeometry;
410 }
411 else
412 {
413 AssertMsgFailed(("Invalid state! Not mounted!\n"));
414 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
415 }
416
417 RTCritSectLeave(&pThis->CritSect);
418 return rc;
419}
420
421
422/** @copydoc PDMIBLOCKBIOS::pfnGetLCHSGeometry */
423static DECLCALLBACK(int) drvHostBaseGetLCHSGeometry(PPDMIBLOCKBIOS pInterface, PPDMMEDIAGEOMETRY pLCHSGeometry)
424{
425 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
426 RTCritSectEnter(&pThis->CritSect);
427
428 int rc = VINF_SUCCESS;
429 if (pThis->fMediaPresent)
430 {
431 if ( pThis->LCHSGeometry.cCylinders > 0
432 && pThis->LCHSGeometry.cHeads > 0
433 && pThis->LCHSGeometry.cSectors > 0)
434 {
435 *pLCHSGeometry = pThis->LCHSGeometry;
436 }
437 else
438 rc = VERR_PDM_GEOMETRY_NOT_SET;
439 }
440 else
441 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
442
443 RTCritSectLeave(&pThis->CritSect);
444 LogFlow(("%s-%d: %s: returns %Rrc CHS={%d,%d,%d}\n",
445 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, __FUNCTION__, rc, pThis->LCHSGeometry.cCylinders, pThis->LCHSGeometry.cHeads, pThis->LCHSGeometry.cSectors));
446 return rc;
447}
448
449
450/** @copydoc PDMIBLOCKBIOS::pfnSetLCHSGeometry */
451static DECLCALLBACK(int) drvHostBaseSetLCHSGeometry(PPDMIBLOCKBIOS pInterface, PCPDMMEDIAGEOMETRY pLCHSGeometry)
452{
453 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
454 LogFlow(("%s-%d: %s: cCylinders=%d cHeads=%d cSectors=%d\n",
455 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, __FUNCTION__, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
456 RTCritSectEnter(&pThis->CritSect);
457
458 int rc = VINF_SUCCESS;
459 if (pThis->fMediaPresent)
460 {
461 pThis->LCHSGeometry = *pLCHSGeometry;
462 }
463 else
464 {
465 AssertMsgFailed(("Invalid state! Not mounted!\n"));
466 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
467 }
468
469 RTCritSectLeave(&pThis->CritSect);
470 return rc;
471}
472
473
474/** @copydoc PDMIBLOCKBIOS::pfnIsVisible */
475static DECLCALLBACK(bool) drvHostBaseIsVisible(PPDMIBLOCKBIOS pInterface)
476{
477 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
478 return pThis->fBiosVisible;
479}
480
481
482/** @copydoc PDMIBLOCKBIOS::pfnGetType */
483static DECLCALLBACK(PDMBLOCKTYPE) drvHostBaseBiosGetType(PPDMIBLOCKBIOS pInterface)
484{
485 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
486 return pThis->enmType;
487}
488
489
490
491/* -=-=-=-=- IMount -=-=-=-=- */
492
493/** @copydoc PDMIMOUNT::pfnMount */
494static DECLCALLBACK(int) drvHostBaseMount(PPDMIMOUNT pInterface, const char *pszFilename, const char *pszCoreDriver)
495{
496 /* We're not mountable. */
497 AssertMsgFailed(("drvHostBaseMount: This shouldn't be called!\n"));
498 return VERR_PDM_MEDIA_MOUNTED;
499}
500
501
502/** @copydoc PDMIMOUNT::pfnUnmount */
503static DECLCALLBACK(int) drvHostBaseUnmount(PPDMIMOUNT pInterface, bool fForce, bool fEject)
504{
505 /* While we're not mountable (see drvHostBaseMount), we're unmountable. */
506 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
507 RTCritSectEnter(&pThis->CritSect);
508
509 /*
510 * Validate state.
511 */
512 int rc = VINF_SUCCESS;
513 if (!pThis->fLocked || fForce)
514 {
515 /* Unlock drive if necessary. */
516 if (pThis->fLocked)
517 {
518 if (pThis->pfnDoLock)
519 rc = pThis->pfnDoLock(pThis, false);
520 if (RT_SUCCESS(rc))
521 pThis->fLocked = false;
522 }
523
524 /*
525 * Media is no longer present.
526 */
527 DRVHostBaseMediaNotPresent(pThis);
528 }
529 else
530 {
531 Log(("drvHostiBaseUnmount: Locked\n"));
532 rc = VERR_PDM_MEDIA_LOCKED;
533 }
534
535 RTCritSectLeave(&pThis->CritSect);
536 LogFlow(("drvHostBaseUnmount: returns %Rrc\n", rc));
537 return rc;
538}
539
540
541/** @copydoc PDMIMOUNT::pfnIsMounted */
542static DECLCALLBACK(bool) drvHostBaseIsMounted(PPDMIMOUNT pInterface)
543{
544 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
545 RTCritSectEnter(&pThis->CritSect);
546
547 bool fRc = pThis->fMediaPresent;
548
549 RTCritSectLeave(&pThis->CritSect);
550 return fRc;
551}
552
553
554/** @copydoc PDMIMOUNT::pfnIsLocked */
555static DECLCALLBACK(int) drvHostBaseLock(PPDMIMOUNT pInterface)
556{
557 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
558 RTCritSectEnter(&pThis->CritSect);
559
560 int rc = VINF_SUCCESS;
561 if (!pThis->fLocked)
562 {
563 if (pThis->pfnDoLock)
564 rc = pThis->pfnDoLock(pThis, true);
565 if (RT_SUCCESS(rc))
566 pThis->fLocked = true;
567 }
568 else
569 LogFlow(("%s-%d: drvHostBaseLock: already locked\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
570
571 RTCritSectLeave(&pThis->CritSect);
572 LogFlow(("%s-%d: drvHostBaseLock: returns %Rrc\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, rc));
573 return rc;
574}
575
576
577/** @copydoc PDMIMOUNT::pfnIsLocked */
578static DECLCALLBACK(int) drvHostBaseUnlock(PPDMIMOUNT pInterface)
579{
580 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
581 RTCritSectEnter(&pThis->CritSect);
582
583 int rc = VINF_SUCCESS;
584 if (pThis->fLocked)
585 {
586 if (pThis->pfnDoLock)
587 rc = pThis->pfnDoLock(pThis, false);
588 if (RT_SUCCESS(rc))
589 pThis->fLocked = false;
590 }
591 else
592 LogFlow(("%s-%d: drvHostBaseUnlock: not locked\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
593
594 RTCritSectLeave(&pThis->CritSect);
595 LogFlow(("%s-%d: drvHostBaseUnlock: returns %Rrc\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, rc));
596 return rc;
597}
598
599
600/** @copydoc PDMIMOUNT::pfnIsLocked */
601static DECLCALLBACK(bool) drvHostBaseIsLocked(PPDMIMOUNT pInterface)
602{
603 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
604 RTCritSectEnter(&pThis->CritSect);
605
606 bool fRc = pThis->fLocked;
607
608 RTCritSectLeave(&pThis->CritSect);
609 return fRc;
610}
611
612
613/* -=-=-=-=- IBase -=-=-=-=- */
614
615/**
616 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
617 */
618static DECLCALLBACK(void *) drvHostBaseQueryInterface(PPDMIBASE pInterface, const char *pszIID)
619{
620 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
621 PDRVHOSTBASE pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTBASE);
622
623 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
624 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCK, &pThis->IBlock);
625 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKBIOS, pThis->fBiosVisible ? &pThis->IBlockBios : NULL);
626 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNT, &pThis->IMount);
627 return NULL;
628}
629
630
631/* -=-=-=-=- poller thread -=-=-=-=- */
632
633#ifdef RT_OS_DARWIN
634/** The runloop input source name for the disk arbitration events. */
635# define MY_RUN_LOOP_MODE CFSTR("drvHostBaseDA") /** @todo r=bird: Check if this will cause trouble in the same way that the one in the USB code did. */
636
637/**
638 * Gets the BSD Name (/dev/disc[0-9]+) for the service.
639 *
640 * This is done by recursing down the I/O registry until we hit upon an entry
641 * with a BSD Name. Usually we find it two levels down. (Further down under
642 * the IOCDPartitionScheme, the volume (slices) BSD Name is found. We don't
643 * seem to have to go this far fortunately.)
644 *
645 * @return VINF_SUCCESS if found, VERR_FILE_NOT_FOUND otherwise.
646 * @param Entry The current I/O registry entry reference.
647 * @param pszName Where to store the name. 128 bytes.
648 * @param cRecursions Number of recursions. This is used as an precaution
649 * just to limit the depth and avoid blowing the stack
650 * should we hit a bug or something.
651 */
652static int drvHostBaseGetBSDName(io_registry_entry_t Entry, char *pszName, unsigned cRecursions)
653{
654 int rc = VERR_FILE_NOT_FOUND;
655 io_iterator_t Children = 0;
656 kern_return_t krc = IORegistryEntryGetChildIterator(Entry, kIOServicePlane, &Children);
657 if (krc == KERN_SUCCESS)
658 {
659 io_object_t Child;
660 while ( rc == VERR_FILE_NOT_FOUND
661 && (Child = IOIteratorNext(Children)) != 0)
662 {
663 CFStringRef BSDNameStrRef = (CFStringRef)IORegistryEntryCreateCFProperty(Child, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0);
664 if (BSDNameStrRef)
665 {
666 if (CFStringGetCString(BSDNameStrRef, pszName, 128, kCFStringEncodingUTF8))
667 rc = VINF_SUCCESS;
668 else
669 AssertFailed();
670 CFRelease(BSDNameStrRef);
671 }
672 if (rc == VERR_FILE_NOT_FOUND && cRecursions < 10)
673 rc = drvHostBaseGetBSDName(Child, pszName, cRecursions + 1);
674 IOObjectRelease(Child);
675 }
676 IOObjectRelease(Children);
677 }
678 return rc;
679}
680
681
682/**
683 * Callback notifying us that the async DADiskClaim()/DADiskUnmount call has completed.
684 *
685 * @param DiskRef The disk that was attempted claimed / unmounted.
686 * @param DissenterRef NULL on success, contains details on failure.
687 * @param pvContext Pointer to the return code variable.
688 */
689static void drvHostBaseDADoneCallback(DADiskRef DiskRef, DADissenterRef DissenterRef, void *pvContext)
690{
691 int *prc = (int *)pvContext;
692 if (!DissenterRef)
693 *prc = 0;
694 else
695 *prc = DADissenterGetStatus(DissenterRef) ? DADissenterGetStatus(DissenterRef) : -1;
696 CFRunLoopStop(CFRunLoopGetCurrent());
697}
698
699
700/**
701 * Obtain exclusive access to the DVD device, umount it if necessary.
702 *
703 * @return VBox status code.
704 * @param pThis The driver instance.
705 * @param DVDService The DVD service object.
706 */
707static int drvHostBaseObtainExclusiveAccess(PDRVHOSTBASE pThis, io_object_t DVDService)
708{
709 PPDMDRVINS pDrvIns = pThis->pDrvIns; NOREF(pDrvIns);
710
711 for (unsigned iTry = 0;; iTry++)
712 {
713 IOReturn irc = (*pThis->ppScsiTaskDI)->ObtainExclusiveAccess(pThis->ppScsiTaskDI);
714 if (irc == kIOReturnSuccess)
715 {
716 /*
717 * This is a bit weird, but if we unmounted the DVD drive we also need to
718 * unlock it afterwards or the guest won't be able to eject it later on.
719 */
720 if (pThis->pDADisk)
721 {
722 uint8_t abCmd[16] =
723 {
724 SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL, 0, 0, 0, false, 0,
725 0,0,0,0,0,0,0,0,0,0
726 };
727 DRVHostBaseScsiCmd(pThis, abCmd, 6, PDMBLOCKTXDIR_NONE, NULL, NULL, NULL, 0, 0);
728 }
729 return VINF_SUCCESS;
730 }
731 if (irc == kIOReturnExclusiveAccess)
732 return VERR_SHARING_VIOLATION; /* already used exclusivly. */
733 if (irc != kIOReturnBusy)
734 return VERR_GENERAL_FAILURE; /* not mounted */
735
736 /*
737 * Attempt to the unmount all volumes of the device.
738 * It seems we can can do this all in one go without having to enumerate the
739 * volumes (sessions) and deal with them one by one. This is very fortuitous
740 * as the disk arbitration API is a bit cumbersome to deal with.
741 */
742 if (iTry > 2)
743 return VERR_DRIVE_LOCKED;
744 char szName[128];
745 int rc = drvHostBaseGetBSDName(DVDService, &szName[0], 0);
746 if (RT_SUCCESS(rc))
747 {
748 pThis->pDASession = DASessionCreate(kCFAllocatorDefault);
749 if (pThis->pDASession)
750 {
751 DASessionScheduleWithRunLoop(pThis->pDASession, CFRunLoopGetCurrent(), MY_RUN_LOOP_MODE);
752 pThis->pDADisk = DADiskCreateFromBSDName(kCFAllocatorDefault, pThis->pDASession, szName);
753 if (pThis->pDADisk)
754 {
755 /*
756 * Try claim the device.
757 */
758 Log(("%s-%d: calling DADiskClaim on '%s'.\n", pDrvIns->pReg->szName, pDrvIns->iInstance, szName));
759 int rcDA = -2;
760 DADiskClaim(pThis->pDADisk, kDADiskClaimOptionDefault, NULL, NULL, drvHostBaseDADoneCallback, &rcDA);
761 SInt32 rc32 = CFRunLoopRunInMode(MY_RUN_LOOP_MODE, 120.0, FALSE);
762 AssertMsg(rc32 == kCFRunLoopRunStopped, ("rc32=%RI32 (%RX32)\n", rc32, rc32));
763 if ( rc32 == kCFRunLoopRunStopped
764 && !rcDA)
765 {
766 /*
767 * Try unmount the device.
768 */
769 Log(("%s-%d: calling DADiskUnmount on '%s'.\n", pDrvIns->pReg->szName, pDrvIns->iInstance, szName));
770 rcDA = -2;
771 DADiskUnmount(pThis->pDADisk, kDADiskUnmountOptionWhole, drvHostBaseDADoneCallback, &rcDA);
772 rc32 = CFRunLoopRunInMode(MY_RUN_LOOP_MODE, 120.0, FALSE);
773 AssertMsg(rc32 == kCFRunLoopRunStopped, ("rc32=%RI32 (%RX32)\n", rc32, rc32));
774 if ( rc32 == kCFRunLoopRunStopped
775 && !rcDA)
776 {
777 iTry = 99;
778 DASessionUnscheduleFromRunLoop(pThis->pDASession, CFRunLoopGetCurrent(), MY_RUN_LOOP_MODE);
779 Log(("%s-%d: unmount succeed - retrying.\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
780 continue;
781 }
782 Log(("%s-%d: umount => rc32=%d & rcDA=%#x\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc32, rcDA));
783
784 /* failed - cleanup */
785 DADiskUnclaim(pThis->pDADisk);
786 }
787 else
788 Log(("%s-%d: claim => rc32=%d & rcDA=%#x\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc32, rcDA));
789
790 CFRelease(pThis->pDADisk);
791 pThis->pDADisk = NULL;
792 }
793 else
794 Log(("%s-%d: failed to open disk '%s'!\n", pDrvIns->pReg->szName, pDrvIns->iInstance, szName));
795
796 DASessionUnscheduleFromRunLoop(pThis->pDASession, CFRunLoopGetCurrent(), MY_RUN_LOOP_MODE);
797 CFRelease(pThis->pDASession);
798 pThis->pDASession = NULL;
799 }
800 else
801 Log(("%s-%d: failed to create DA session!\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
802 }
803 RTThreadSleep(10);
804 }
805}
806#endif /* RT_OS_DARWIN */
807
808
809#ifndef RT_OS_SOLARIS
810/**
811 * Wrapper for open / RTFileOpen / IOKit.
812 *
813 * @remark The Darwin code must correspond exactly to the enumeration
814 * done in Main/darwin/iokit.c.
815 */
816static int drvHostBaseOpen(PDRVHOSTBASE pThis, PRTFILE pFileDevice, bool fReadOnly)
817{
818# ifdef RT_OS_DARWIN
819 /* Darwin is kind of special... */
820 Assert(!pFileDevice); NOREF(pFileDevice);
821 Assert(!pThis->cbBlock);
822 Assert(!pThis->MasterPort);
823 Assert(!pThis->ppMMCDI);
824 Assert(!pThis->ppScsiTaskDI);
825
826 /*
827 * Open the master port on the first invocation.
828 */
829 kern_return_t krc = IOMasterPort(MACH_PORT_NULL, &pThis->MasterPort);
830 AssertReturn(krc == KERN_SUCCESS, VERR_GENERAL_FAILURE);
831
832 /*
833 * Create a matching dictionary for searching for CD, DVD and BlueRay services in the IOKit.
834 *
835 * The idea is to find all the devices which are of class IOCDBlockStorageDevice.
836 * CD devices are represented by IOCDBlockStorageDevice class itself, while DVD and BlueRay ones
837 * have it as a parent class.
838 */
839 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IOCDBlockStorageDevice");
840 AssertReturn(RefMatchingDict, NULL);
841
842 /*
843 * do the search and get a collection of keyboards.
844 */
845 io_iterator_t DVDServices = NULL;
846 IOReturn irc = IOServiceGetMatchingServices(pThis->MasterPort, RefMatchingDict, &DVDServices);
847 AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%d\n", irc), NULL);
848 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
849
850 /*
851 * Enumerate the matching drives (services).
852 * (This enumeration must be identical to the one performed in Main/src-server/darwin/iokit.cpp.)
853 */
854 int rc = VERR_FILE_NOT_FOUND;
855 unsigned i = 0;
856 io_object_t DVDService;
857 while ((DVDService = IOIteratorNext(DVDServices)) != 0)
858 {
859 /*
860 * Get the properties we use to identify the DVD drive.
861 *
862 * While there is a (weird 12 byte) GUID, it isn't persistent
863 * across boots. So, we have to use a combination of the
864 * vendor name and product name properties with an optional
865 * sequence number for identification.
866 */
867 CFMutableDictionaryRef PropsRef = 0;
868 krc = IORegistryEntryCreateCFProperties(DVDService, &PropsRef, kCFAllocatorDefault, kNilOptions);
869 if (krc == KERN_SUCCESS)
870 {
871 /* Get the Device Characteristics dictionary. */
872 CFDictionaryRef DevCharRef = (CFDictionaryRef)CFDictionaryGetValue(PropsRef, CFSTR(kIOPropertyDeviceCharacteristicsKey));
873 if (DevCharRef)
874 {
875 /* The vendor name. */
876 char szVendor[128];
877 char *pszVendor = &szVendor[0];
878 CFTypeRef ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyVendorNameKey));
879 if ( ValueRef
880 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
881 && CFStringGetCString((CFStringRef)ValueRef, szVendor, sizeof(szVendor), kCFStringEncodingUTF8))
882 pszVendor = RTStrStrip(szVendor);
883 else
884 *pszVendor = '\0';
885
886 /* The product name. */
887 char szProduct[128];
888 char *pszProduct = &szProduct[0];
889 ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyProductNameKey));
890 if ( ValueRef
891 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
892 && CFStringGetCString((CFStringRef)ValueRef, szProduct, sizeof(szProduct), kCFStringEncodingUTF8))
893 pszProduct = RTStrStrip(szProduct);
894 else
895 *pszProduct = '\0';
896
897 /* Construct the two names and compare thwm with the one we're searching for. */
898 char szName1[256 + 32];
899 char szName2[256 + 32];
900 if (*pszVendor || *pszProduct)
901 {
902 if (*pszVendor && *pszProduct)
903 {
904 RTStrPrintf(szName1, sizeof(szName1), "%s %s", pszVendor, pszProduct);
905 RTStrPrintf(szName2, sizeof(szName2), "%s %s (#%u)", pszVendor, pszProduct, i);
906 }
907 else
908 {
909 strcpy(szName1, *pszVendor ? pszVendor : pszProduct);
910 RTStrPrintf(szName2, sizeof(szName2), "%s (#%u)", *pszVendor ? pszVendor : pszProduct, i);
911 }
912 }
913 else
914 {
915 RTStrPrintf(szName1, sizeof(szName1), "(#%u)", i);
916 strcpy(szName2, szName1);
917 }
918
919 if ( !strcmp(szName1, pThis->pszDeviceOpen)
920 || !strcmp(szName2, pThis->pszDeviceOpen))
921 {
922 /*
923 * Found it! Now, get the client interface and stuff.
924 * Note that we could also query kIOSCSITaskDeviceUserClientTypeID here if the
925 * MMC client plugin is missing. For now we assume this won't be necessary.
926 */
927 SInt32 Score = 0;
928 IOCFPlugInInterface **ppPlugInInterface = NULL;
929 krc = IOCreatePlugInInterfaceForService(DVDService, kIOMMCDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
930 &ppPlugInInterface, &Score);
931 if (krc == KERN_SUCCESS)
932 {
933 HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
934 CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID),
935 (LPVOID *)&pThis->ppMMCDI);
936 (*ppPlugInInterface)->Release(ppPlugInInterface);
937 ppPlugInInterface = NULL;
938 if (hrc == S_OK)
939 {
940 pThis->ppScsiTaskDI = (*pThis->ppMMCDI)->GetSCSITaskDeviceInterface(pThis->ppMMCDI);
941 if (pThis->ppScsiTaskDI)
942 rc = VINF_SUCCESS;
943 else
944 {
945 LogRel(("GetSCSITaskDeviceInterface failed on '%s'\n", pThis->pszDeviceOpen));
946 rc = VERR_NOT_SUPPORTED;
947 (*pThis->ppMMCDI)->Release(pThis->ppMMCDI);
948 }
949 }
950 else
951 {
952 rc = VERR_GENERAL_FAILURE;//RTErrConvertFromDarwinCOM(krc);
953 pThis->ppMMCDI = NULL;
954 }
955 }
956 else /* Check for kIOSCSITaskDeviceUserClientTypeID? */
957 rc = VERR_GENERAL_FAILURE;//RTErrConvertFromDarwinKern(krc);
958
959 /* Obtain exclusive access to the device so we can send SCSI commands. */
960 if (RT_SUCCESS(rc))
961 rc = drvHostBaseObtainExclusiveAccess(pThis, DVDService);
962
963 /* Cleanup on failure. */
964 if (RT_FAILURE(rc))
965 {
966 if (pThis->ppScsiTaskDI)
967 {
968 (*pThis->ppScsiTaskDI)->Release(pThis->ppScsiTaskDI);
969 pThis->ppScsiTaskDI = NULL;
970 }
971 if (pThis->ppMMCDI)
972 {
973 (*pThis->ppMMCDI)->Release(pThis->ppMMCDI);
974 pThis->ppMMCDI = NULL;
975 }
976 }
977
978 IOObjectRelease(DVDService);
979 break;
980 }
981 }
982 CFRelease(PropsRef);
983 }
984 else
985 AssertMsgFailed(("krc=%#x\n", krc));
986
987 IOObjectRelease(DVDService);
988 i++;
989 }
990
991 IOObjectRelease(DVDServices);
992 return rc;
993
994#elif defined(RT_OS_FREEBSD)
995 RTFILE hFileDevice;
996 int rc = RTFileOpen(&hFileDevice, pThis->pszDeviceOpen, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
997 if (RT_FAILURE(rc))
998 return rc;
999
1000 /*
1001 * The current device handle can't passthrough SCSI commands.
1002 * We have to get he passthrough device path and open this.
1003 */
1004 union ccb DeviceCCB;
1005 memset(&DeviceCCB, 0, sizeof(DeviceCCB));
1006
1007 DeviceCCB.ccb_h.func_code = XPT_GDEVLIST;
1008 int rcBSD = ioctl(RTFileToNative(hFileDevice), CAMGETPASSTHRU, &DeviceCCB);
1009 if (!rcBSD)
1010 {
1011 char *pszPassthroughDevice = NULL;
1012 rc = RTStrAPrintf(&pszPassthroughDevice, "/dev/%s%u",
1013 DeviceCCB.cgdl.periph_name, DeviceCCB.cgdl.unit_number);
1014 if (rc >= 0)
1015 {
1016 RTFILE hPassthroughDevice;
1017 rc = RTFileOpen(&hPassthroughDevice, pszPassthroughDevice, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
1018 RTStrFree(pszPassthroughDevice);
1019 if (RT_SUCCESS(rc))
1020 {
1021 /* Get needed device parameters. */
1022
1023 /*
1024 * The device path, target id and lun id. Those are
1025 * needed for the SCSI passthrough ioctl.
1026 */
1027 memset(&DeviceCCB, 0, sizeof(DeviceCCB));
1028 DeviceCCB.ccb_h.func_code = XPT_GDEVLIST;
1029
1030 rcBSD = ioctl(RTFileToNative(hPassthroughDevice), CAMGETPASSTHRU, &DeviceCCB);
1031 if (!rcBSD)
1032 {
1033 if (DeviceCCB.cgdl.status != CAM_GDEVLIST_ERROR)
1034 {
1035 pThis->ScsiBus = DeviceCCB.ccb_h.path_id;
1036 pThis->ScsiTargetID = DeviceCCB.ccb_h.target_id;
1037 pThis->ScsiLunID = DeviceCCB.ccb_h.target_lun;
1038 *pFileDevice = hPassthroughDevice;
1039 }
1040 else
1041 {
1042 /* The passthrough device wasn't found. */
1043 rc = VERR_NOT_FOUND;
1044 }
1045 }
1046 else
1047 rc = RTErrConvertFromErrno(errno);
1048
1049 if (RT_FAILURE(rc))
1050 RTFileClose(hPassthroughDevice);
1051 }
1052 }
1053 else
1054 rc = VERR_NO_STR_MEMORY;
1055 }
1056 else
1057 rc = RTErrConvertFromErrno(errno);
1058
1059 RTFileClose(hFileDevice);
1060 return rc;
1061
1062#else
1063 uint32_t fFlags = (fReadOnly ? RTFILE_O_READ : RTFILE_O_READWRITE) | RTFILE_O_OPEN | RTFILE_O_DENY_NONE;
1064# ifdef RT_OS_LINUX
1065 fFlags |= RTFILE_O_NON_BLOCK;
1066# endif
1067 return RTFileOpen(pFileDevice, pThis->pszDeviceOpen, fFlags);
1068#endif
1069}
1070
1071#else /* RT_OS_SOLARIS */
1072
1073/**
1074 * Solaris wrapper for RTFileOpen.
1075 *
1076 * Solaris has to deal with two filehandles, a block and a raw one. Rather than messing
1077 * with drvHostBaseOpen's function signature & body, having a separate one is better.
1078 *
1079 * @returns VBox status code.
1080 */
1081static int drvHostBaseOpen(PDRVHOSTBASE pThis, PRTFILE pFileBlockDevice, PRTFILE pFileRawDevice, bool fReadOnly)
1082{
1083 unsigned fFlags = (fReadOnly ? RTFILE_O_READ : RTFILE_O_READWRITE)
1084 | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_NON_BLOCK;
1085 int rc = RTFileOpen(pFileBlockDevice, pThis->pszDeviceOpen, fFlags);
1086 if (RT_SUCCESS(rc))
1087 {
1088 rc = RTFileOpen(pFileRawDevice, pThis->pszRawDeviceOpen, fFlags);
1089 if (RT_SUCCESS(rc))
1090 return rc;
1091
1092 LogRel(("DVD: failed to open device %s rc=%Rrc\n", pThis->pszRawDeviceOpen, rc));
1093 RTFileClose(*pFileBlockDevice);
1094 }
1095 else
1096 LogRel(("DVD: failed to open device %s rc=%Rrc\n", pThis->pszDeviceOpen, rc));
1097 return rc;
1098}
1099#endif /* RT_OS_SOLARIS */
1100
1101
1102/**
1103 * (Re)opens the device.
1104 *
1105 * This is used to open the device during construction, but it's also used to re-open
1106 * the device when a media is inserted. This re-open will kill off any cached data
1107 * that Linux for some peculiar reason thinks should survive a media change...
1108 *
1109 * @returns VBOX status code.
1110 * @param pThis Instance data.
1111 */
1112static int drvHostBaseReopen(PDRVHOSTBASE pThis)
1113{
1114#ifndef RT_OS_DARWIN /* Only *one* open for darwin. */
1115 LogFlow(("%s-%d: drvHostBaseReopen: '%s'\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->pszDeviceOpen));
1116
1117 RTFILE hFileDevice;
1118#ifdef RT_OS_SOLARIS
1119 if (pThis->hFileRawDevice != NIL_RTFILE)
1120 {
1121 RTFileClose(pThis->hFileRawDevice);
1122 pThis->hFileRawDevice = NIL_RTFILE;
1123 }
1124 if (pThis->hFileDevice != NIL_RTFILE)
1125 {
1126 RTFileClose(pThis->hFileDevice);
1127 pThis->hFileDevice = NIL_RTFILE;
1128 }
1129 RTFILE hFileRawDevice;
1130 int rc = drvHostBaseOpen(pThis, &hFileDevice, &hFileRawDevice, pThis->fReadOnlyConfig);
1131#else
1132 int rc = drvHostBaseOpen(pThis, &hFileDevice, pThis->fReadOnlyConfig);
1133#endif
1134 if (RT_FAILURE(rc))
1135 {
1136 if (!pThis->fReadOnlyConfig)
1137 {
1138 LogFlow(("%s-%d: drvHostBaseReopen: '%s' - retry readonly (%Rrc)\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->pszDeviceOpen, rc));
1139#ifdef RT_OS_SOLARIS
1140 rc = drvHostBaseOpen(pThis, &hFileDevice, &hFileRawDevice, false);
1141#else
1142 rc = drvHostBaseOpen(pThis, &hFileDevice, false);
1143#endif
1144 }
1145 if (RT_FAILURE(rc))
1146 {
1147 LogFlow(("%s-%d: failed to open device '%s', rc=%Rrc\n",
1148 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->pszDevice, rc));
1149 return rc;
1150 }
1151 pThis->fReadOnly = true;
1152 }
1153 else
1154 pThis->fReadOnly = pThis->fReadOnlyConfig;
1155
1156#ifdef RT_OS_SOLARIS
1157 if (pThis->hFileRawDevice != NIL_RTFILE)
1158 RTFileClose(pThis->hFileRawDevice);
1159 pThis->hFileRawDevice = hFileRawDevice;
1160#endif
1161
1162 if (pThis->hFileDevice != NIL_RTFILE)
1163 RTFileClose(pThis->hFileDevice);
1164 pThis->hFileDevice = hFileDevice;
1165#endif /* !RT_OS_DARWIN */
1166 return VINF_SUCCESS;
1167}
1168
1169
1170/**
1171 * Queries the media size.
1172 *
1173 * @returns VBox status code.
1174 * @param pThis Pointer to the instance data.
1175 * @param pcb Where to store the media size in bytes.
1176 */
1177static int drvHostBaseGetMediaSize(PDRVHOSTBASE pThis, uint64_t *pcb)
1178{
1179#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
1180 /*
1181 * Try a READ_CAPACITY command...
1182 */
1183 struct
1184 {
1185 uint32_t cBlocks;
1186 uint32_t cbBlock;
1187 } Buf = {0, 0};
1188 uint32_t cbBuf = sizeof(Buf);
1189 uint8_t abCmd[16] =
1190 {
1191 SCSI_READ_CAPACITY, 0, 0, 0, 0, 0, 0,
1192 0,0,0,0,0,0,0,0,0
1193 };
1194 int rc = DRVHostBaseScsiCmd(pThis, abCmd, 6, PDMBLOCKTXDIR_FROM_DEVICE, &Buf, &cbBuf, NULL, 0, 0);
1195 if (RT_SUCCESS(rc))
1196 {
1197 Assert(cbBuf == sizeof(Buf));
1198 Buf.cBlocks = RT_BE2H_U32(Buf.cBlocks);
1199 Buf.cbBlock = RT_BE2H_U32(Buf.cbBlock);
1200 //if (Buf.cbBlock > 2048) /* everyone else is doing this... check if it needed/right.*/
1201 // Buf.cbBlock = 2048;
1202 pThis->cbBlock = Buf.cbBlock;
1203
1204 *pcb = (uint64_t)Buf.cBlocks * Buf.cbBlock;
1205 }
1206 return rc;
1207
1208#elif defined(RT_OS_SOLARIS)
1209 /*
1210 * Sun docs suggests using DKIOCGGEOM instead of DKIOCGMEDIAINFO, but
1211 * Sun themselves use DKIOCGMEDIAINFO for DVDs/CDs, and use DKIOCGGEOM
1212 * for secondary storage devices.
1213 */
1214 struct dk_minfo MediaInfo;
1215 if (ioctl(RTFileToNative(pThis->hFileRawDevice), DKIOCGMEDIAINFO, &MediaInfo) == 0)
1216 {
1217 *pcb = MediaInfo.dki_capacity * (uint64_t)MediaInfo.dki_lbsize;
1218 return VINF_SUCCESS;
1219 }
1220 return RTFileSeek(pThis->hFileDevice, 0, RTFILE_SEEK_END, pcb);
1221
1222#elif defined(RT_OS_WINDOWS)
1223 /* use NT api, retry a few times if the media is being verified. */
1224 IO_STATUS_BLOCK IoStatusBlock = {0};
1225 FILE_FS_SIZE_INFORMATION FsSize= {0};
1226 NTSTATUS rcNt = NtQueryVolumeInformationFile((HANDLE)RTFileToNative(pThis->hFileDevice), &IoStatusBlock,
1227 &FsSize, sizeof(FsSize), FileFsSizeInformation);
1228 int cRetries = 5;
1229 while (rcNt == STATUS_VERIFY_REQUIRED && cRetries-- > 0)
1230 {
1231 RTThreadSleep(10);
1232 rcNt = NtQueryVolumeInformationFile((HANDLE)RTFileToNative(pThis->hFileDevice), &IoStatusBlock,
1233 &FsSize, sizeof(FsSize), FileFsSizeInformation);
1234 }
1235 if (rcNt >= 0)
1236 {
1237 *pcb = FsSize.TotalAllocationUnits.QuadPart * FsSize.BytesPerSector;
1238 return VINF_SUCCESS;
1239 }
1240
1241 /* convert nt status code to VBox status code. */
1242 /** @todo Make conversion function!. */
1243 int rc = VERR_GENERAL_FAILURE;
1244 switch (rcNt)
1245 {
1246 case STATUS_NO_MEDIA_IN_DEVICE: rc = VERR_MEDIA_NOT_PRESENT; break;
1247 case STATUS_VERIFY_REQUIRED: rc = VERR_TRY_AGAIN; break;
1248 }
1249 LogFlow(("drvHostBaseGetMediaSize: NtQueryVolumeInformationFile -> %#lx\n", rcNt, rc));
1250 return rc;
1251#else
1252 return RTFileSeek(pThis->hFileDevice, 0, RTFILE_SEEK_END, pcb);
1253#endif
1254}
1255
1256
1257#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
1258/**
1259 * Execute a SCSI command.
1260 *
1261 * @param pThis The instance data.
1262 * @param pbCmd Pointer to the SCSI command.
1263 * @param cbCmd The size of the SCSI command.
1264 * @param enmTxDir The transfer direction.
1265 * @param pvBuf The buffer. Can be NULL if enmTxDir is PDMBLOCKTXDIR_NONE.
1266 * @param pcbBuf Where to get the buffer size from and put the actual transfer size. Can be NULL.
1267 * @param pbSense Where to put the sense data. Can be NULL.
1268 * @param cbSense Size of the sense data buffer.
1269 * @param cTimeoutMillies The timeout. 0 mean the default timeout.
1270 *
1271 * @returns VINF_SUCCESS on success (no sense code).
1272 * @returns VERR_UNRESOLVED_ERROR if sense code is present.
1273 * @returns Some other VBox status code on failures without sense code.
1274 *
1275 * @todo Fix VERR_UNRESOLVED_ERROR abuse.
1276 */
1277DECLCALLBACK(int) DRVHostBaseScsiCmd(PDRVHOSTBASE pThis, const uint8_t *pbCmd, size_t cbCmd, PDMBLOCKTXDIR enmTxDir,
1278 void *pvBuf, uint32_t *pcbBuf, uint8_t *pbSense, size_t cbSense, uint32_t cTimeoutMillies)
1279{
1280 /*
1281 * Minimal input validation.
1282 */
1283 Assert(enmTxDir == PDMBLOCKTXDIR_NONE || enmTxDir == PDMBLOCKTXDIR_FROM_DEVICE || enmTxDir == PDMBLOCKTXDIR_TO_DEVICE);
1284 Assert(!pvBuf || pcbBuf);
1285 Assert(pvBuf || enmTxDir == PDMBLOCKTXDIR_NONE);
1286 Assert(pbSense || !cbSense);
1287 AssertPtr(pbCmd);
1288 Assert(cbCmd <= 16 && cbCmd >= 1);
1289 const uint32_t cbBuf = pcbBuf ? *pcbBuf : 0;
1290 if (pcbBuf)
1291 *pcbBuf = 0;
1292
1293# ifdef RT_OS_DARWIN
1294 Assert(pThis->ppScsiTaskDI);
1295
1296 int rc = VERR_GENERAL_FAILURE;
1297 SCSITaskInterface **ppScsiTaskI = (*pThis->ppScsiTaskDI)->CreateSCSITask(pThis->ppScsiTaskDI);
1298 if (!ppScsiTaskI)
1299 return VERR_NO_MEMORY;
1300 do
1301 {
1302 /* Setup the scsi command. */
1303 SCSICommandDescriptorBlock cdb = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
1304 memcpy(&cdb[0], pbCmd, cbCmd);
1305 IOReturn irc = (*ppScsiTaskI)->SetCommandDescriptorBlock(ppScsiTaskI, cdb, cbCmd);
1306 AssertBreak(irc == kIOReturnSuccess);
1307
1308 /* Setup the buffer. */
1309 if (enmTxDir == PDMBLOCKTXDIR_NONE)
1310 irc = (*ppScsiTaskI)->SetScatterGatherEntries(ppScsiTaskI, NULL, 0, 0, kSCSIDataTransfer_NoDataTransfer);
1311 else
1312 {
1313 IOVirtualRange Range = { (IOVirtualAddress)pvBuf, cbBuf };
1314 irc = (*ppScsiTaskI)->SetScatterGatherEntries(ppScsiTaskI, &Range, 1, cbBuf,
1315 enmTxDir == PDMBLOCKTXDIR_FROM_DEVICE
1316 ? kSCSIDataTransfer_FromTargetToInitiator
1317 : kSCSIDataTransfer_FromInitiatorToTarget);
1318 }
1319 AssertBreak(irc == kIOReturnSuccess);
1320
1321 /* Set the timeout. */
1322 irc = (*ppScsiTaskI)->SetTimeoutDuration(ppScsiTaskI, cTimeoutMillies ? cTimeoutMillies : 30000 /*ms*/);
1323 AssertBreak(irc == kIOReturnSuccess);
1324
1325 /* Execute the command and get the response. */
1326 SCSI_Sense_Data SenseData = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
1327 SCSIServiceResponse ServiceResponse = kSCSIServiceResponse_Request_In_Process;
1328 SCSITaskStatus TaskStatus = kSCSITaskStatus_GOOD;
1329 UInt64 cbReturned = 0;
1330 irc = (*ppScsiTaskI)->ExecuteTaskSync(ppScsiTaskI, &SenseData, &TaskStatus, &cbReturned);
1331 AssertBreak(irc == kIOReturnSuccess);
1332 if (pcbBuf)
1333 *pcbBuf = (int32_t)cbReturned;
1334
1335 irc = (*ppScsiTaskI)->GetSCSIServiceResponse(ppScsiTaskI, &ServiceResponse);
1336 AssertBreak(irc == kIOReturnSuccess);
1337 AssertBreak(ServiceResponse == kSCSIServiceResponse_TASK_COMPLETE);
1338
1339 if (TaskStatus == kSCSITaskStatus_GOOD)
1340 rc = VINF_SUCCESS;
1341 else if ( TaskStatus == kSCSITaskStatus_CHECK_CONDITION
1342 && pbSense)
1343 {
1344 memset(pbSense, 0, cbSense); /* lazy */
1345 memcpy(pbSense, &SenseData, RT_MIN(sizeof(SenseData), cbSense));
1346 rc = VERR_UNRESOLVED_ERROR;
1347 }
1348 /** @todo convert sense codes when caller doesn't wish to do this himself. */
1349 /*else if ( TaskStatus == kSCSITaskStatus_CHECK_CONDITION
1350 && SenseData.ADDITIONAL_SENSE_CODE == 0x3A)
1351 rc = VERR_MEDIA_NOT_PRESENT; */
1352 else
1353 {
1354 rc = enmTxDir == PDMBLOCKTXDIR_NONE
1355 ? VERR_DEV_IO_ERROR
1356 : enmTxDir == PDMBLOCKTXDIR_FROM_DEVICE
1357 ? VERR_READ_ERROR
1358 : VERR_WRITE_ERROR;
1359 if (pThis->cLogRelErrors++ < 10)
1360 LogRel(("DVD scsi error: cmd={%.*Rhxs} TaskStatus=%#x key=%#x ASC=%#x ASCQ=%#x (%Rrc)\n",
1361 cbCmd, pbCmd, TaskStatus, SenseData.SENSE_KEY, SenseData.ADDITIONAL_SENSE_CODE,
1362 SenseData.ADDITIONAL_SENSE_CODE_QUALIFIER, rc));
1363 }
1364 } while (0);
1365
1366 (*ppScsiTaskI)->Release(ppScsiTaskI);
1367
1368# elif defined(RT_OS_FREEBSD)
1369 int rc = VINF_SUCCESS;
1370 int rcBSD = 0;
1371 union ccb DeviceCCB;
1372 union ccb *pDeviceCCB = &DeviceCCB;
1373 u_int32_t fFlags;
1374
1375 memset(pDeviceCCB, 0, sizeof(DeviceCCB));
1376 pDeviceCCB->ccb_h.path_id = pThis->ScsiBus;
1377 pDeviceCCB->ccb_h.target_id = pThis->ScsiTargetID;
1378 pDeviceCCB->ccb_h.target_lun = pThis->ScsiLunID;
1379
1380 /* The SCSI INQUIRY command can't be passed through directly. */
1381 if (pbCmd[0] == SCSI_INQUIRY)
1382 {
1383 pDeviceCCB->ccb_h.func_code = XPT_GDEV_TYPE;
1384
1385 rcBSD = ioctl(RTFileToNative(pThis->hFileDevice), CAMIOCOMMAND, pDeviceCCB);
1386 if (!rcBSD)
1387 {
1388 uint32_t cbCopy = cbBuf < sizeof(struct scsi_inquiry_data)
1389 ? cbBuf
1390 : sizeof(struct scsi_inquiry_data);;
1391 memcpy(pvBuf, &pDeviceCCB->cgd.inq_data, cbCopy);
1392 memset(pbSense, 0, cbSense);
1393
1394 if (pcbBuf)
1395 *pcbBuf = cbCopy;
1396 }
1397 else
1398 rc = RTErrConvertFromErrno(errno);
1399 }
1400 else
1401 {
1402 /* Copy the CDB. */
1403 memcpy(&pDeviceCCB->csio.cdb_io.cdb_bytes, pbCmd, cbCmd);
1404
1405 /* Set direction. */
1406 if (enmTxDir == PDMBLOCKTXDIR_NONE)
1407 fFlags = CAM_DIR_NONE;
1408 else if (enmTxDir == PDMBLOCKTXDIR_FROM_DEVICE)
1409 fFlags = CAM_DIR_IN;
1410 else
1411 fFlags = CAM_DIR_OUT;
1412
1413 fFlags |= CAM_DEV_QFRZDIS;
1414
1415 cam_fill_csio(&pDeviceCCB->csio, 1, NULL, fFlags, MSG_SIMPLE_Q_TAG,
1416 (u_int8_t *)pvBuf, cbBuf, cbSense, cbCmd,
1417 cTimeoutMillies ? cTimeoutMillies : 30000/* timeout */);
1418
1419 /* Send command */
1420 rcBSD = ioctl(RTFileToNative(pThis->hFileDevice), CAMIOCOMMAND, pDeviceCCB);
1421 if (!rcBSD)
1422 {
1423 switch (pDeviceCCB->ccb_h.status & CAM_STATUS_MASK)
1424 {
1425 case CAM_REQ_CMP:
1426 rc = VINF_SUCCESS;
1427 break;
1428 case CAM_SEL_TIMEOUT:
1429 rc = VERR_DEV_IO_ERROR;
1430 break;
1431 case CAM_CMD_TIMEOUT:
1432 rc = VERR_TIMEOUT;
1433 break;
1434 default:
1435 rc = VERR_DEV_IO_ERROR;
1436 }
1437
1438 if (pcbBuf)
1439 *pcbBuf = cbBuf - pDeviceCCB->csio.resid;
1440
1441 if (pbSense)
1442 memcpy(pbSense, &pDeviceCCB->csio.sense_data,
1443 cbSense - pDeviceCCB->csio.sense_resid);
1444 }
1445 else
1446 rc = RTErrConvertFromErrno(errno);
1447 }
1448# endif
1449
1450 return rc;
1451}
1452#endif
1453
1454
1455/**
1456 * Media present.
1457 * Query the size and notify the above driver / device.
1458 *
1459 * @param pThis The instance data.
1460 */
1461int DRVHostBaseMediaPresent(PDRVHOSTBASE pThis)
1462{
1463 /*
1464 * Open the drive.
1465 */
1466 int rc = drvHostBaseReopen(pThis);
1467 if (RT_FAILURE(rc))
1468 return rc;
1469
1470 /*
1471 * Determine the size.
1472 */
1473 uint64_t cb;
1474 rc = pThis->pfnGetMediaSize(pThis, &cb);
1475 if (RT_FAILURE(rc))
1476 {
1477 LogFlow(("%s-%d: failed to figure media size of %s, rc=%Rrc\n",
1478 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->pszDevice, rc));
1479 return rc;
1480 }
1481
1482 /*
1483 * Update the data and inform the unit.
1484 */
1485 pThis->cbSize = cb;
1486 pThis->fMediaPresent = true;
1487 if (pThis->pDrvMountNotify)
1488 pThis->pDrvMountNotify->pfnMountNotify(pThis->pDrvMountNotify);
1489 LogFlow(("%s-%d: drvHostBaseMediaPresent: cbSize=%lld (%#llx)\n",
1490 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->cbSize, pThis->cbSize));
1491 return VINF_SUCCESS;
1492}
1493
1494
1495/**
1496 * Media no longer present.
1497 * @param pThis The instance data.
1498 */
1499void DRVHostBaseMediaNotPresent(PDRVHOSTBASE pThis)
1500{
1501 pThis->fMediaPresent = false;
1502 pThis->fLocked = false;
1503 pThis->PCHSGeometry.cCylinders = 0;
1504 pThis->PCHSGeometry.cHeads = 0;
1505 pThis->PCHSGeometry.cSectors = 0;
1506 pThis->LCHSGeometry.cCylinders = 0;
1507 pThis->LCHSGeometry.cHeads = 0;
1508 pThis->LCHSGeometry.cSectors = 0;
1509 if (pThis->pDrvMountNotify)
1510 pThis->pDrvMountNotify->pfnUnmountNotify(pThis->pDrvMountNotify);
1511}
1512
1513
1514#ifdef RT_OS_WINDOWS
1515
1516/**
1517 * Window procedure for the invisible window used to catch the WM_DEVICECHANGE broadcasts.
1518 */
1519static LRESULT CALLBACK DeviceChangeWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1520{
1521 Log2(("DeviceChangeWindowProc: hwnd=%08x uMsg=%08x\n", hwnd, uMsg));
1522 if (uMsg == WM_DESTROY)
1523 {
1524 PDRVHOSTBASE pThis = (PDRVHOSTBASE)GetWindowLongPtr(hwnd, GWLP_USERDATA);
1525 if (pThis)
1526 ASMAtomicXchgSize(&pThis->hwndDeviceChange, NULL);
1527 PostQuitMessage(0);
1528 }
1529
1530 if (uMsg != WM_DEVICECHANGE)
1531 return DefWindowProc(hwnd, uMsg, wParam, lParam);
1532
1533 PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
1534 PDRVHOSTBASE pThis = (PDRVHOSTBASE)GetWindowLongPtr(hwnd, GWLP_USERDATA);
1535 Assert(pThis);
1536 if (pThis == NULL)
1537 return 0;
1538
1539 switch (wParam)
1540 {
1541 case DBT_DEVICEARRIVAL:
1542 case DBT_DEVICEREMOVECOMPLETE:
1543 // Check whether a CD or DVD was inserted into or removed from a drive.
1544 if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
1545 {
1546 PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
1547 if ( (lpdbv->dbcv_flags & DBTF_MEDIA)
1548 && (pThis->fUnitMask & lpdbv->dbcv_unitmask))
1549 {
1550 RTCritSectEnter(&pThis->CritSect);
1551 if (wParam == DBT_DEVICEARRIVAL)
1552 {
1553 int cRetries = 10;
1554 int rc = DRVHostBaseMediaPresent(pThis);
1555 while (RT_FAILURE(rc) && cRetries-- > 0)
1556 {
1557 RTThreadSleep(50);
1558 rc = DRVHostBaseMediaPresent(pThis);
1559 }
1560 }
1561 else
1562 DRVHostBaseMediaNotPresent(pThis);
1563 RTCritSectLeave(&pThis->CritSect);
1564 }
1565 }
1566 break;
1567 }
1568 return TRUE;
1569}
1570
1571#endif /* RT_OS_WINDOWS */
1572
1573
1574/**
1575 * This thread will periodically poll the device for media presence.
1576 *
1577 * @returns Ignored.
1578 * @param ThreadSelf Handle of this thread. Ignored.
1579 * @param pvUser Pointer to the driver instance structure.
1580 */
1581static DECLCALLBACK(int) drvHostBaseMediaThread(RTTHREAD ThreadSelf, void *pvUser)
1582{
1583 PDRVHOSTBASE pThis = (PDRVHOSTBASE)pvUser;
1584 LogFlow(("%s-%d: drvHostBaseMediaThread: ThreadSelf=%p pvUser=%p\n",
1585 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, ThreadSelf, pvUser));
1586#ifdef RT_OS_WINDOWS
1587 static WNDCLASS s_classDeviceChange = {0};
1588 static ATOM s_hAtomDeviceChange = 0;
1589
1590 /*
1591 * Register custom window class.
1592 */
1593 if (s_hAtomDeviceChange == 0)
1594 {
1595 memset(&s_classDeviceChange, 0, sizeof(s_classDeviceChange));
1596 s_classDeviceChange.lpfnWndProc = DeviceChangeWindowProc;
1597 s_classDeviceChange.lpszClassName = "VBOX_DeviceChangeClass";
1598 s_classDeviceChange.hInstance = GetModuleHandle("VBoxDD.dll");
1599 Assert(s_classDeviceChange.hInstance);
1600 s_hAtomDeviceChange = RegisterClassA(&s_classDeviceChange);
1601 Assert(s_hAtomDeviceChange);
1602 }
1603
1604 /*
1605 * Create Window w/ the pThis as user data.
1606 */
1607 HWND hwnd = CreateWindow((LPCTSTR)s_hAtomDeviceChange, "", WS_POPUP, 0, 0, 0, 0, 0, 0, s_classDeviceChange.hInstance, 0);
1608 AssertMsg(hwnd, ("CreateWindow failed with %d\n", GetLastError()));
1609 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
1610
1611 /*
1612 * Signal the waiting EMT thread that everything went fine.
1613 */
1614 ASMAtomicXchgSize(&pThis->hwndDeviceChange, hwnd);
1615 RTThreadUserSignal(ThreadSelf);
1616 if (!hwnd)
1617 {
1618 LogFlow(("%s-%d: drvHostBaseMediaThread: returns VERR_GENERAL_FAILURE\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
1619 return VERR_GENERAL_FAILURE;
1620 }
1621 LogFlow(("%s-%d: drvHostBaseMediaThread: Created hwndDeviceChange=%p\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, hwnd));
1622
1623 /*
1624 * Message pump.
1625 */
1626 MSG Msg;
1627 BOOL fRet;
1628 while ((fRet = GetMessage(&Msg, NULL, 0, 0)) != FALSE)
1629 {
1630 if (fRet != -1)
1631 {
1632 TranslateMessage(&Msg);
1633 DispatchMessage(&Msg);
1634 }
1635 //else: handle the error and possibly exit
1636 }
1637 Assert(!pThis->hwndDeviceChange);
1638
1639#else /* !RT_OS_WINDOWS */
1640 bool fFirst = true;
1641 int cRetries = 10;
1642 while (!pThis->fShutdownPoller)
1643 {
1644 /*
1645 * Perform the polling (unless we've run out of 50ms retries).
1646 */
1647 if ( pThis->pfnPoll
1648 && cRetries-- > 0)
1649 {
1650
1651 int rc = pThis->pfnPoll(pThis);
1652 if (RT_FAILURE(rc))
1653 {
1654 RTSemEventWait(pThis->EventPoller, 50);
1655 continue;
1656 }
1657 }
1658
1659 /*
1660 * Signal EMT after the first go.
1661 */
1662 if (fFirst)
1663 {
1664 RTThreadUserSignal(ThreadSelf);
1665 fFirst = false;
1666 }
1667
1668 /*
1669 * Sleep.
1670 */
1671 int rc = RTSemEventWait(pThis->EventPoller, pThis->cMilliesPoller);
1672 if ( RT_FAILURE(rc)
1673 && rc != VERR_TIMEOUT)
1674 {
1675 AssertMsgFailed(("rc=%Rrc\n", rc));
1676 pThis->ThreadPoller = NIL_RTTHREAD;
1677 LogFlow(("drvHostBaseMediaThread: returns %Rrc\n", rc));
1678 return rc;
1679 }
1680 cRetries = 10;
1681 }
1682
1683#endif /* !RT_OS_WINDOWS */
1684
1685 /* (Don't clear the thread handle here, the destructor thread is using it to wait.) */
1686 LogFlow(("%s-%d: drvHostBaseMediaThread: returns VINF_SUCCESS\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
1687 return VINF_SUCCESS;
1688}
1689
1690/* -=-=-=-=- driver interface -=-=-=-=- */
1691
1692
1693/**
1694 * Done state load operation.
1695 *
1696 * @returns VBox load code.
1697 * @param pDrvIns Driver instance of the driver which registered the data unit.
1698 * @param pSSM SSM operation handle.
1699 */
1700static DECLCALLBACK(int) drvHostBaseLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1701{
1702 PDRVHOSTBASE pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTBASE);
1703 LogFlow(("%s-%d: drvHostBaseMediaThread:\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
1704 RTCritSectEnter(&pThis->CritSect);
1705
1706 /*
1707 * Tell the device/driver above us that the media status is uncertain.
1708 */
1709 if (pThis->pDrvMountNotify)
1710 {
1711 pThis->pDrvMountNotify->pfnUnmountNotify(pThis->pDrvMountNotify);
1712 if (pThis->fMediaPresent)
1713 pThis->pDrvMountNotify->pfnMountNotify(pThis->pDrvMountNotify);
1714 }
1715
1716 RTCritSectLeave(&pThis->CritSect);
1717 return VINF_SUCCESS;
1718}
1719
1720
1721/** @copydoc FNPDMDRVDESTRUCT */
1722DECLCALLBACK(void) DRVHostBaseDestruct(PPDMDRVINS pDrvIns)
1723{
1724 PDRVHOSTBASE pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTBASE);
1725 LogFlow(("%s-%d: drvHostBaseDestruct: iInstance=%d\n", pDrvIns->pReg->szName, pDrvIns->iInstance, pDrvIns->iInstance));
1726
1727 /*
1728 * Terminate the thread.
1729 */
1730 if (pThis->ThreadPoller != NIL_RTTHREAD)
1731 {
1732 pThis->fShutdownPoller = true;
1733 int rc;
1734 int cTimes = 50;
1735 do
1736 {
1737#ifdef RT_OS_WINDOWS
1738 if (pThis->hwndDeviceChange)
1739 PostMessage(pThis->hwndDeviceChange, WM_CLOSE, 0, 0); /* default win proc will destroy the window */
1740#else
1741 RTSemEventSignal(pThis->EventPoller);
1742#endif
1743 rc = RTThreadWait(pThis->ThreadPoller, 100, NULL);
1744 } while (cTimes-- > 0 && rc == VERR_TIMEOUT);
1745
1746 if (!rc)
1747 pThis->ThreadPoller = NIL_RTTHREAD;
1748 }
1749
1750 /*
1751 * Unlock the drive if we've locked it or we're in passthru mode.
1752 */
1753#ifdef RT_OS_DARWIN
1754 if ( ( pThis->fLocked
1755 || pThis->IBlock.pfnSendCmd)
1756 && pThis->ppScsiTaskDI
1757#else /** @todo Check if the other guys can mix pfnDoLock with scsi passthru.
1758 * (We're currently not unlocking the device after use. See todo in DevATA.cpp.) */
1759 if ( pThis->fLocked
1760 && pThis->hFileDevice != NIL_RTFILE
1761#endif
1762 && pThis->pfnDoLock)
1763 {
1764 int rc = pThis->pfnDoLock(pThis, false);
1765 if (RT_SUCCESS(rc))
1766 pThis->fLocked = false;
1767 }
1768
1769 /*
1770 * Cleanup the other resources.
1771 */
1772#ifdef RT_OS_WINDOWS
1773 if (pThis->hwndDeviceChange)
1774 {
1775 if (SetWindowLongPtr(pThis->hwndDeviceChange, GWLP_USERDATA, 0) == (LONG_PTR)pThis)
1776 PostMessage(pThis->hwndDeviceChange, WM_CLOSE, 0, 0); /* default win proc will destroy the window */
1777 pThis->hwndDeviceChange = NULL;
1778 }
1779#else
1780 if (pThis->EventPoller != NULL)
1781 {
1782 RTSemEventDestroy(pThis->EventPoller);
1783 pThis->EventPoller = NULL;
1784 }
1785#endif
1786
1787#ifdef RT_OS_DARWIN
1788 /*
1789 * The unclaiming doesn't seem to mean much, the DVD is actually
1790 * remounted when we release exclusive access. I'm not quite sure
1791 * if I should put the unclaim first or not...
1792 *
1793 * Anyway, that it's automatically remounted very good news for us,
1794 * because that means we don't have to mess with that ourselves. Of
1795 * course there is the unlikely scenario that we've succeeded in claiming
1796 * and umount the DVD but somehow failed to gain exclusive scsi access...
1797 */
1798 if (pThis->ppScsiTaskDI)
1799 {
1800 LogFlow(("%s-%d: releasing exclusive scsi access!\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
1801 (*pThis->ppScsiTaskDI)->ReleaseExclusiveAccess(pThis->ppScsiTaskDI);
1802 (*pThis->ppScsiTaskDI)->Release(pThis->ppScsiTaskDI);
1803 pThis->ppScsiTaskDI = NULL;
1804 }
1805 if (pThis->pDADisk)
1806 {
1807 LogFlow(("%s-%d: unclaiming the disk!\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
1808 DADiskUnclaim(pThis->pDADisk);
1809 CFRelease(pThis->pDADisk);
1810 pThis->pDADisk = NULL;
1811 }
1812 if (pThis->ppMMCDI)
1813 {
1814 LogFlow(("%s-%d: releasing the MMC object!\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
1815 (*pThis->ppMMCDI)->Release(pThis->ppMMCDI);
1816 pThis->ppMMCDI = NULL;
1817 }
1818 if (pThis->MasterPort)
1819 {
1820 mach_port_deallocate(mach_task_self(), pThis->MasterPort);
1821 pThis->MasterPort = NULL;
1822 }
1823 if (pThis->pDASession)
1824 {
1825 LogFlow(("%s-%d: releasing the DA session!\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
1826 CFRelease(pThis->pDASession);
1827 pThis->pDASession = NULL;
1828 }
1829#else
1830 if (pThis->hFileDevice != NIL_RTFILE)
1831 {
1832 int rc = RTFileClose(pThis->hFileDevice);
1833 AssertRC(rc);
1834 pThis->hFileDevice = NIL_RTFILE;
1835 }
1836#endif
1837
1838#ifdef RT_OS_SOLARIS
1839 if (pThis->hFileRawDevice != NIL_RTFILE)
1840 {
1841 int rc = RTFileClose(pThis->hFileRawDevice);
1842 AssertRC(rc);
1843 pThis->hFileRawDevice = NIL_RTFILE;
1844 }
1845
1846 if (pThis->pszRawDeviceOpen)
1847 {
1848 RTStrFree(pThis->pszRawDeviceOpen);
1849 pThis->pszRawDeviceOpen = NULL;
1850 }
1851#endif
1852
1853 if (pThis->pszDevice)
1854 {
1855 MMR3HeapFree(pThis->pszDevice);
1856 pThis->pszDevice = NULL;
1857 }
1858
1859 if (pThis->pszDeviceOpen)
1860 {
1861 RTStrFree(pThis->pszDeviceOpen);
1862 pThis->pszDeviceOpen = NULL;
1863 }
1864
1865 /* Forget about the notifications. */
1866 pThis->pDrvMountNotify = NULL;
1867
1868 /* Leave the instance operational if this is just a cleanup of the state
1869 * after an attach error happened. So don't destroy the critsect then. */
1870 if (!pThis->fKeepInstance && RTCritSectIsInitialized(&pThis->CritSect))
1871 RTCritSectDelete(&pThis->CritSect);
1872 LogFlow(("%s-%d: drvHostBaseDestruct completed\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
1873}
1874
1875
1876/**
1877 * Initializes the instance data (init part 1).
1878 *
1879 * The driver which derives from this base driver will override function pointers after
1880 * calling this method, and complete the construction by calling DRVHostBaseInitFinish().
1881 *
1882 * On failure call DRVHostBaseDestruct().
1883 *
1884 * @returns VBox status code.
1885 * @param pDrvIns Driver instance.
1886 * @param pCfg Configuration handle.
1887 * @param enmType Device type.
1888 */
1889int DRVHostBaseInitData(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, PDMBLOCKTYPE enmType)
1890{
1891 PDRVHOSTBASE pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTBASE);
1892 LogFlow(("%s-%d: DRVHostBaseInitData: iInstance=%d\n", pDrvIns->pReg->szName, pDrvIns->iInstance, pDrvIns->iInstance));
1893
1894 /*
1895 * Initialize most of the data members.
1896 */
1897 pThis->pDrvIns = pDrvIns;
1898 pThis->fKeepInstance = false;
1899 pThis->ThreadPoller = NIL_RTTHREAD;
1900#ifdef RT_OS_DARWIN
1901 pThis->MasterPort = NULL;
1902 pThis->ppMMCDI = NULL;
1903 pThis->ppScsiTaskDI = NULL;
1904 pThis->cbBlock = 0;
1905 pThis->pDADisk = NULL;
1906 pThis->pDASession = NULL;
1907#else
1908 pThis->hFileDevice = NIL_RTFILE;
1909#endif
1910#ifdef RT_OS_SOLARIS
1911 pThis->hFileRawDevice = NIL_RTFILE;
1912#endif
1913 pThis->enmType = enmType;
1914 //pThis->cErrors = 0;
1915 pThis->fAttachFailError = true; /* It's an error until we've read the config. */
1916
1917 pThis->pfnGetMediaSize = drvHostBaseGetMediaSize;
1918
1919 /* IBase. */
1920 pDrvIns->IBase.pfnQueryInterface = drvHostBaseQueryInterface;
1921
1922 /* IBlock. */
1923 pThis->IBlock.pfnRead = drvHostBaseRead;
1924 pThis->IBlock.pfnWrite = drvHostBaseWrite;
1925 pThis->IBlock.pfnFlush = drvHostBaseFlush;
1926 pThis->IBlock.pfnIsReadOnly = drvHostBaseIsReadOnly;
1927 pThis->IBlock.pfnGetSize = drvHostBaseGetSize;
1928 pThis->IBlock.pfnGetType = drvHostBaseGetType;
1929 pThis->IBlock.pfnGetUuid = drvHostBaseGetUuid;
1930 pThis->IBlock.pfnIoBufAlloc = drvHostBaseIoBufAlloc;
1931 pThis->IBlock.pfnIoBufFree = drvHostBaseIoBufFree;
1932
1933 /* IBlockBios. */
1934 pThis->IBlockBios.pfnGetPCHSGeometry = drvHostBaseGetPCHSGeometry;
1935 pThis->IBlockBios.pfnSetPCHSGeometry = drvHostBaseSetPCHSGeometry;
1936 pThis->IBlockBios.pfnGetLCHSGeometry = drvHostBaseGetLCHSGeometry;
1937 pThis->IBlockBios.pfnSetLCHSGeometry = drvHostBaseSetLCHSGeometry;
1938 pThis->IBlockBios.pfnIsVisible = drvHostBaseIsVisible;
1939 pThis->IBlockBios.pfnGetType = drvHostBaseBiosGetType;
1940
1941 /* IMount. */
1942 pThis->IMount.pfnMount = drvHostBaseMount;
1943 pThis->IMount.pfnUnmount = drvHostBaseUnmount;
1944 pThis->IMount.pfnIsMounted = drvHostBaseIsMounted;
1945 pThis->IMount.pfnLock = drvHostBaseLock;
1946 pThis->IMount.pfnUnlock = drvHostBaseUnlock;
1947 pThis->IMount.pfnIsLocked = drvHostBaseIsLocked;
1948
1949 /*
1950 * Get the IBlockPort & IMountNotify interfaces of the above driver/device.
1951 */
1952 pThis->pDrvBlockPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIBLOCKPORT);
1953 if (!pThis->pDrvBlockPort)
1954 {
1955 AssertMsgFailed(("Configuration error: No block port interface above!\n"));
1956 return VERR_PDM_MISSING_INTERFACE_ABOVE;
1957 }
1958 pThis->pDrvMountNotify = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMOUNTNOTIFY);
1959
1960 /*
1961 * Query configuration.
1962 */
1963 /* Device */
1964 int rc = CFGMR3QueryStringAlloc(pCfg, "Path", &pThis->pszDevice);
1965 if (RT_FAILURE(rc))
1966 {
1967 AssertMsgFailed(("Configuration error: query for \"Path\" string returned %Rra.\n", rc));
1968 return rc;
1969 }
1970
1971 /* Mountable */
1972 uint32_t u32;
1973 rc = CFGMR3QueryU32(pCfg, "Interval", &u32);
1974 if (RT_SUCCESS(rc))
1975 pThis->cMilliesPoller = u32;
1976 else if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1977 pThis->cMilliesPoller = 1000;
1978 else if (RT_FAILURE(rc))
1979 {
1980 AssertMsgFailed(("Configuration error: Query \"Mountable\" resulted in %Rrc.\n", rc));
1981 return rc;
1982 }
1983
1984 /* ReadOnly */
1985 rc = CFGMR3QueryBool(pCfg, "ReadOnly", &pThis->fReadOnlyConfig);
1986 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1987 pThis->fReadOnlyConfig = enmType == PDMBLOCKTYPE_DVD || enmType == PDMBLOCKTYPE_CDROM ? true : false;
1988 else if (RT_FAILURE(rc))
1989 {
1990 AssertMsgFailed(("Configuration error: Query \"ReadOnly\" resulted in %Rrc.\n", rc));
1991 return rc;
1992 }
1993
1994 /* Locked */
1995 rc = CFGMR3QueryBool(pCfg, "Locked", &pThis->fLocked);
1996 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1997 pThis->fLocked = false;
1998 else if (RT_FAILURE(rc))
1999 {
2000 AssertMsgFailed(("Configuration error: Query \"Locked\" resulted in %Rrc.\n", rc));
2001 return rc;
2002 }
2003
2004 /* BIOS visible */
2005 rc = CFGMR3QueryBool(pCfg, "BIOSVisible", &pThis->fBiosVisible);
2006 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2007 pThis->fBiosVisible = true;
2008 else if (RT_FAILURE(rc))
2009 {
2010 AssertMsgFailed(("Configuration error: Query \"BIOSVisible\" resulted in %Rrc.\n", rc));
2011 return rc;
2012 }
2013
2014 /* Uuid */
2015 char *psz;
2016 rc = CFGMR3QueryStringAlloc(pCfg, "Uuid", &psz);
2017 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2018 RTUuidClear(&pThis->Uuid);
2019 else if (RT_SUCCESS(rc))
2020 {
2021 rc = RTUuidFromStr(&pThis->Uuid, psz);
2022 if (RT_FAILURE(rc))
2023 {
2024 AssertMsgFailed(("Configuration error: Uuid from string failed on \"%s\", rc=%Rrc.\n", psz, rc));
2025 MMR3HeapFree(psz);
2026 return rc;
2027 }
2028 MMR3HeapFree(psz);
2029 }
2030 else
2031 {
2032 AssertMsgFailed(("Configuration error: Failed to obtain the uuid, rc=%Rrc.\n", rc));
2033 return rc;
2034 }
2035
2036 /* Define whether attach failure is an error (default) or not. */
2037 bool fAttachFailError;
2038 rc = CFGMR3QueryBool(pCfg, "AttachFailError", &fAttachFailError);
2039 if (RT_FAILURE(rc))
2040 fAttachFailError = true;
2041 pThis->fAttachFailError = fAttachFailError;
2042
2043 /* name to open & watch for */
2044#ifdef RT_OS_WINDOWS
2045 int iBit = RT_C_TO_UPPER(pThis->pszDevice[0]) - 'A';
2046 if ( iBit > 'Z' - 'A'
2047 || pThis->pszDevice[1] != ':'
2048 || pThis->pszDevice[2])
2049 {
2050 AssertMsgFailed(("Configuration error: Invalid drive specification: '%s'\n", pThis->pszDevice));
2051 return VERR_INVALID_PARAMETER;
2052 }
2053 pThis->fUnitMask = 1 << iBit;
2054 RTStrAPrintf(&pThis->pszDeviceOpen, "\\\\.\\%s", pThis->pszDevice);
2055
2056#elif defined(RT_OS_SOLARIS)
2057 char *pszBlockDevName = getfullblkname(pThis->pszDevice);
2058 if (!pszBlockDevName)
2059 return VERR_NO_MEMORY;
2060 pThis->pszDeviceOpen = RTStrDup(pszBlockDevName); /* for RTStrFree() */
2061 free(pszBlockDevName);
2062 pThis->pszRawDeviceOpen = RTStrDup(pThis->pszDevice);
2063
2064#else
2065 pThis->pszDeviceOpen = RTStrDup(pThis->pszDevice);
2066#endif
2067
2068 if (!pThis->pszDeviceOpen)
2069 return VERR_NO_MEMORY;
2070
2071 return VINF_SUCCESS;
2072}
2073
2074
2075/**
2076 * Do the 2nd part of the init after the derived driver has overridden the defaults.
2077 *
2078 * On failure call DRVHostBaseDestruct().
2079 *
2080 * @returns VBox status code.
2081 * @param pThis Pointer to the instance data.
2082 */
2083int DRVHostBaseInitFinish(PDRVHOSTBASE pThis)
2084{
2085 int src = VINF_SUCCESS;
2086 PPDMDRVINS pDrvIns = pThis->pDrvIns;
2087
2088 /* log config summary */
2089 Log(("%s-%d: pszDevice='%s' (%s) cMilliesPoller=%d fReadOnlyConfig=%d fLocked=%d fBIOSVisible=%d Uuid=%RTuuid\n",
2090 pDrvIns->pReg->szName, pDrvIns->iInstance, pThis->pszDevice, pThis->pszDeviceOpen, pThis->cMilliesPoller,
2091 pThis->fReadOnlyConfig, pThis->fLocked, pThis->fBiosVisible, &pThis->Uuid));
2092
2093 /*
2094 * Check that there are no drivers below us.
2095 */
2096 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
2097 ("Configuration error: Not possible to attach anything to this driver!\n"),
2098 VERR_PDM_DRVINS_NO_ATTACH);
2099
2100 /*
2101 * Register saved state.
2102 */
2103 int rc = PDMDrvHlpSSMRegisterLoadDone(pDrvIns, drvHostBaseLoadDone);
2104 if (RT_FAILURE(rc))
2105 return rc;
2106
2107 /*
2108 * Verify type.
2109 */
2110#ifdef RT_OS_WINDOWS
2111 UINT uDriveType = GetDriveType(pThis->pszDevice);
2112 switch (pThis->enmType)
2113 {
2114 case PDMBLOCKTYPE_FLOPPY_360:
2115 case PDMBLOCKTYPE_FLOPPY_720:
2116 case PDMBLOCKTYPE_FLOPPY_1_20:
2117 case PDMBLOCKTYPE_FLOPPY_1_44:
2118 case PDMBLOCKTYPE_FLOPPY_2_88:
2119 case PDMBLOCKTYPE_FLOPPY_FAKE_15_6:
2120 case PDMBLOCKTYPE_FLOPPY_FAKE_63_5:
2121 if (uDriveType != DRIVE_REMOVABLE)
2122 {
2123 AssertMsgFailed(("Configuration error: '%s' is not a floppy (type=%d)\n",
2124 pThis->pszDevice, uDriveType));
2125 return VERR_INVALID_PARAMETER;
2126 }
2127 break;
2128 case PDMBLOCKTYPE_CDROM:
2129 case PDMBLOCKTYPE_DVD:
2130 if (uDriveType != DRIVE_CDROM)
2131 {
2132 AssertMsgFailed(("Configuration error: '%s' is not a cdrom (type=%d)\n",
2133 pThis->pszDevice, uDriveType));
2134 return VERR_INVALID_PARAMETER;
2135 }
2136 break;
2137 case PDMBLOCKTYPE_HARD_DISK:
2138 default:
2139 AssertMsgFailed(("enmType=%d\n", pThis->enmType));
2140 return VERR_INVALID_PARAMETER;
2141 }
2142#endif
2143
2144 /*
2145 * Open the device.
2146 */
2147#if defined(RT_OS_DARWIN)
2148 rc = drvHostBaseOpen(pThis, NULL, pThis->fReadOnlyConfig);
2149#else
2150 rc = drvHostBaseReopen(pThis);
2151#endif
2152 if (RT_FAILURE(rc))
2153 {
2154 char *pszDevice = pThis->pszDevice;
2155#ifndef RT_OS_DARWIN
2156 char szPathReal[256];
2157 if ( RTPathExists(pszDevice)
2158 && RT_SUCCESS(RTPathReal(pszDevice, szPathReal, sizeof(szPathReal))))
2159 pszDevice = szPathReal;
2160 pThis->hFileDevice = NIL_RTFILE;
2161#endif
2162#ifdef RT_OS_SOLARIS
2163 pThis->hFileRawDevice = NIL_RTFILE;
2164#endif
2165
2166 /*
2167 * Disable CD/DVD passthrough in case it was enabled. Would cause
2168 * weird failures later when the guest issues commands. These would
2169 * all fail because of the invalid file handle. So use the normal
2170 * virtual CD/DVD code, which deals more gracefully with unavailable
2171 * "media" - actually a complete drive in this case.
2172 */
2173 pThis->IBlock.pfnSendCmd = NULL;
2174 AssertMsgFailed(("Could not open host device %s, rc=%Rrc\n", pszDevice, rc));
2175 switch (rc)
2176 {
2177 case VERR_ACCESS_DENIED:
2178 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
2179#ifdef RT_OS_LINUX
2180 N_("Cannot open host device '%s' for %s access. Check the permissions "
2181 "of that device ('/bin/ls -l %s'): Most probably you need to be member "
2182 "of the device group. Make sure that you logout/login after changing "
2183 "the group settings of the current user"),
2184#else
2185 N_("Cannot open host device '%s' for %s access. Check the permissions "
2186 "of that device"),
2187#endif
2188 pszDevice, pThis->fReadOnlyConfig ? "readonly" : "read/write",
2189 pszDevice);
2190 default:
2191 {
2192 if (pThis->fAttachFailError)
2193 return rc;
2194 int erc = PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/,
2195 "DrvHost_MOUNTFAIL",
2196 N_("Cannot attach to host device '%s'"), pszDevice);
2197 AssertRC(erc);
2198 src = rc;
2199 }
2200 }
2201 }
2202#ifdef RT_OS_WINDOWS
2203 if (RT_SUCCESS(src))
2204 DRVHostBaseMediaPresent(pThis);
2205#endif
2206
2207 /*
2208 * Lock the drive if that's required by the configuration.
2209 */
2210 if (pThis->fLocked)
2211 {
2212 if (pThis->pfnDoLock)
2213 rc = pThis->pfnDoLock(pThis, true);
2214 if (RT_FAILURE(rc))
2215 {
2216 AssertMsgFailed(("Failed to lock the dvd drive. rc=%Rrc\n", rc));
2217 return rc;
2218 }
2219 }
2220
2221#ifndef RT_OS_WINDOWS
2222 if (RT_SUCCESS(src))
2223 {
2224 /*
2225 * Create the event semaphore which the poller thread will wait on.
2226 */
2227 rc = RTSemEventCreate(&pThis->EventPoller);
2228 if (RT_FAILURE(rc))
2229 return rc;
2230 }
2231#endif
2232
2233 /*
2234 * Initialize the critical section used for serializing the access to the media.
2235 */
2236 rc = RTCritSectInit(&pThis->CritSect);
2237 if (RT_FAILURE(rc))
2238 return rc;
2239
2240 if (RT_SUCCESS(src))
2241 {
2242 /*
2243 * Start the thread which will poll for the media.
2244 */
2245 rc = RTThreadCreate(&pThis->ThreadPoller, drvHostBaseMediaThread, pThis, 0,
2246 RTTHREADTYPE_INFREQUENT_POLLER, RTTHREADFLAGS_WAITABLE, "DVDMEDIA");
2247 if (RT_FAILURE(rc))
2248 {
2249 AssertMsgFailed(("Failed to create poller thread. rc=%Rrc\n", rc));
2250 return rc;
2251 }
2252
2253 /*
2254 * Wait for the thread to start up (!w32:) and do one detection loop.
2255 */
2256 rc = RTThreadUserWait(pThis->ThreadPoller, 10000);
2257 AssertRC(rc);
2258#ifdef RT_OS_WINDOWS
2259 if (!pThis->hwndDeviceChange)
2260 return VERR_GENERAL_FAILURE;
2261#endif
2262 }
2263
2264 if (RT_FAILURE(src))
2265 return src;
2266 return rc;
2267}
2268
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