VirtualBox

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

Last change on this file since 23973 was 23973, checked in by vboxsync, 15 years ago

*,RTFileOpen: Fixing RTFileOpen flag misdesign: The deny, access and action flags are mandatory now.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 76.8 KB
Line 
1/* $Id: DrvHostBase.cpp 23973 2009-10-22 12:34:22Z vboxsync $ */
2/** @file
3 * DrvHostBase - Host base drive access driver.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DRV_HOST_BASE
27#ifdef RT_OS_DARWIN
28# include <mach/mach.h>
29# include <Carbon/Carbon.h>
30# include <IOKit/IOKitLib.h>
31# include <IOKit/storage/IOStorageDeviceCharacteristics.h>
32# include <IOKit/scsi/SCSITaskLib.h>
33# include <IOKit/scsi/SCSICommandOperationCodes.h>
34# include <IOKit/IOBSD.h>
35# include <DiskArbitration/DiskArbitration.h>
36# include <mach/mach_error.h>
37# include <VBox/scsi.h>
38
39#elif defined(RT_OS_L4)
40 /* Nothing special requires... yeah, right. */
41
42#elif defined(RT_OS_LINUX)
43# include <sys/ioctl.h>
44# include <sys/fcntl.h>
45# include <errno.h>
46
47#elif defined(RT_OS_SOLARIS)
48# include <fcntl.h>
49# include <errno.h>
50# include <stropts.h>
51# include <malloc.h>
52# include <sys/dkio.h>
53extern "C" char *getfullblkname(char *);
54
55#elif defined(RT_OS_WINDOWS)
56# define WIN32_NO_STATUS
57# include <Windows.h>
58# include <dbt.h>
59# undef WIN32_NO_STATUS
60# include <ntstatus.h>
61
62/* from ntdef.h */
63typedef LONG NTSTATUS;
64
65/* from ntddk.h */
66typedef struct _IO_STATUS_BLOCK {
67 union {
68 NTSTATUS Status;
69 PVOID Pointer;
70 };
71 ULONG_PTR Information;
72} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
73
74
75/* from ntinternals.com */
76typedef enum _FS_INFORMATION_CLASS {
77 FileFsVolumeInformation=1,
78 FileFsLabelInformation,
79 FileFsSizeInformation,
80 FileFsDeviceInformation,
81 FileFsAttributeInformation,
82 FileFsControlInformation,
83 FileFsFullSizeInformation,
84 FileFsObjectIdInformation,
85 FileFsMaximumInformation
86} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS;
87
88typedef struct _FILE_FS_SIZE_INFORMATION {
89 LARGE_INTEGER TotalAllocationUnits;
90 LARGE_INTEGER AvailableAllocationUnits;
91 ULONG SectorsPerAllocationUnit;
92 ULONG BytesPerSector;
93} FILE_FS_SIZE_INFORMATION, *PFILE_FS_SIZE_INFORMATION;
94
95extern "C"
96NTSTATUS __stdcall NtQueryVolumeInformationFile(
97 /*IN*/ HANDLE FileHandle,
98 /*OUT*/ PIO_STATUS_BLOCK IoStatusBlock,
99 /*OUT*/ PVOID FileSystemInformation,
100 /*IN*/ ULONG Length,
101 /*IN*/ FS_INFORMATION_CLASS FileSystemInformationClass );
102
103#elif defined(RT_OS_FREEBSD)
104# include <sys/cdefs.h>
105# include <sys/param.h>
106# include <errno.h>
107# include <stdio.h>
108# include <cam/cam.h>
109# include <cam/cam_ccb.h>
110# include <cam/scsi/scsi_message.h>
111# include <cam/scsi/scsi_pass.h>
112# include <VBox/scsi.h>
113# include <iprt/log.h>
114#else
115# error "Unsupported Platform."
116#endif
117
118#include <VBox/pdmdrv.h>
119#include <iprt/assert.h>
120#include <iprt/file.h>
121#include <iprt/path.h>
122#include <iprt/string.h>
123#include <iprt/thread.h>
124#include <iprt/semaphore.h>
125#include <iprt/uuid.h>
126#include <iprt/asm.h>
127#include <iprt/critsect.h>
128#include <iprt/ctype.h>
129
130#include "DrvHostBase.h"
131
132
133
134
135/* -=-=-=-=- IBlock -=-=-=-=- */
136
137/** @copydoc PDMIBLOCK::pfnRead */
138static DECLCALLBACK(int) drvHostBaseRead(PPDMIBLOCK pInterface, uint64_t off, void *pvBuf, size_t cbRead)
139{
140 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
141 LogFlow(("%s-%d: drvHostBaseRead: off=%#llx pvBuf=%p cbRead=%#x (%s)\n",
142 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, off, pvBuf, cbRead, pThis->pszDevice));
143 RTCritSectEnter(&pThis->CritSect);
144
145 /*
146 * Check the state.
147 */
148 int rc;
149#ifdef RT_OS_DARWIN
150 if ( pThis->fMediaPresent
151 && pThis->ppScsiTaskDI
152 && pThis->cbBlock)
153#elif RT_OS_FREEBSD
154 if ( pThis->fMediaPresent
155 && pThis->cbBlock)
156#else
157 if (pThis->fMediaPresent)
158#endif
159 {
160#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
161 /*
162 * Issue a READ(12) request.
163 */
164 do
165 {
166 const uint32_t LBA = off / pThis->cbBlock;
167 AssertReturn(!(off % pThis->cbBlock), VERR_INVALID_PARAMETER);
168 uint32_t cbRead32 = cbRead > SCSI_MAX_BUFFER_SIZE
169 ? SCSI_MAX_BUFFER_SIZE
170 : (uint32_t)cbRead;
171 const uint32_t cBlocks = cbRead32 / pThis->cbBlock;
172 AssertReturn(!(cbRead % pThis->cbBlock), VERR_INVALID_PARAMETER);
173 uint8_t abCmd[16] =
174 {
175 SCSI_READ_12, 0,
176 RT_BYTE4(LBA), RT_BYTE3(LBA), RT_BYTE2(LBA), RT_BYTE1(LBA),
177 RT_BYTE4(cBlocks), RT_BYTE3(cBlocks), RT_BYTE2(cBlocks), RT_BYTE1(cBlocks),
178 0, 0, 0, 0, 0
179 };
180 rc = DRVHostBaseScsiCmd(pThis, abCmd, 12, PDMBLOCKTXDIR_FROM_DEVICE, pvBuf, &cbRead32, NULL, 0, 0);
181
182 off += cbRead32;
183 cbRead -= cbRead32;
184 pvBuf = (uint8_t *)pvBuf + cbRead32;
185 } while ((cbRead > 0) && RT_SUCCESS(rc));
186
187#else
188 /*
189 * Seek and read.
190 */
191 rc = RTFileSeek(pThis->FileDevice, off, RTFILE_SEEK_BEGIN, NULL);
192 if (RT_SUCCESS(rc))
193 {
194 rc = RTFileRead(pThis->FileDevice, pvBuf, cbRead, NULL);
195 if (RT_SUCCESS(rc))
196 {
197 Log2(("%s-%d: drvHostBaseRead: off=%#llx cbRead=%#x\n"
198 "%16.*Rhxd\n",
199 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, off, cbRead, cbRead, pvBuf));
200 }
201 else
202 Log(("%s-%d: drvHostBaseRead: RTFileRead(%d, %p, %#x) -> %Rrc (off=%#llx '%s')\n",
203 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pThis->FileDevice,
204 pvBuf, cbRead, rc, off, pThis->pszDevice));
205 }
206 else
207 Log(("%s-%d: drvHostBaseRead: RTFileSeek(%d,%#llx,) -> %Rrc\n", pThis->pDrvIns->pDrvReg->szDriverName,
208 pThis->pDrvIns->iInstance, pThis->FileDevice, off, rc));
209#endif
210 }
211 else
212 rc = VERR_MEDIA_NOT_PRESENT;
213
214 RTCritSectLeave(&pThis->CritSect);
215 LogFlow(("%s-%d: drvHostBaseRead: returns %Rrc\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, rc));
216 return rc;
217}
218
219
220/** @copydoc PDMIBLOCK::pfnWrite */
221static DECLCALLBACK(int) drvHostBaseWrite(PPDMIBLOCK pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
222{
223 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
224 LogFlow(("%s-%d: drvHostBaseWrite: off=%#llx pvBuf=%p cbWrite=%#x (%s)\n",
225 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, off, pvBuf, cbWrite, pThis->pszDevice));
226 Log2(("%s-%d: drvHostBaseWrite: off=%#llx cbWrite=%#x\n"
227 "%16.*Rhxd\n",
228 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, off, cbWrite, cbWrite, pvBuf));
229 RTCritSectEnter(&pThis->CritSect);
230
231 /*
232 * Check the state.
233 */
234 int rc;
235 if (!pThis->fReadOnly)
236 {
237 if (pThis->fMediaPresent)
238 {
239#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
240 /** @todo write support... */
241 rc = VERR_WRITE_PROTECT;
242
243#else
244 /*
245 * Seek and write.
246 */
247 rc = RTFileSeek(pThis->FileDevice, off, RTFILE_SEEK_BEGIN, NULL);
248 if (RT_SUCCESS(rc))
249 {
250 rc = RTFileWrite(pThis->FileDevice, pvBuf, cbWrite, NULL);
251 if (RT_FAILURE(rc))
252 Log(("%s-%d: drvHostBaseWrite: RTFileWrite(%d, %p, %#x) -> %Rrc (off=%#llx '%s')\n",
253 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pThis->FileDevice,
254 pvBuf, cbWrite, rc, off, pThis->pszDevice));
255 }
256 else
257 Log(("%s-%d: drvHostBaseWrite: RTFileSeek(%d,%#llx,) -> %Rrc\n",
258 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pThis->FileDevice, off, rc));
259#endif
260 }
261 else
262 rc = VERR_MEDIA_NOT_PRESENT;
263 }
264 else
265 rc = VERR_WRITE_PROTECT;
266
267 RTCritSectLeave(&pThis->CritSect);
268 LogFlow(("%s-%d: drvHostBaseWrite: returns %Rrc\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, rc));
269 return rc;
270}
271
272
273/** @copydoc PDMIBLOCK::pfnFlush */
274static DECLCALLBACK(int) drvHostBaseFlush(PPDMIBLOCK pInterface)
275{
276 int rc;
277 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
278 LogFlow(("%s-%d: drvHostBaseFlush: (%s)\n",
279 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pThis->pszDevice));
280 RTCritSectEnter(&pThis->CritSect);
281
282 if (pThis->fMediaPresent)
283 {
284#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
285 rc = VINF_SUCCESS;
286 /** @todo scsi device buffer flush... */
287#else
288 rc = RTFileFlush(pThis->FileDevice);
289#endif
290 }
291 else
292 rc = VERR_MEDIA_NOT_PRESENT;
293
294 RTCritSectLeave(&pThis->CritSect);
295 LogFlow(("%s-%d: drvHostBaseFlush: returns %Rrc\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, rc));
296 return rc;
297}
298
299
300/** @copydoc PDMIBLOCK::pfnIsReadOnly */
301static DECLCALLBACK(bool) drvHostBaseIsReadOnly(PPDMIBLOCK pInterface)
302{
303 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
304 return pThis->fReadOnly;
305}
306
307
308/** @copydoc PDMIBLOCK::pfnGetSize */
309static DECLCALLBACK(uint64_t) drvHostBaseGetSize(PPDMIBLOCK pInterface)
310{
311 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
312 RTCritSectEnter(&pThis->CritSect);
313
314 uint64_t cb = 0;
315 if (pThis->fMediaPresent)
316 cb = pThis->cbSize;
317
318 RTCritSectLeave(&pThis->CritSect);
319 LogFlow(("%s-%d: drvHostBaseGetSize: returns %llu\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, cb));
320 return cb;
321}
322
323
324/** @copydoc PDMIBLOCK::pfnGetType */
325static DECLCALLBACK(PDMBLOCKTYPE) drvHostBaseGetType(PPDMIBLOCK pInterface)
326{
327 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
328 LogFlow(("%s-%d: drvHostBaseGetType: returns %d\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pThis->enmType));
329 return pThis->enmType;
330}
331
332
333/** @copydoc PDMIBLOCK::pfnGetUuid */
334static DECLCALLBACK(int) drvHostBaseGetUuid(PPDMIBLOCK pInterface, PRTUUID pUuid)
335{
336 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
337
338 *pUuid = pThis->Uuid;
339
340 LogFlow(("%s-%d: drvHostBaseGetUuid: returns VINF_SUCCESS *pUuid=%RTuuid\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pUuid));
341 return VINF_SUCCESS;
342}
343
344
345/* -=-=-=-=- IBlockBios -=-=-=-=- */
346
347/** Makes a PDRVHOSTBASE out of a PPDMIBLOCKBIOS. */
348#define PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface) ( (PDRVHOSTBASE((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTBASE, IBlockBios))) )
349
350
351/** @copydoc PDMIBLOCKBIOS::pfnGetPCHSGeometry */
352static DECLCALLBACK(int) drvHostBaseGetPCHSGeometry(PPDMIBLOCKBIOS pInterface, PPDMMEDIAGEOMETRY pPCHSGeometry)
353{
354 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
355 RTCritSectEnter(&pThis->CritSect);
356
357 int rc = VINF_SUCCESS;
358 if (pThis->fMediaPresent)
359 {
360 if ( pThis->PCHSGeometry.cCylinders > 0
361 && pThis->PCHSGeometry.cHeads > 0
362 && pThis->PCHSGeometry.cSectors > 0)
363 {
364 *pPCHSGeometry = pThis->PCHSGeometry;
365 }
366 else
367 rc = VERR_PDM_GEOMETRY_NOT_SET;
368 }
369 else
370 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
371
372 RTCritSectLeave(&pThis->CritSect);
373 LogFlow(("%s-%d: %s: returns %Rrc CHS={%d,%d,%d}\n",
374 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, __FUNCTION__, rc, pThis->PCHSGeometry.cCylinders, pThis->PCHSGeometry.cHeads, pThis->PCHSGeometry.cSectors));
375 return rc;
376}
377
378
379/** @copydoc PDMIBLOCKBIOS::pfnSetPCHSGeometry */
380static DECLCALLBACK(int) drvHostBaseSetPCHSGeometry(PPDMIBLOCKBIOS pInterface, PCPDMMEDIAGEOMETRY pPCHSGeometry)
381{
382 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
383 LogFlow(("%s-%d: %s: cCylinders=%d cHeads=%d cSectors=%d\n",
384 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, __FUNCTION__, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
385 RTCritSectEnter(&pThis->CritSect);
386
387 int rc = VINF_SUCCESS;
388 if (pThis->fMediaPresent)
389 {
390 pThis->PCHSGeometry = *pPCHSGeometry;
391 }
392 else
393 {
394 AssertMsgFailed(("Invalid state! Not mounted!\n"));
395 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
396 }
397
398 RTCritSectLeave(&pThis->CritSect);
399 return rc;
400}
401
402
403/** @copydoc PDMIBLOCKBIOS::pfnGetLCHSGeometry */
404static DECLCALLBACK(int) drvHostBaseGetLCHSGeometry(PPDMIBLOCKBIOS pInterface, PPDMMEDIAGEOMETRY pLCHSGeometry)
405{
406 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
407 RTCritSectEnter(&pThis->CritSect);
408
409 int rc = VINF_SUCCESS;
410 if (pThis->fMediaPresent)
411 {
412 if ( pThis->LCHSGeometry.cCylinders > 0
413 && pThis->LCHSGeometry.cHeads > 0
414 && pThis->LCHSGeometry.cSectors > 0)
415 {
416 *pLCHSGeometry = pThis->LCHSGeometry;
417 }
418 else
419 rc = VERR_PDM_GEOMETRY_NOT_SET;
420 }
421 else
422 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
423
424 RTCritSectLeave(&pThis->CritSect);
425 LogFlow(("%s-%d: %s: returns %Rrc CHS={%d,%d,%d}\n",
426 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, __FUNCTION__, rc, pThis->LCHSGeometry.cCylinders, pThis->LCHSGeometry.cHeads, pThis->LCHSGeometry.cSectors));
427 return rc;
428}
429
430
431/** @copydoc PDMIBLOCKBIOS::pfnSetLCHSGeometry */
432static DECLCALLBACK(int) drvHostBaseSetLCHSGeometry(PPDMIBLOCKBIOS pInterface, PCPDMMEDIAGEOMETRY pLCHSGeometry)
433{
434 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
435 LogFlow(("%s-%d: %s: cCylinders=%d cHeads=%d cSectors=%d\n",
436 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, __FUNCTION__, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
437 RTCritSectEnter(&pThis->CritSect);
438
439 int rc = VINF_SUCCESS;
440 if (pThis->fMediaPresent)
441 {
442 pThis->LCHSGeometry = *pLCHSGeometry;
443 }
444 else
445 {
446 AssertMsgFailed(("Invalid state! Not mounted!\n"));
447 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
448 }
449
450 RTCritSectLeave(&pThis->CritSect);
451 return rc;
452}
453
454
455/** @copydoc PDMIBLOCKBIOS::pfnIsVisible */
456static DECLCALLBACK(bool) drvHostBaseIsVisible(PPDMIBLOCKBIOS pInterface)
457{
458 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
459 return pThis->fBiosVisible;
460}
461
462
463/** @copydoc PDMIBLOCKBIOS::pfnGetType */
464static DECLCALLBACK(PDMBLOCKTYPE) drvHostBaseBiosGetType(PPDMIBLOCKBIOS pInterface)
465{
466 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
467 return pThis->enmType;
468}
469
470
471
472/* -=-=-=-=- IMount -=-=-=-=- */
473
474/** @copydoc PDMIMOUNT::pfnMount */
475static DECLCALLBACK(int) drvHostBaseMount(PPDMIMOUNT pInterface, const char *pszFilename, const char *pszCoreDriver)
476{
477 /* We're not mountable. */
478 AssertMsgFailed(("drvHostBaseMount: This shouldn't be called!\n"));
479 return VERR_PDM_MEDIA_MOUNTED;
480}
481
482
483/** @copydoc PDMIMOUNT::pfnUnmount */
484static DECLCALLBACK(int) drvHostBaseUnmount(PPDMIMOUNT pInterface, bool fForce)
485{
486 LogFlow(("drvHostBaseUnmount: returns VERR_NOT_SUPPORTED\n"));
487 return VERR_NOT_SUPPORTED;
488}
489
490
491/** @copydoc PDMIMOUNT::pfnIsMounted */
492static DECLCALLBACK(bool) drvHostBaseIsMounted(PPDMIMOUNT pInterface)
493{
494 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
495 RTCritSectEnter(&pThis->CritSect);
496
497 bool fRc = pThis->fMediaPresent;
498
499 RTCritSectLeave(&pThis->CritSect);
500 return fRc;
501}
502
503
504/** @copydoc PDMIMOUNT::pfnIsLocked */
505static DECLCALLBACK(int) drvHostBaseLock(PPDMIMOUNT pInterface)
506{
507 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
508 RTCritSectEnter(&pThis->CritSect);
509
510 int rc = VINF_SUCCESS;
511 if (!pThis->fLocked)
512 {
513 if (pThis->pfnDoLock)
514 rc = pThis->pfnDoLock(pThis, true);
515 if (RT_SUCCESS(rc))
516 pThis->fLocked = true;
517 }
518 else
519 LogFlow(("%s-%d: drvHostBaseLock: already locked\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance));
520
521 RTCritSectLeave(&pThis->CritSect);
522 LogFlow(("%s-%d: drvHostBaseLock: returns %Rrc\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, rc));
523 return rc;
524}
525
526
527/** @copydoc PDMIMOUNT::pfnIsLocked */
528static DECLCALLBACK(int) drvHostBaseUnlock(PPDMIMOUNT pInterface)
529{
530 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
531 RTCritSectEnter(&pThis->CritSect);
532
533 int rc = VINF_SUCCESS;
534 if (pThis->fLocked)
535 {
536 if (pThis->pfnDoLock)
537 rc = pThis->pfnDoLock(pThis, false);
538 if (RT_SUCCESS(rc))
539 pThis->fLocked = false;
540 }
541 else
542 LogFlow(("%s-%d: drvHostBaseUnlock: not locked\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance));
543
544 RTCritSectLeave(&pThis->CritSect);
545 LogFlow(("%s-%d: drvHostBaseUnlock: returns %Rrc\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, rc));
546 return rc;
547}
548
549
550/** @copydoc PDMIMOUNT::pfnIsLocked */
551static DECLCALLBACK(bool) drvHostBaseIsLocked(PPDMIMOUNT pInterface)
552{
553 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
554 RTCritSectEnter(&pThis->CritSect);
555
556 bool fRc = pThis->fLocked;
557
558 RTCritSectLeave(&pThis->CritSect);
559 return fRc;
560}
561
562
563/* -=-=-=-=- IBase -=-=-=-=- */
564
565/** @copydoc PDMIBASE::pfnQueryInterface. */
566static DECLCALLBACK(void *) drvHostBaseQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
567{
568 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
569 PDRVHOSTBASE pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTBASE);
570 switch (enmInterface)
571 {
572 case PDMINTERFACE_BASE:
573 return &pDrvIns->IBase;
574 case PDMINTERFACE_BLOCK:
575 return &pThis->IBlock;
576 case PDMINTERFACE_BLOCK_BIOS:
577 return pThis->fBiosVisible ? &pThis->IBlockBios : NULL;
578 case PDMINTERFACE_MOUNT:
579 return &pThis->IMount;
580 default:
581 return NULL;
582 }
583}
584
585
586/* -=-=-=-=- poller thread -=-=-=-=- */
587
588#ifdef RT_OS_DARWIN
589/** The runloop input source name for the disk arbitration events. */
590#define MY_RUN_LOOP_MODE CFSTR("drvHostBaseDA")
591
592/**
593 * Gets the BSD Name (/dev/disc[0-9]+) for the service.
594 *
595 * This is done by recursing down the I/O registry until we hit upon an entry
596 * with a BSD Name. Usually we find it two levels down. (Further down under
597 * the IOCDPartitionScheme, the volume (slices) BSD Name is found. We don't
598 * seem to have to go this far fortunately.)
599 *
600 * @return VINF_SUCCESS if found, VERR_FILE_NOT_FOUND otherwise.
601 * @param Entry The current I/O registry entry reference.
602 * @param pszName Where to store the name. 128 bytes.
603 * @param cRecursions Number of recursions. This is used as an precation
604 * just to limit the depth and avoid blowing the stack
605 * should we hit a bug or something.
606 */
607static int drvHostBaseGetBSDName(io_registry_entry_t Entry, char *pszName, unsigned cRecursions)
608{
609 int rc = VERR_FILE_NOT_FOUND;
610 io_iterator_t Children = 0;
611 kern_return_t krc = IORegistryEntryGetChildIterator(Entry, kIOServicePlane, &Children);
612 if (krc == KERN_SUCCESS)
613 {
614 io_object_t Child;
615 while ( rc == VERR_FILE_NOT_FOUND
616 && (Child = IOIteratorNext(Children)) != 0)
617 {
618 CFStringRef BSDNameStrRef = (CFStringRef)IORegistryEntryCreateCFProperty(Child, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0);
619 if (BSDNameStrRef)
620 {
621 if (CFStringGetCString(BSDNameStrRef, pszName, 128, kCFStringEncodingUTF8))
622 rc = VINF_SUCCESS;
623 else
624 AssertFailed();
625 CFRelease(BSDNameStrRef);
626 }
627 if (rc == VERR_FILE_NOT_FOUND && cRecursions < 10)
628 rc = drvHostBaseGetBSDName(Child, pszName, cRecursions + 1);
629 IOObjectRelease(Child);
630 }
631 IOObjectRelease(Children);
632 }
633 return rc;
634}
635
636
637/**
638 * Callback notifying us that the async DADiskClaim()/DADiskUnmount call has completed.
639 *
640 * @param DiskRef The disk that was attempted claimed / unmounted.
641 * @param DissenterRef NULL on success, contains details on failure.
642 * @param pvContext Pointer to the return code variable.
643 */
644static void drvHostBaseDADoneCallback(DADiskRef DiskRef, DADissenterRef DissenterRef, void *pvContext)
645{
646 int *prc = (int *)pvContext;
647 if (!DissenterRef)
648 *prc = 0;
649 else
650 *prc = DADissenterGetStatus(DissenterRef) ? DADissenterGetStatus(DissenterRef) : -1;
651 CFRunLoopStop(CFRunLoopGetCurrent());
652}
653
654
655/**
656 * Obtain exclusive access to the DVD device, umount it if necessary.
657 *
658 * @return VBox status code.
659 * @param pThis The driver instance.
660 * @param DVDService The DVD service object.
661 */
662static int drvHostBaseObtainExclusiveAccess(PDRVHOSTBASE pThis, io_object_t DVDService)
663{
664 PPDMDRVINS pDrvIns = pThis->pDrvIns; NOREF(pDrvIns);
665
666 for (unsigned iTry = 0;; iTry++)
667 {
668 IOReturn irc = (*pThis->ppScsiTaskDI)->ObtainExclusiveAccess(pThis->ppScsiTaskDI);
669 if (irc == kIOReturnSuccess)
670 {
671 /*
672 * This is a bit weird, but if we unmounted the DVD drive we also need to
673 * unlock it afterwards or the guest won't be able to eject it later on.
674 */
675 if (pThis->pDADisk)
676 {
677 uint8_t abCmd[16] =
678 {
679 SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL, 0, 0, 0, false, 0,
680 0,0,0,0,0,0,0,0,0,0
681 };
682 DRVHostBaseScsiCmd(pThis, abCmd, 6, PDMBLOCKTXDIR_NONE, NULL, NULL, NULL, 0, 0);
683 }
684 return VINF_SUCCESS;
685 }
686 if (irc == kIOReturnExclusiveAccess)
687 return VERR_SHARING_VIOLATION; /* already used exclusivly. */
688 if (irc != kIOReturnBusy)
689 return VERR_GENERAL_FAILURE; /* not mounted */
690
691 /*
692 * Attempt to the unmount all volumes of the device.
693 * It seems we can can do this all in one go without having to enumerate the
694 * volumes (sessions) and deal with them one by one. This is very fortuitous
695 * as the disk arbitration API is a bit cumbersome to deal with.
696 */
697 if (iTry > 2)
698 return VERR_DRIVE_LOCKED;
699 char szName[128];
700 int rc = drvHostBaseGetBSDName(DVDService, &szName[0], 0);
701 if (RT_SUCCESS(rc))
702 {
703 pThis->pDASession = DASessionCreate(kCFAllocatorDefault);
704 if (pThis->pDASession)
705 {
706 DASessionScheduleWithRunLoop(pThis->pDASession, CFRunLoopGetCurrent(), MY_RUN_LOOP_MODE);
707 pThis->pDADisk = DADiskCreateFromBSDName(kCFAllocatorDefault, pThis->pDASession, szName);
708 if (pThis->pDADisk)
709 {
710 /*
711 * Try claim the device.
712 */
713 Log(("%s-%d: calling DADiskClaim on '%s'.\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance, szName));
714 int rcDA = -2;
715 DADiskClaim(pThis->pDADisk, kDADiskClaimOptionDefault, NULL, NULL, drvHostBaseDADoneCallback, &rcDA);
716 SInt32 rc32 = CFRunLoopRunInMode(MY_RUN_LOOP_MODE, 120.0, FALSE);
717 AssertMsg(rc32 == kCFRunLoopRunStopped, ("rc32=%RI32 (%RX32)\n", rc32, rc32));
718 if ( rc32 == kCFRunLoopRunStopped
719 && !rcDA)
720 {
721 /*
722 * Try unmount the device.
723 */
724 Log(("%s-%d: calling DADiskUnmount on '%s'.\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance, szName));
725 rcDA = -2;
726 DADiskUnmount(pThis->pDADisk, kDADiskUnmountOptionWhole, drvHostBaseDADoneCallback, &rcDA);
727 SInt32 rc32 = CFRunLoopRunInMode(MY_RUN_LOOP_MODE, 120.0, FALSE);
728 AssertMsg(rc32 == kCFRunLoopRunStopped, ("rc32=%RI32 (%RX32)\n", rc32, rc32));
729 if ( rc32 == kCFRunLoopRunStopped
730 && !rcDA)
731 {
732 iTry = 99;
733 DASessionUnscheduleFromRunLoop(pThis->pDASession, CFRunLoopGetCurrent(), MY_RUN_LOOP_MODE);
734 Log(("%s-%d: unmount succeed - retrying.\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance));
735 continue;
736 }
737 Log(("%s-%d: umount => rc32=%d & rcDA=%#x\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance, rc32, rcDA));
738
739 /* failed - cleanup */
740 DADiskUnclaim(pThis->pDADisk);
741 }
742 else
743 Log(("%s-%d: claim => rc32=%d & rcDA=%#x\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance, rc32, rcDA));
744
745 CFRelease(pThis->pDADisk);
746 pThis->pDADisk = NULL;
747 }
748 else
749 Log(("%s-%d: failed to open disk '%s'!\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance, szName));
750
751 DASessionUnscheduleFromRunLoop(pThis->pDASession, CFRunLoopGetCurrent(), MY_RUN_LOOP_MODE);
752 CFRelease(pThis->pDASession);
753 pThis->pDASession = NULL;
754 }
755 else
756 Log(("%s-%d: failed to create DA session!\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance));
757 }
758 RTThreadSleep(10);
759 }
760}
761#endif /* RT_OS_DARWIN */
762
763
764#ifndef RT_OS_SOLARIS
765/**
766 * Wrapper for open / RTFileOpen / IOKit.
767 *
768 * @remark The Darwin code must correspond exactly to the enumeration
769 * done in Main/darwin/iokit.c.
770 */
771static int drvHostBaseOpen(PDRVHOSTBASE pThis, PRTFILE pFileDevice, bool fReadOnly)
772{
773#ifdef RT_OS_DARWIN
774 /* Darwin is kind of special... */
775 Assert(!pFileDevice); NOREF(pFileDevice);
776 Assert(!pThis->cbBlock);
777 Assert(!pThis->MasterPort);
778 Assert(!pThis->ppMMCDI);
779 Assert(!pThis->ppScsiTaskDI);
780
781 /*
782 * Open the master port on the first invocation.
783 */
784 kern_return_t krc = IOMasterPort(MACH_PORT_NULL, &pThis->MasterPort);
785 AssertReturn(krc == KERN_SUCCESS, VERR_GENERAL_FAILURE);
786
787 /*
788 * Create a matching dictionary for searching for DVD services in the IOKit.
789 *
790 * [If I understand this correctly, plain CDROMs doesn't show up as
791 * IODVDServices. Too keep things simple, we will only support DVDs
792 * until somebody complains about it and we get hardware to test it on.
793 * (Unless I'm much mistaken, there aren't any (orignal) intel macs with
794 * plain cdroms.)]
795 */
796 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IODVDServices");
797 AssertReturn(RefMatchingDict, NULL);
798
799 /*
800 * do the search and get a collection of keyboards.
801 */
802 io_iterator_t DVDServices = NULL;
803 IOReturn irc = IOServiceGetMatchingServices(pThis->MasterPort, RefMatchingDict, &DVDServices);
804 AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%d\n", irc), NULL);
805 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
806
807 /*
808 * Enumerate the DVD drives (services).
809 * (This enumeration must be identical to the one performed in DrvHostBase.cpp.)
810 */
811 int rc = VERR_FILE_NOT_FOUND;
812 unsigned i = 0;
813 io_object_t DVDService;
814 while ((DVDService = IOIteratorNext(DVDServices)) != 0)
815 {
816 /*
817 * Get the properties we use to identify the DVD drive.
818 *
819 * While there is a (weird 12 byte) GUID, it isn't persistent
820 * accross boots. So, we have to use a combination of the
821 * vendor name and product name properties with an optional
822 * sequence number for identification.
823 */
824 CFMutableDictionaryRef PropsRef = 0;
825 kern_return_t krc = IORegistryEntryCreateCFProperties(DVDService, &PropsRef, kCFAllocatorDefault, kNilOptions);
826 if (krc == KERN_SUCCESS)
827 {
828 /* Get the Device Characteristics dictionary. */
829 CFDictionaryRef DevCharRef = (CFDictionaryRef)CFDictionaryGetValue(PropsRef, CFSTR(kIOPropertyDeviceCharacteristicsKey));
830 if (DevCharRef)
831 {
832 /* The vendor name. */
833 char szVendor[128];
834 char *pszVendor = &szVendor[0];
835 CFTypeRef ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyVendorNameKey));
836 if ( ValueRef
837 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
838 && CFStringGetCString((CFStringRef)ValueRef, szVendor, sizeof(szVendor), kCFStringEncodingUTF8))
839 pszVendor = RTStrStrip(szVendor);
840 else
841 *pszVendor = '\0';
842
843 /* The product name. */
844 char szProduct[128];
845 char *pszProduct = &szProduct[0];
846 ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyProductNameKey));
847 if ( ValueRef
848 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
849 && CFStringGetCString((CFStringRef)ValueRef, szProduct, sizeof(szProduct), kCFStringEncodingUTF8))
850 pszProduct = RTStrStrip(szProduct);
851 else
852 *pszProduct = '\0';
853
854 /* Construct the two names and compare thwm with the one we're searching for. */
855 char szName1[256 + 32];
856 char szName2[256 + 32];
857 if (*pszVendor || *pszProduct)
858 {
859 if (*pszVendor && *pszProduct)
860 {
861 RTStrPrintf(szName1, sizeof(szName1), "%s %s", pszVendor, pszProduct);
862 RTStrPrintf(szName2, sizeof(szName2), "%s %s (#%u)", pszVendor, pszProduct, i);
863 }
864 else
865 {
866 strcpy(szName1, *pszVendor ? pszVendor : pszProduct);
867 RTStrPrintf(szName2, sizeof(szName2), "%s %s (#%u)", *pszVendor ? pszVendor : pszProduct, i);
868 }
869 }
870 else
871 {
872 RTStrPrintf(szName1, sizeof(szName1), "(#%u)", i);
873 strcpy(szName2, szName1);
874 }
875
876 if ( !strcmp(szName1, pThis->pszDeviceOpen)
877 || !strcmp(szName2, pThis->pszDeviceOpen))
878 {
879 /*
880 * Found it! Now, get the client interface and stuff.
881 * Note that we could also query kIOSCSITaskDeviceUserClientTypeID here if the
882 * MMC client plugin is missing. For now we assume this won't be necessary.
883 */
884 SInt32 Score = 0;
885 IOCFPlugInInterface **ppPlugInInterface = NULL;
886 krc = IOCreatePlugInInterfaceForService(DVDService, kIOMMCDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
887 &ppPlugInInterface, &Score);
888 if (krc == KERN_SUCCESS)
889 {
890 HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
891 CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID),
892 (LPVOID *)&pThis->ppMMCDI);
893 (*ppPlugInInterface)->Release(ppPlugInInterface);
894 ppPlugInInterface = NULL;
895 if (hrc == S_OK)
896 {
897 pThis->ppScsiTaskDI = (*pThis->ppMMCDI)->GetSCSITaskDeviceInterface(pThis->ppMMCDI);
898 if (pThis->ppScsiTaskDI)
899 rc = VINF_SUCCESS;
900 else
901 {
902 LogRel(("GetSCSITaskDeviceInterface failed on '%s'\n", pThis->pszDeviceOpen));
903 rc = VERR_NOT_SUPPORTED;
904 (*pThis->ppMMCDI)->Release(pThis->ppMMCDI);
905 }
906 }
907 else
908 {
909 rc = VERR_GENERAL_FAILURE;//RTErrConvertFromDarwinCOM(krc);
910 pThis->ppMMCDI = NULL;
911 }
912 }
913 else /* Check for kIOSCSITaskDeviceUserClientTypeID? */
914 rc = VERR_GENERAL_FAILURE;//RTErrConvertFromDarwinKern(krc);
915
916 /* Obtain exclusive access to the device so we can send SCSI commands. */
917 if (RT_SUCCESS(rc))
918 rc = drvHostBaseObtainExclusiveAccess(pThis, DVDService);
919
920 /* Cleanup on failure. */
921 if (RT_FAILURE(rc))
922 {
923 if (pThis->ppScsiTaskDI)
924 {
925 (*pThis->ppScsiTaskDI)->Release(pThis->ppScsiTaskDI);
926 pThis->ppScsiTaskDI = NULL;
927 }
928 if (pThis->ppMMCDI)
929 {
930 (*pThis->ppMMCDI)->Release(pThis->ppMMCDI);
931 pThis->ppMMCDI = NULL;
932 }
933 }
934
935 IOObjectRelease(DVDService);
936 break;
937 }
938 }
939 CFRelease(PropsRef);
940 }
941 else
942 AssertMsgFailed(("krc=%#x\n", krc));
943
944 IOObjectRelease(DVDService);
945 i++;
946 }
947
948 IOObjectRelease(DVDServices);
949 return rc;
950
951#elif defined(RT_OS_LINUX)
952 /** @todo we've got RTFILE_O_NON_BLOCK now. Change the code to use RTFileOpen. */
953 int FileDevice = open(pThis->pszDeviceOpen, (pThis->fReadOnlyConfig ? O_RDONLY : O_RDWR) | O_NONBLOCK);
954 if (FileDevice < 0)
955 return RTErrConvertFromErrno(errno);
956 *pFileDevice = FileDevice;
957 return VINF_SUCCESS;
958
959#elif defined(RT_OS_FREEBSD)
960 int rc = VINF_SUCCESS;
961 RTFILE FileDevice;
962
963 rc = RTFileOpen(&FileDevice, pThis->pszDeviceOpen, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
964 if (RT_FAILURE(rc))
965 return rc;
966
967 /*
968 * The current device handle can't passthrough SCSI commands.
969 * We have to get he passthrough device path and open this.
970 */
971 union ccb DeviceCCB;
972 memset(&DeviceCCB, 0, sizeof(DeviceCCB));
973
974 DeviceCCB.ccb_h.func_code = XPT_GDEVLIST;
975 int rcBSD = ioctl(FileDevice, CAMGETPASSTHRU, &DeviceCCB);
976 if (!rcBSD)
977 {
978 char *pszPassthroughDevice = NULL;
979 rc = RTStrAPrintf(&pszPassthroughDevice, "/dev/%s%u",
980 DeviceCCB.cgdl.periph_name, DeviceCCB.cgdl.unit_number);
981 if (RT_SUCCESS(rc))
982 {
983 RTFILE PassthroughDevice;
984
985 rc = RTFileOpen(&PassthroughDevice, pszPassthroughDevice, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
986
987 RTStrFree(pszPassthroughDevice);
988
989 if (RT_SUCCESS(rc))
990 {
991 /* Get needed device parameters. */
992 union ccb DeviceCCB;
993
994 /*
995 * The device path, target id and lun id. Those are
996 * needed for the SCSI passthrough ioctl.
997 */
998 memset(&DeviceCCB, 0, sizeof(DeviceCCB));
999 DeviceCCB.ccb_h.func_code = XPT_GDEVLIST;
1000
1001 rcBSD = ioctl(PassthroughDevice, CAMGETPASSTHRU, &DeviceCCB);
1002 if (!rcBSD)
1003 {
1004 if (DeviceCCB.cgdl.status != CAM_GDEVLIST_ERROR)
1005 {
1006 pThis->ScsiBus = DeviceCCB.ccb_h.path_id;
1007 pThis->ScsiTargetID = DeviceCCB.ccb_h.target_id;
1008 pThis->ScsiLunID = DeviceCCB.ccb_h.target_lun;
1009 *pFileDevice = PassthroughDevice;
1010 }
1011 else
1012 {
1013 /* The passthrough device wasn't found. */
1014 rc = VERR_NOT_FOUND;
1015 }
1016 }
1017 else
1018 rc = RTErrConvertFromErrno(errno);
1019
1020 if (RT_FAILURE(rc))
1021 RTFileClose(PassthroughDevice);
1022 }
1023 }
1024 }
1025 else
1026 rc = RTErrConvertFromErrno(errno);
1027
1028 RTFileClose(FileDevice);
1029 return rc;
1030#else
1031 return RTFileOpen(pFileDevice, pThis->pszDeviceOpen,
1032 (fReadOnly ? RTFILE_O_READ : RTFILE_O_READWRITE) | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
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 seperate 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->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pThis->pszDeviceOpen));
1081
1082 RTFILE FileDevice;
1083#ifdef RT_OS_SOLARIS
1084 if (pThis->FileRawDevice != NIL_RTFILE)
1085 {
1086 RTFileClose(pThis->FileRawDevice);
1087 pThis->FileRawDevice = NIL_RTFILE;
1088 }
1089 if (pThis->FileDevice != NIL_RTFILE)
1090 {
1091 RTFileClose(pThis->FileDevice);
1092 pThis->FileDevice = NIL_RTFILE;
1093 }
1094 RTFILE FileRawDevice;
1095 int rc = drvHostBaseOpen(pThis, &FileDevice, &FileRawDevice, pThis->fReadOnlyConfig);
1096#else
1097 int rc = drvHostBaseOpen(pThis, &FileDevice, 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->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pThis->pszDeviceOpen, rc));
1104#ifdef RT_OS_SOLARIS
1105 rc = drvHostBaseOpen(pThis, &FileDevice, &FileRawDevice, false);
1106#else
1107 rc = drvHostBaseOpen(pThis, &FileDevice, 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->pDrvReg->szDriverName, 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->FileRawDevice != NIL_RTFILE)
1123 RTFileClose(pThis->FileRawDevice);
1124 pThis->FileRawDevice = FileRawDevice;
1125#endif
1126
1127 if (pThis->FileDevice != NIL_RTFILE)
1128 RTFileClose(pThis->FileDevice);
1129 pThis->FileDevice = FileDevice;
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(pThis->FileRawDevice, DKIOCGMEDIAINFO, &MediaInfo) == 0)
1181 {
1182 *pcb = MediaInfo.dki_capacity * (uint64_t)MediaInfo.dki_lbsize;
1183 return VINF_SUCCESS;
1184 }
1185 return RTFileSeek(pThis->FileDevice, 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)pThis->FileDevice, &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)pThis->FileDevice, &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 convertion 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->FileDevice, 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(pThis->FileDevice, 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(pThis->FileDevice, 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->pDrvReg->szDriverName, 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->pDrvReg->szDriverName, 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->pDrvReg->szDriverName, 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->pDrvReg->szDriverName, pThis->pDrvIns->iInstance));
1584 return VERR_GENERAL_FAILURE;
1585 }
1586 LogFlow(("%s-%d: drvHostBaseMediaThread: Created hwndDeviceChange=%p\n", pThis->pDrvIns->pDrvReg->szDriverName, 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->pDrvReg->szDriverName, 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->pDrvReg->szDriverName, 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->pDrvReg->szDriverName, 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->FileDevice != 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 actaully
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->pDrvReg->szDriverName, 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->pDrvReg->szDriverName, 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->pDrvReg->szDriverName, 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->pDrvReg->szDriverName, pDrvIns->iInstance));
1791 CFRelease(pThis->pDASession);
1792 pThis->pDASession = NULL;
1793 }
1794#else
1795 if (pThis->FileDevice != NIL_RTFILE)
1796 {
1797 int rc = RTFileClose(pThis->FileDevice);
1798 AssertRC(rc);
1799 pThis->FileDevice = NIL_RTFILE;
1800 }
1801#endif
1802
1803#ifdef RT_OS_SOLARIS
1804 if (pThis->FileRawDevice != NIL_RTFILE)
1805 {
1806 int rc = RTFileClose(pThis->FileRawDevice);
1807 AssertRC(rc);
1808 pThis->FileRawDevice = 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 destry the critsect then. */
1835 if (!pThis->fKeepInstance && RTCritSectIsInitialized(&pThis->CritSect))
1836 RTCritSectDelete(&pThis->CritSect);
1837 LogFlow(("%s-%d: drvHostBaseDestruct completed\n", pDrvIns->pDrvReg->szDriverName, 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 pCfgHandle Configuration handle.
1852 * @param enmType Device type.
1853 */
1854int DRVHostBaseInitData(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, PDMBLOCKTYPE enmType)
1855{
1856 PDRVHOSTBASE pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTBASE);
1857 LogFlow(("%s-%d: DRVHostBaseInitData: iInstance=%d\n", pDrvIns->pDrvReg->szDriverName, 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->FileDevice = NIL_RTFILE;
1874#endif
1875#ifdef RT_OS_SOLARIS
1876 pThis->FileRawDevice = 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 = (PPDMIBLOCKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_BLOCK_PORT);
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 = (PPDMIMOUNTNOTIFY)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_MOUNT_NOTIFY);
1921
1922 /*
1923 * Query configuration.
1924 */
1925 /* Device */
1926 int rc = CFGMR3QueryStringAlloc(pCfgHandle, "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(pCfgHandle, "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(pCfgHandle, "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(pCfgHandle, "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(pCfgHandle, "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(pCfgHandle, "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(pCfgHandle, "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 = toupper(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->pDrvReg->szDriverName, 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->FileDevice = NIL_RTFILE;
2121#endif
2122#ifdef RT_OS_SOLARIS
2123 pThis->FileRawDevice = 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