VirtualBox

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

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

PDM: s/pCfgHandle/pCfg/g - part 2.

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