VirtualBox

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

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

Solaris.

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