VirtualBox

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

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

Linux 2.6.28 hack

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