VirtualBox

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

Last change on this file since 9152 was 8155, checked in by vboxsync, 17 years ago

The Big Sun Rebranding Header Change

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