VirtualBox

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

Last change on this file since 39417 was 37780, checked in by vboxsync, 13 years ago

Storage/DrvHostBase: add generic unmount implementation, so that host floppy unmount works
Main/Console: fix swapped error messages for medium change

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