VirtualBox

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

Last change on this file since 5881 was 5703, checked in by vboxsync, 17 years ago

Solaris setuid wrapper, in progress.

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