VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvHostDVD.cpp@ 12767

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

attempt to fix Darwin passthrough

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.4 KB
Line 
1/* $Id: DrvHostDVD.cpp 12767 2008-09-26 13:00:29Z vboxsync $ */
2/** @file
3 * DrvHostDVD - Host DVD block 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_DVD
27#define __STDC_LIMIT_MACROS
28#define __STDC_CONSTANT_MACROS
29#ifdef RT_OS_DARWIN
30# include <mach/mach.h>
31# include <Carbon/Carbon.h>
32# include <IOKit/IOKitLib.h>
33# include <IOKit/IOCFPlugIn.h>
34# include <IOKit/scsi-commands/SCSITaskLib.h>
35# include <IOKit/scsi-commands/SCSICommandOperationCodes.h>
36# include <IOKit/storage/IOStorageDeviceCharacteristics.h>
37# include <mach/mach_error.h>
38# define USE_MEDIA_POLLING
39
40#elif defined(RT_OS_L4)
41/* nothing (yet). */
42
43#elif defined RT_OS_LINUX
44# include <sys/ioctl.h>
45/* This is a hack to work around conflicts between these linux kernel headers
46 * and the GLIBC tcpip headers. They have different declarations of the 4
47 * standard byte order functions. */
48# define _LINUX_BYTEORDER_GENERIC_H
49/* This is another hack for not bothering with C++ unfriendly byteswap macros. */
50# define _LINUX_BYTEORDER_SWAB_H
51/* Those macros that are needed are defined in the header below */
52# include "swab.h"
53# include <linux/cdrom.h>
54# include <sys/fcntl.h>
55# include <errno.h>
56# include <limits.h>
57# define USE_MEDIA_POLLING
58
59#elif defined(RT_OS_SOLARIS)
60# include <stropts.h>
61# include <fcntl.h>
62# include <ctype.h>
63# include <errno.h>
64# include <pwd.h>
65# include <unistd.h>
66# include <syslog.h>
67# ifdef VBOX_WITH_SUID_WRAPPER
68# include <auth_attr.h>
69# endif
70# include <sys/dkio.h>
71# include <sys/sockio.h>
72# include <sys/scsi/scsi.h>
73# define USE_MEDIA_POLLING
74
75#elif defined(RT_OS_WINDOWS)
76# include <Windows.h>
77# include <winioctl.h>
78# include <ntddscsi.h>
79# undef USE_MEDIA_POLLING
80
81#else
82# error "Unsupported Platform."
83#endif
84
85#include <VBox/pdmdrv.h>
86#include <iprt/assert.h>
87#include <iprt/file.h>
88#include <iprt/string.h>
89#include <iprt/thread.h>
90#include <iprt/critsect.h>
91#include <VBox/scsi.h>
92
93#include "Builtins.h"
94#include "DrvHostBase.h"
95
96
97/* Forward declarations. */
98
99static DECLCALLBACK(int) drvHostDvdDoLock(PDRVHOSTBASE pThis, bool fLock);
100#ifdef VBOX_WITH_SUID_WRAPPER
101static int solarisCheckUserAuth();
102static int solarisEnterRootMode(uid_t *pEffUserID);
103static int solarisExitRootMode(uid_t *pEffUserID);
104#endif
105
106
107/** @copydoc PDMIMOUNT::pfnUnmount */
108static DECLCALLBACK(int) drvHostDvdUnmount(PPDMIMOUNT pInterface, bool fForce)
109{
110 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
111 RTCritSectEnter(&pThis->CritSect);
112
113 /*
114 * Validate state.
115 */
116 int rc = VINF_SUCCESS;
117 if (!pThis->fLocked || fForce)
118 {
119 /* Unlock drive if necessary. */
120 if (pThis->fLocked)
121 drvHostDvdDoLock(pThis, false);
122
123 /*
124 * Eject the disc.
125 */
126#ifdef RT_OS_DARWIN
127 uint8_t abCmd[16] =
128 {
129 SCSI_START_STOP_UNIT, 0, 0, 0, 2 /*eject+stop*/, 0,
130 0,0,0,0,0,0,0,0,0,0
131 };
132 rc = DRVHostBaseScsiCmd(pThis, abCmd, 6, PDMBLOCKTXDIR_NONE, NULL, NULL, NULL, 0, 0);
133
134#elif defined(RT_OS_LINUX)
135 rc = ioctl(pThis->FileDevice, CDROMEJECT, 0);
136 if (rc < 0)
137 {
138 if (errno == EBUSY)
139 rc = VERR_PDM_MEDIA_LOCKED;
140 else if (errno == ENOSYS)
141 rc = VERR_NOT_SUPPORTED;
142 else
143 rc = RTErrConvertFromErrno(errno);
144 }
145
146#elif defined(RT_OS_SOLARIS)
147 rc = ioctl(pThis->FileRawDevice, DKIOCEJECT, 0);
148 if (rc < 0)
149 {
150 if (errno == EBUSY)
151 rc = VERR_PDM_MEDIA_LOCKED;
152 else if (errno == ENOSYS || errno == ENOTSUP)
153 rc = VERR_NOT_SUPPORTED;
154 else if (errno == ENODEV)
155 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
156 else
157 rc = RTErrConvertFromErrno(errno);
158 }
159
160#elif defined(RT_OS_WINDOWS)
161 RTFILE FileDevice = pThis->FileDevice;
162 if (FileDevice == NIL_RTFILE) /* obsolete crap */
163 rc = RTFileOpen(&FileDevice, pThis->pszDeviceOpen, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
164 if (RT_SUCCESS(rc))
165 {
166 /* do ioctl */
167 DWORD cbReturned;
168 if (DeviceIoControl((HANDLE)FileDevice, IOCTL_STORAGE_EJECT_MEDIA,
169 NULL, 0,
170 NULL, 0, &cbReturned,
171 NULL))
172 rc = VINF_SUCCESS;
173 else
174 rc = RTErrConvertFromWin32(GetLastError());
175
176 /* clean up handle */
177 if (FileDevice != pThis->FileDevice)
178 RTFileClose(FileDevice);
179 }
180 else
181 AssertMsgFailed(("Failed to open '%s' for ejecting this tray.\n", rc));
182
183
184#else
185 AssertMsgFailed(("Eject is not implemented!\n"));
186 rc = VINF_SUCCESS;
187#endif
188
189 /*
190 * Media is no longer present.
191 */
192 DRVHostBaseMediaNotPresent(pThis); /** @todo This isn't thread safe! */
193 }
194 else
195 {
196 Log(("drvHostDvdUnmount: Locked\n"));
197 rc = VERR_PDM_MEDIA_LOCKED;
198 }
199
200 RTCritSectLeave(&pThis->CritSect);
201 LogFlow(("drvHostDvdUnmount: returns %Rrc\n", rc));
202 return rc;
203}
204
205
206/**
207 * Locks or unlocks the drive.
208 *
209 * @returns VBox status code.
210 * @param pThis The instance data.
211 * @param fLock True if the request is to lock the drive, false if to unlock.
212 */
213static DECLCALLBACK(int) drvHostDvdDoLock(PDRVHOSTBASE pThis, bool fLock)
214{
215#ifdef RT_OS_DARWIN
216 uint8_t abCmd[16] =
217 {
218 SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL, 0, 0, 0, fLock, 0,
219 0,0,0,0,0,0,0,0,0,0
220 };
221 int rc = DRVHostBaseScsiCmd(pThis, abCmd, 6, PDMBLOCKTXDIR_NONE, NULL, NULL, NULL, 0, 0);
222
223#elif defined(RT_OS_LINUX)
224 int rc = ioctl(pThis->FileDevice, CDROM_LOCKDOOR, (int)fLock);
225 if (rc < 0)
226 {
227 if (errno == EBUSY)
228 rc = VERR_ACCESS_DENIED;
229 else if (errno == EDRIVE_CANT_DO_THIS)
230 rc = VERR_NOT_SUPPORTED;
231 else
232 rc = RTErrConvertFromErrno(errno);
233 }
234
235#elif defined(RT_OS_SOLARIS)
236 int rc = ioctl(pThis->FileRawDevice, fLock ? DKIOCLOCK : DKIOCUNLOCK, 0);
237 if (rc < 0)
238 {
239 if (errno == EBUSY)
240 rc = VERR_ACCESS_DENIED;
241 else if (errno == ENOTSUP || errno == ENOSYS)
242 rc = VERR_NOT_SUPPORTED;
243 else
244 rc = RTErrConvertFromErrno(errno);
245 }
246
247#elif defined(RT_OS_WINDOWS)
248
249 PREVENT_MEDIA_REMOVAL PreventMediaRemoval = {fLock};
250 DWORD cbReturned;
251 int rc;
252 if (DeviceIoControl((HANDLE)pThis->FileDevice, IOCTL_STORAGE_MEDIA_REMOVAL,
253 &PreventMediaRemoval, sizeof(PreventMediaRemoval),
254 NULL, 0, &cbReturned,
255 NULL))
256 rc = VINF_SUCCESS;
257 else
258 /** @todo figure out the return codes for already locked. */
259 rc = RTErrConvertFromWin32(GetLastError());
260
261#else
262 AssertMsgFailed(("Lock/Unlock is not implemented!\n"));
263 int rc = VINF_SUCCESS;
264
265#endif
266
267 LogFlow(("drvHostDvdDoLock(, fLock=%RTbool): returns %Rrc\n", fLock, rc));
268 return rc;
269}
270
271
272
273#ifdef RT_OS_LINUX
274/**
275 * Get the media size.
276 *
277 * @returns VBox status code.
278 * @param pThis The instance data.
279 * @param pcb Where to store the size.
280 */
281static int drvHostDvdGetMediaSize(PDRVHOSTBASE pThis, uint64_t *pcb)
282{
283 /*
284 * Query the media size.
285 */
286 /* Clear the media-changed-since-last-call-thingy just to be on the safe side. */
287 ioctl(pThis->FileDevice, CDROM_MEDIA_CHANGED, CDSL_CURRENT);
288 return RTFileSeek(pThis->FileDevice, 0, RTFILE_SEEK_END, pcb);
289
290}
291#endif /* RT_OS_LINUX */
292
293
294#ifdef USE_MEDIA_POLLING
295/**
296 * Do media change polling.
297 */
298DECLCALLBACK(int) drvHostDvdPoll(PDRVHOSTBASE pThis)
299{
300 /*
301 * Poll for media change.
302 */
303#ifdef RT_OS_DARWIN
304 AssertReturn(pThis->ppScsiTaskDI, VERR_INTERNAL_ERROR);
305
306 /*
307 * Issue a TEST UNIT READY request.
308 */
309 bool fMediaChanged = false;
310 bool fMediaPresent = false;
311 uint8_t abCmd[16] = { SCSI_TEST_UNIT_READY, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
312 uint8_t abSense[32];
313 int rc2 = DRVHostBaseScsiCmd(pThis, abCmd, 6, PDMBLOCKTXDIR_NONE, NULL, NULL, abSense, sizeof(abSense), 0);
314 if (RT_SUCCESS(rc2))
315 fMediaPresent = true;
316 else if ( rc2 == VERR_UNRESOLVED_ERROR
317 && abSense[2] == 6 /* unit attention */
318 && ( (abSense[12] == 0x29 && abSense[13] < 5 /* reset */)
319 || (abSense[12] == 0x2a && abSense[13] == 0 /* parameters changed */) //???
320 || (abSense[12] == 0x3f && abSense[13] == 0 /* target operating conditions have changed */) //???
321 || (abSense[12] == 0x3f && abSense[13] == 2 /* changed operating definition */) //???
322 || (abSense[12] == 0x3f && abSense[13] == 3 /* inquery parameters changed */)
323 || (abSense[12] == 0x3f && abSense[13] == 5 /* device identifier changed */)
324 )
325 )
326 {
327 fMediaPresent = false;
328 fMediaChanged = true;
329 /** @todo check this media chance stuff on Darwin. */
330 }
331
332#elif defined(RT_OS_LINUX)
333 bool fMediaPresent = ioctl(pThis->FileDevice, CDROM_DRIVE_STATUS, CDSL_CURRENT) == CDS_DISC_OK;
334
335#elif defined(RT_OS_SOLARIS)
336 bool fMediaPresent = false;
337 bool fMediaChanged = false;
338
339 /* Need to pass the previous state and DKIO_NONE for the first time. */
340 static dkio_state s_DeviceState = DKIO_NONE;
341 dkio_state PreviousState = s_DeviceState;
342 int rc2 = ioctl(pThis->FileRawDevice, DKIOCSTATE, &s_DeviceState);
343 if (rc2 == 0)
344 {
345 fMediaPresent = (s_DeviceState == DKIO_INSERTED);
346 if (PreviousState != s_DeviceState)
347 fMediaChanged = true;
348 }
349 else
350 fMediaChanged = true;
351
352#else
353# error "Unsupported platform."
354#endif
355
356 RTCritSectEnter(&pThis->CritSect);
357
358 int rc = VINF_SUCCESS;
359 if (pThis->fMediaPresent != fMediaPresent)
360 {
361 LogFlow(("drvHostDvdPoll: %d -> %d\n", pThis->fMediaPresent, fMediaPresent));
362 pThis->fMediaPresent = false;
363 if (fMediaPresent)
364 rc = DRVHostBaseMediaPresent(pThis);
365 else
366 DRVHostBaseMediaNotPresent(pThis);
367 }
368 else if (fMediaPresent)
369 {
370 /*
371 * Poll for media change.
372 */
373#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
374 /* taken care of above. */
375#elif defined(RT_OS_LINUX)
376 bool fMediaChanged = ioctl(pThis->FileDevice, CDROM_MEDIA_CHANGED, CDSL_CURRENT) == 1;
377#else
378# error "Unsupported platform."
379#endif
380 if (fMediaChanged)
381 {
382 LogFlow(("drvHostDVDMediaThread: Media changed!\n"));
383 DRVHostBaseMediaNotPresent(pThis);
384 rc = DRVHostBaseMediaPresent(pThis);
385 }
386 }
387
388 RTCritSectLeave(&pThis->CritSect);
389 return rc;
390}
391#endif /* USE_MEDIA_POLLING */
392
393
394/** @copydoc PDMIBLOCK::pfnSendCmd */
395static int drvHostDvdSendCmd(PPDMIBLOCK pInterface, const uint8_t *pbCmd,
396 PDMBLOCKTXDIR enmTxDir, void *pvBuf, size_t *pcbBuf,
397 uint8_t *pabSense, size_t cbSense, uint32_t cTimeoutMillies)
398{
399 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
400 int rc;
401 LogFlow(("%s: cmd[0]=%#04x txdir=%d pcbBuf=%d timeout=%d\n", __FUNCTION__, pbCmd[0], enmTxDir, *pcbBuf, cTimeoutMillies));
402
403#ifdef RT_OS_DARWIN
404 /*
405 * Pass the request on to the internal scsi command interface.
406 * The command seems to be 12 bytes long, the docs a bit copy&pasty on the command length point...
407 */
408 if (enmTxDir == PDMBLOCKTXDIR_FROM_DEVICE)
409 memset(pvBuf, '\0', *pcbBuf); /* we got read size, but zero it anyway. */
410 rc = DRVHostBaseScsiCmd(pThis, pbCmd, 12, PDMBLOCKTXDIR_FROM_DEVICE, pvBuf, pcbBuf, pabSense, cbSense, cTimeoutMillies);
411 if (rc == VERR_UNRESOLVED_ERROR)
412 /* sense information set */
413 rc = VERR_DEV_IO_ERROR;
414
415#elif defined(RT_OS_L4)
416 /* Not really ported to L4 yet. */
417 rc = VERR_INTERNAL_ERROR;
418
419#elif defined(RT_OS_LINUX)
420 int direction;
421 struct cdrom_generic_command cgc;
422
423 switch (enmTxDir)
424 {
425 case PDMBLOCKTXDIR_NONE:
426 Assert(*pcbBuf == 0);
427 direction = CGC_DATA_NONE;
428 break;
429 case PDMBLOCKTXDIR_FROM_DEVICE:
430 Assert(*pcbBuf != 0);
431 /* Make sure that the buffer is clear for commands reading
432 * data. The actually received data may be shorter than what
433 * we expect, and due to the unreliable feedback about how much
434 * data the ioctl actually transferred, it's impossible to
435 * prevent that. Returning previous buffer contents may cause
436 * security problems inside the guest OS, if users can issue
437 * commands to the CDROM device. */
438 memset(pvBuf, '\0', *pcbBuf);
439 direction = CGC_DATA_READ;
440 break;
441 case PDMBLOCKTXDIR_TO_DEVICE:
442 Assert(*pcbBuf != 0);
443 direction = CGC_DATA_WRITE;
444 break;
445 default:
446 AssertMsgFailed(("enmTxDir invalid!\n"));
447 direction = CGC_DATA_NONE;
448 }
449 memset(&cgc, '\0', sizeof(cgc));
450 memcpy(cgc.cmd, pbCmd, CDROM_PACKET_SIZE);
451 cgc.buffer = (unsigned char *)pvBuf;
452 cgc.buflen = *pcbBuf;
453 cgc.stat = 0;
454 Assert(cbSense >= sizeof(struct request_sense));
455 cgc.sense = (struct request_sense *)pabSense;
456 cgc.data_direction = direction;
457 cgc.quiet = false;
458 cgc.timeout = cTimeoutMillies;
459 rc = ioctl(pThis->FileDevice, CDROM_SEND_PACKET, &cgc);
460 if (rc < 0)
461 {
462 if (errno == EBUSY)
463 rc = VERR_PDM_MEDIA_LOCKED;
464 else if (errno == ENOSYS)
465 rc = VERR_NOT_SUPPORTED;
466 else
467 {
468 rc = RTErrConvertFromErrno(errno);
469 if (rc == VERR_ACCESS_DENIED && cgc.sense->sense_key == SCSI_SENSE_NONE)
470 cgc.sense->sense_key = SCSI_SENSE_ILLEGAL_REQUEST;
471 Log2(("%s: error status %d, rc=%Rrc\n", __FUNCTION__, cgc.stat, rc));
472 }
473 }
474 Log2(("%s: after ioctl: cgc.buflen=%d txlen=%d\n", __FUNCTION__, cgc.buflen, *pcbBuf));
475 /* The value of cgc.buflen does not reliably reflect the actual amount
476 * of data transferred (for packet commands with little data transfer
477 * it's 0). So just assume that everything worked ok. */
478
479#elif defined(RT_OS_SOLARIS)
480 struct uscsi_cmd usc;
481 union scsi_cdb scdb;
482 memset(&usc, 0, sizeof(struct uscsi_cmd));
483 memset(&scdb, 0, sizeof(scdb));
484
485 switch (enmTxDir)
486 {
487 case PDMBLOCKTXDIR_NONE:
488 Assert(*pcbBuf == 0);
489 usc.uscsi_flags = USCSI_READ;
490 /* nothing to do */
491 break;
492
493 case PDMBLOCKTXDIR_FROM_DEVICE:
494 Assert(*pcbBuf != 0);
495 /* Make sure that the buffer is clear for commands reading
496 * data. The actually received data may be shorter than what
497 * we expect, and due to the unreliable feedback about how much
498 * data the ioctl actually transferred, it's impossible to
499 * prevent that. Returning previous buffer contents may cause
500 * security problems inside the guest OS, if users can issue
501 * commands to the CDROM device. */
502 memset(pvBuf, '\0', *pcbBuf);
503 usc.uscsi_flags = USCSI_READ;
504 break;
505 case PDMBLOCKTXDIR_TO_DEVICE:
506 Assert(*pcbBuf != 0);
507 usc.uscsi_flags = USCSI_WRITE;
508 break;
509 default:
510 AssertMsgFailedReturn(("%d\n", enmTxDir), VERR_INTERNAL_ERROR);
511 }
512 usc.uscsi_flags |= USCSI_RQENABLE;
513 usc.uscsi_rqbuf = (char *)pabSense;
514 usc.uscsi_rqlen = cbSense;
515 usc.uscsi_cdb = (caddr_t)&scdb;
516 usc.uscsi_cdblen = 12;
517 memcpy (usc.uscsi_cdb, pbCmd, usc.uscsi_cdblen);
518 usc.uscsi_bufaddr = (caddr_t)pvBuf;
519 usc.uscsi_buflen = *pcbBuf;
520 usc.uscsi_timeout = (cTimeoutMillies + 999) / 1000;
521
522 /* We need root privileges for user-SCSI under Solaris. */
523#ifdef VBOX_WITH_SUID_WRAPPER
524 uid_t effUserID = geteuid();
525 solarisEnterRootMode(&effUserID); /** @todo check return code when this really works. */
526#endif
527 rc = ioctl(pThis->FileRawDevice, USCSICMD, &usc);
528#ifdef VBOX_WITH_SUID_WRAPPER
529 solarisExitRootMode(&effUserID);
530#endif
531 if (rc < 0)
532 {
533 if (errno == EPERM)
534 return VERR_PERMISSION_DENIED;
535 if (usc.uscsi_status)
536 {
537 rc = RTErrConvertFromErrno(errno);
538 Log2(("%s: error status. rc=%Rrc\n", __FUNCTION__, rc));
539 }
540 }
541 Log2(("%s: after ioctl: residual buflen=%d original buflen=%d\n", __FUNCTION__, usc.uscsi_resid, usc.uscsi_buflen));
542
543#elif defined(RT_OS_WINDOWS)
544 int direction;
545 struct _REQ
546 {
547 SCSI_PASS_THROUGH_DIRECT spt;
548 uint8_t aSense[64];
549 } Req;
550 DWORD cbReturned = 0;
551
552 switch (enmTxDir)
553 {
554 case PDMBLOCKTXDIR_NONE:
555 direction = SCSI_IOCTL_DATA_UNSPECIFIED;
556 break;
557 case PDMBLOCKTXDIR_FROM_DEVICE:
558 Assert(*pcbBuf != 0);
559 /* Make sure that the buffer is clear for commands reading
560 * data. The actually received data may be shorter than what
561 * we expect, and due to the unreliable feedback about how much
562 * data the ioctl actually transferred, it's impossible to
563 * prevent that. Returning previous buffer contents may cause
564 * security problems inside the guest OS, if users can issue
565 * commands to the CDROM device. */
566 memset(pvBuf, '\0', *pcbBuf);
567 direction = SCSI_IOCTL_DATA_IN;
568 break;
569 case PDMBLOCKTXDIR_TO_DEVICE:
570 direction = SCSI_IOCTL_DATA_OUT;
571 break;
572 default:
573 AssertMsgFailed(("enmTxDir invalid!\n"));
574 direction = SCSI_IOCTL_DATA_UNSPECIFIED;
575 }
576 memset(&Req, '\0', sizeof(Req));
577 Req.spt.Length = sizeof(Req.spt);
578 Req.spt.CdbLength = 12;
579 memcpy(Req.spt.Cdb, pbCmd, Req.spt.CdbLength);
580 Req.spt.DataBuffer = pvBuf;
581 Req.spt.DataTransferLength = *pcbBuf;
582 Req.spt.DataIn = direction;
583 Req.spt.TimeOutValue = (cTimeoutMillies + 999) / 1000; /* Convert to seconds */
584 Assert(cbSense <= sizeof(Req.aSense));
585 Req.spt.SenseInfoLength = cbSense;
586 Req.spt.SenseInfoOffset = RT_OFFSETOF(struct _REQ, aSense);
587 if (DeviceIoControl((HANDLE)pThis->FileDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT,
588 &Req, sizeof(Req), &Req, sizeof(Req), &cbReturned, NULL))
589 {
590 if (cbReturned > RT_OFFSETOF(struct _REQ, aSense))
591 memcpy(pabSense, Req.aSense, cbSense);
592 else
593 memset(pabSense, '\0', cbSense);
594 /* Windows shares the property of not properly reflecting the actually
595 * transferred data size. See above. Assume that everything worked ok.
596 * Except if there are sense information. */
597 rc = (pabSense[2] & 0x0f) == SCSI_SENSE_NONE
598 ? VINF_SUCCESS
599 : VERR_DEV_IO_ERROR;
600 }
601 else
602 rc = RTErrConvertFromWin32(GetLastError());
603 Log2(("%s: scsistatus=%d bytes returned=%d tlength=%d\n", __FUNCTION__, Req.spt.ScsiStatus, cbReturned, Req.spt.DataTransferLength));
604
605#else
606# error "Unsupported platform."
607#endif
608
609 if (pbCmd[0] == SCSI_GET_EVENT_STATUS_NOTIFICATION)
610 {
611 uint8_t *pbBuf = (uint8_t*)pvBuf;
612 Log2(("Event Status Notification class=%#02x supported classes=%#02x\n", pbBuf[2], pbBuf[3]));
613 if (RT_BE2H_U16(*(uint16_t*)pbBuf) >= 6)
614 Log2((" event %#02x %#02x %#02x %#02x\n", pbBuf[4], pbBuf[5], pbBuf[6], pbBuf[7]));
615 }
616
617 LogFlow(("%s: rc=%Rrc\n", __FUNCTION__, rc));
618 return rc;
619}
620
621#ifdef VBOX_WITH_SUID_WRAPPER
622/* These functions would have to go into a seperate solaris binary with
623 * the setuid permission set, which would run the user-SCSI ioctl and
624 * return the value. BUT... this might be prohibitively slow.
625 */
626#ifdef RT_OS_SOLARIS
627/**
628 * Checks if the current user is authorized using Solaris' role-based access control.
629 * Made as a seperate function with so that it need not be invoked each time we need
630 * to gain root access.
631 *
632 * @returns VBox error code.
633 */
634static int solarisCheckUserAuth()
635{
636 /* Uses Solaris' role-based access control (RBAC).*/
637 struct passwd *pPass = getpwuid(getuid());
638 if (pPass == NULL || chkauthattr("solaris.device.cdrw", pPass->pw_name) == 0)
639 return VERR_PERMISSION_DENIED;
640
641 return VINF_SUCCESS;
642}
643
644/**
645 * Setuid wrapper to gain root access.
646 *
647 * @returns VBox error code.
648 * @param pEffUserID Pointer to effective user ID.
649 */
650static int solarisEnterRootMode(uid_t *pEffUserID)
651{
652 /* Increase privilege if required */
653 if (*pEffUserID != 0)
654 {
655 if (seteuid(0) == 0)
656 {
657 *pEffUserID = 0;
658 return VINF_SUCCESS;
659 }
660 return VERR_PERMISSION_DENIED;
661 }
662 return VINF_SUCCESS;
663}
664
665/**
666 * Setuid wrapper to relinquish root access.
667 *
668 * @returns VBox error code.
669 * @param pEffUserID Pointer to effective user ID.
670 */
671static int solarisExitRootMode(uid_t *pEffUserID)
672{
673 /* Get back to user mode. */
674 if (*pEffUserID == 0)
675 {
676 uid_t realID = getuid();
677 if (seteuid(realID) == 0)
678 {
679 *pEffUserID = realID;
680 return VINF_SUCCESS;
681 }
682 return VERR_PERMISSION_DENIED;
683 }
684 return VINF_SUCCESS;
685}
686#endif /* RT_OS_SOLARIS */
687#endif
688
689/* -=-=-=-=- driver interface -=-=-=-=- */
690
691
692/**
693 * Construct a host dvd drive driver instance.
694 *
695 * @returns VBox status.
696 * @param pDrvIns The driver instance data.
697 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
698 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
699 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
700 * iInstance it's expected to be used a bit in this function.
701 */
702static DECLCALLBACK(int) drvHostDvdConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
703{
704 PDRVHOSTBASE pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTBASE);
705 LogFlow(("drvHostDvdConstruct: iInstance=%d\n", pDrvIns->iInstance));
706
707 /*
708 * Validate configuration.
709 */
710 if (!CFGMR3AreValuesValid(pCfgHandle, "Path\0Interval\0Locked\0BIOSVisible\0AttachFailError\0Passthrough\0"))
711 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
712
713
714 /*
715 * Init instance data.
716 */
717 int rc = DRVHostBaseInitData(pDrvIns, pCfgHandle, PDMBLOCKTYPE_DVD);
718 if (RT_SUCCESS(rc))
719 {
720 /*
721 * Override stuff.
722 */
723
724#ifndef RT_OS_L4 /* Passthrough is not supported on L4 yet */
725 bool fPassthrough;
726 rc = CFGMR3QueryBool(pCfgHandle, "Passthrough", &fPassthrough);
727 if (RT_SUCCESS(rc) && fPassthrough)
728 {
729 pThis->IBlock.pfnSendCmd = drvHostDvdSendCmd;
730 /* Passthrough requires opening the device in R/W mode. */
731 pThis->fReadOnlyConfig = false;
732# ifdef VBOX_WITH_SUID_WRAPPER /* Solaris setuid for Passthrough mode. */
733 rc = solarisCheckUserAuth();
734 if (RT_FAILURE(rc))
735 {
736 Log(("DVD: solarisCheckUserAuth failed. Permission denied!\n"));
737 return rc;
738 }
739# endif /* VBOX_WITH_SUID_WRAPPER */
740 }
741#endif /* !RT_OS_L4 */
742
743 pThis->IMount.pfnUnmount = drvHostDvdUnmount;
744 pThis->pfnDoLock = drvHostDvdDoLock;
745#ifdef USE_MEDIA_POLLING
746 if (!fPassthrough)
747 pThis->pfnPoll = drvHostDvdPoll;
748 else
749 pThis->pfnPoll = NULL;
750#endif
751#ifdef RT_OS_LINUX
752 pThis->pfnGetMediaSize = drvHostDvdGetMediaSize;
753#endif
754
755 /*
756 * 2nd init part.
757 */
758 rc = DRVHostBaseInitFinish(pThis);
759 }
760 if (RT_FAILURE(rc))
761 {
762 if (!pThis->fAttachFailError)
763 {
764 /* Suppressing the attach failure error must not affect the normal
765 * DRVHostBaseDestruct, so reset this flag below before leaving. */
766 pThis->fKeepInstance = true;
767 rc = VINF_SUCCESS;
768 }
769 DRVHostBaseDestruct(pDrvIns);
770 pThis->fKeepInstance = false;
771 }
772
773 LogFlow(("drvHostDvdConstruct: returns %Rrc\n", rc));
774 return rc;
775}
776
777
778/**
779 * Block driver registration record.
780 */
781const PDMDRVREG g_DrvHostDVD =
782{
783 /* u32Version */
784 PDM_DRVREG_VERSION,
785 /* szDriverName */
786 "HostDVD",
787 /* pszDescription */
788 "Host DVD Block Driver.",
789 /* fFlags */
790 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
791 /* fClass. */
792 PDM_DRVREG_CLASS_BLOCK,
793 /* cMaxInstances */
794 ~0,
795 /* cbInstance */
796 sizeof(DRVHOSTBASE),
797 /* pfnConstruct */
798 drvHostDvdConstruct,
799 /* pfnDestruct */
800 DRVHostBaseDestruct,
801 /* pfnIOCtl */
802 NULL,
803 /* pfnPowerOn */
804 NULL,
805 /* pfnReset */
806 NULL,
807 /* pfnSuspend */
808 NULL,
809 /* pfnResume */
810 NULL,
811 /* pfnDetach */
812 NULL
813};
814
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