VirtualBox

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

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