Index: /trunk/src/VBox/Devices/Storage/DrvHostBase-linux.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/DrvHostBase-linux.cpp	(revision 64241)
+++ /trunk/src/VBox/Devices/Storage/DrvHostBase-linux.cpp	(revision 64241)
@@ -0,0 +1,128 @@
+/* $Id$ */
+/** @file
+ * DrvHostBase - Host base drive access driver, Linux specifics.
+ */
+
+/*
+ * Copyright (C) 2006-2016 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#define LOG_GROUP LOG_GROUP_DRV_HOST_BASE
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <errno.h>
+#include <linux/version.h>
+/* All the following crap is apparently not necessary anymore since Linux
+ * version 2.6.29. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
+/* This is a hack to work around conflicts between these linux kernel headers
+ * and the GLIBC tcpip headers. They have different declarations of the 4
+ * standard byte order functions. */
+# define _LINUX_BYTEORDER_GENERIC_H
+/* This is another hack for not bothering with C++ unfriendly byteswap macros. */
+/* Those macros that are needed are defined in the header below. */
+# include "swab.h"
+#endif
+#include <linux/cdrom.h>
+#include <limits.h>
+
+#include <iprt/mem.h>
+#include <iprt/file.h>
+#include <VBox/scsi.h>
+
+#include "DrvHostBase.h"
+
+DECLHIDDEN(int) drvHostBaseScsiCmdOs(PDRVHOSTBASE pThis, const uint8_t *pbCmd, size_t cbCmd, PDMMEDIATXDIR enmTxDir,
+                                     void *pvBuf, uint32_t *pcbBuf, uint8_t *pbSense, size_t cbSense, uint32_t cTimeoutMillies)
+{
+    /*
+     * Minimal input validation.
+     */
+    Assert(enmTxDir == PDMMEDIATXDIR_NONE || enmTxDir == PDMMEDIATXDIR_FROM_DEVICE || enmTxDir == PDMMEDIATXDIR_TO_DEVICE);
+    Assert(!pvBuf || pcbBuf);
+    Assert(pvBuf || enmTxDir == PDMMEDIATXDIR_NONE);
+    Assert(pbSense || !cbSense);
+    AssertPtr(pbCmd);
+    Assert(cbCmd <= 16 && cbCmd >= 1);
+
+    int rc = VERR_GENERAL_FAILURE;
+    int direction;
+    struct cdrom_generic_command cgc;
+
+    switch (enmTxDir)
+    {
+        case PDMMEDIATXDIR_NONE:
+            Assert(*pcbBuf == 0);
+            direction = CGC_DATA_NONE;
+            break;
+        case PDMMEDIATXDIR_FROM_DEVICE:
+            Assert(*pcbBuf != 0);
+            Assert(*pcbBuf <= SCSI_MAX_BUFFER_SIZE);
+            /* Make sure that the buffer is clear for commands reading
+             * data. The actually received data may be shorter than what
+             * we expect, and due to the unreliable feedback about how much
+             * data the ioctl actually transferred, it's impossible to
+             * prevent that. Returning previous buffer contents may cause
+             * security problems inside the guest OS, if users can issue
+             * commands to the CDROM device. */
+            memset(pThis->pbDoubleBuffer, '\0', *pcbBuf);
+            direction = CGC_DATA_READ;
+            break;
+        case PDMMEDIATXDIR_TO_DEVICE:
+            Assert(*pcbBuf != 0);
+            Assert(*pcbBuf <= SCSI_MAX_BUFFER_SIZE);
+            memcpy(pThis->pbDoubleBuffer, pvBuf, *pcbBuf);
+            direction = CGC_DATA_WRITE;
+            break;
+        default:
+            AssertMsgFailed(("enmTxDir invalid!\n"));
+            direction = CGC_DATA_NONE;
+    }
+    memset(&cgc, '\0', sizeof(cgc));
+    memcpy(cgc.cmd, pbCmd, RT_MIN(CDROM_PACKET_SIZE, cbCmd));
+    cgc.buffer = (unsigned char *)pThis->pbDoubleBuffer;
+    cgc.buflen = *pcbBuf;
+    cgc.stat = 0;
+    Assert(cbSense >= sizeof(struct request_sense));
+    cgc.sense = (struct request_sense *)pbSense;
+    cgc.data_direction = direction;
+    cgc.quiet = false;
+    cgc.timeout = cTimeoutMillies;
+    rc = ioctl(RTFileToNative(pThis->hFileDevice), CDROM_SEND_PACKET, &cgc);
+    if (rc < 0)
+    {
+        if (errno == EBUSY)
+            rc = VERR_PDM_MEDIA_LOCKED;
+        else if (errno == ENOSYS)
+            rc = VERR_NOT_SUPPORTED;
+        else
+        {
+            rc = RTErrConvertFromErrno(errno);
+            if (rc == VERR_ACCESS_DENIED && cgc.sense->sense_key == SCSI_SENSE_NONE)
+                cgc.sense->sense_key = SCSI_SENSE_ILLEGAL_REQUEST;
+            Log2(("%s: error status %d, rc=%Rrc\n", __FUNCTION__, cgc.stat, rc));
+        }
+    }
+    switch (enmTxDir)
+    {
+        case PDMMEDIATXDIR_FROM_DEVICE:
+            memcpy(pvBuf, pThis->pbDoubleBuffer, *pcbBuf);
+            break;
+        default:
+            ;
+    }
+    Log2(("%s: after ioctl: cgc.buflen=%d txlen=%d\n", __FUNCTION__, cgc.buflen, *pcbBuf));
+    /* The value of cgc.buflen does not reliably reflect the actual amount
+     * of data transferred (for packet commands with little data transfer
+     * it's 0). So just assume that everything worked ok. */
+
+    return rc;
+}
+
Index: /trunk/src/VBox/Devices/Storage/DrvHostBase-solaris.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/DrvHostBase-solaris.cpp	(revision 64241)
+++ /trunk/src/VBox/Devices/Storage/DrvHostBase-solaris.cpp	(revision 64241)
@@ -0,0 +1,167 @@
+/* $Id$ */
+/** @file
+ * DrvHostBase - Host base drive access driver, Solaris specifics.
+ */
+
+/*
+ * Copyright (C) 2006-2016 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#define LOG_GROUP LOG_GROUP_DRV_HOST_BASE
+#include <fcntl.h>
+#include <errno.h>
+#include <stropts.h>
+#include <malloc.h>
+#include <sys/dkio.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <syslog.h>
+#ifdef VBOX_WITH_SUID_WRAPPER
+# include <auth_attr.h>
+#endif
+#include <sys/sockio.h>
+#include <sys/scsi/scsi.h>
+
+extern "C" char *getfullblkname(char *);
+
+#include "DrvHostBase.h"
+
+#ifdef VBOX_WITH_SUID_WRAPPER
+/* These functions would have to go into a separate solaris binary with
+ * the setuid permission set, which would run the user-SCSI ioctl and
+ * return the value. BUT... this might be prohibitively slow.
+ */
+
+/**
+ * Setuid wrapper to gain root access.
+ *
+ * @returns VBox error code.
+ * @param   pEffUserID     Pointer to effective user ID.
+ */
+static int solarisEnterRootMode(uid_t *pEffUserID)
+{
+    /* Increase privilege if required */
+    if (*pEffUserID != 0)
+    {
+        if (seteuid(0) == 0)
+        {
+            *pEffUserID = 0;
+            return VINF_SUCCESS;
+        }
+        return VERR_PERMISSION_DENIED;
+    }
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Setuid wrapper to relinquish root access.
+ *
+ * @returns VBox error code.
+ * @param   pEffUserID     Pointer to effective user ID.
+ */
+static int solarisExitRootMode(uid_t *pEffUserID)
+{
+    /* Get back to user mode. */
+    if (*pEffUserID == 0)
+    {
+        uid_t realID = getuid();
+        if (seteuid(realID) == 0)
+        {
+            *pEffUserID = realID;
+            return VINF_SUCCESS;
+        }
+        return VERR_PERMISSION_DENIED;
+    }
+    return VINF_SUCCESS;
+}
+
+#endif /* VBOX_WITH_SUID_WRAPPER */
+
+DECLHIDDEN(int) drvHostBaseScsiCmdOs(PDRVHOSTBASE pThis, const uint8_t *pbCmd, size_t cbCmd, PDMMEDIATXDIR enmTxDir,
+                                     void *pvBuf, uint32_t *pcbBuf, uint8_t *pbSense, size_t cbSense, uint32_t cTimeoutMillies)
+{
+    /*
+     * Minimal input validation.
+     */
+    Assert(enmTxDir == PDMMEDIATXDIR_NONE || enmTxDir == PDMMEDIATXDIR_FROM_DEVICE || enmTxDir == PDMMEDIATXDIR_TO_DEVICE);
+    Assert(!pvBuf || pcbBuf);
+    Assert(pvBuf || enmTxDir == PDMMEDIATXDIR_NONE);
+    Assert(pbSense || !cbSense);
+    AssertPtr(pbCmd);
+    Assert(cbCmd <= 16 && cbCmd >= 1);
+
+    int rc = VERR_GENERAL_FAILURE;
+    struct uscsi_cmd usc;
+    union scsi_cdb scdb;
+    memset(&usc, 0, sizeof(struct uscsi_cmd));
+    memset(&scdb, 0, sizeof(scdb));
+
+    switch (enmTxDir)
+    {
+        case PDMMEDIATXDIR_NONE:
+            Assert(*pcbBuf == 0);
+            usc.uscsi_flags = USCSI_READ;
+            /* nothing to do */
+            break;
+
+        case PDMMEDIATXDIR_FROM_DEVICE:
+            Assert(*pcbBuf != 0);
+            /* Make sure that the buffer is clear for commands reading
+             * data. The actually received data may be shorter than what
+             * we expect, and due to the unreliable feedback about how much
+             * data the ioctl actually transferred, it's impossible to
+             * prevent that. Returning previous buffer contents may cause
+             * security problems inside the guest OS, if users can issue
+             * commands to the CDROM device. */
+            memset(pvBuf, '\0', *pcbBuf);
+            usc.uscsi_flags = USCSI_READ;
+            break;
+        case PDMMEDIATXDIR_TO_DEVICE:
+            Assert(*pcbBuf != 0);
+            usc.uscsi_flags = USCSI_WRITE;
+            break;
+        default:
+            AssertMsgFailedReturn(("%d\n", enmTxDir), VERR_INTERNAL_ERROR);
+    }
+    usc.uscsi_flags |= USCSI_RQENABLE;
+    usc.uscsi_rqbuf = (char *)pbSense;
+    usc.uscsi_rqlen = cbSense;
+    usc.uscsi_cdb = (caddr_t)&scdb;
+    usc.uscsi_cdblen = 12;
+    memcpy (usc.uscsi_cdb, pbCmd, usc.uscsi_cdblen);
+    usc.uscsi_bufaddr = (caddr_t)pvBuf;
+    usc.uscsi_buflen = *pcbBuf;
+    usc.uscsi_timeout = (cTimeoutMillies + 999) / 1000;
+
+    /* We need root privileges for user-SCSI under Solaris. */
+#ifdef VBOX_WITH_SUID_WRAPPER
+    uid_t effUserID = geteuid();
+    solarisEnterRootMode(&effUserID); /** @todo check return code when this really works. */
+#endif
+    rc = ioctl(RTFileToNative(pThis->hFileRawDevice), USCSICMD, &usc);
+#ifdef VBOX_WITH_SUID_WRAPPER
+    solarisExitRootMode(&effUserID);
+#endif
+    if (rc < 0)
+    {
+        if (errno == EPERM)
+            return VERR_PERMISSION_DENIED;
+        if (usc.uscsi_status)
+        {
+            rc = RTErrConvertFromErrno(errno);
+            Log2(("%s: error status. rc=%Rrc\n", __FUNCTION__, rc));
+        }
+    }
+    Log2(("%s: after ioctl: residual buflen=%d original buflen=%d\n", __FUNCTION__, usc.uscsi_resid, usc.uscsi_buflen));
+
+    return rc;
+}
+
Index: /trunk/src/VBox/Devices/Storage/DrvHostBase-win.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/DrvHostBase-win.cpp	(revision 64241)
+++ /trunk/src/VBox/Devices/Storage/DrvHostBase-win.cpp	(revision 64241)
@@ -0,0 +1,108 @@
+/* $Id$ */
+/** @file
+ * DrvHostBase - Host base drive access driver, Windows specifics.
+ */
+
+/*
+ * Copyright (C) 2006-2016 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#define LOG_GROUP LOG_GROUP_DRV_HOST_BASE
+#include <mach/mach.h>
+#include <Carbon/Carbon.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
+#include <IOKit/scsi/SCSITaskLib.h>
+#include <IOKit/scsi/SCSICommandOperationCodes.h>
+#include <IOKit/IOBSD.h>
+#include <DiskArbitration/DiskArbitration.h>
+#include <mach/mach_error.h>
+#include <VBox/scsi.h>
+
+#include "DrvHostBase.h"
+
+DECLHIDDEN(int) drvHostBaseScsiCmdOs(PDRVHOSTBASE pThis, const uint8_t *pbCmd, size_t cbCmd, PDMMEDIATXDIR enmTxDir,
+                                     void *pvBuf, uint32_t *pcbBuf, uint8_t *pbSense, size_t cbSense, uint32_t cTimeoutMillies)
+{
+    /*
+     * Minimal input validation.
+     */
+    Assert(enmTxDir == PDMMEDIATXDIR_NONE || enmTxDir == PDMMEDIATXDIR_FROM_DEVICE || enmTxDir == PDMMEDIATXDIR_TO_DEVICE);
+    Assert(!pvBuf || pcbBuf);
+    Assert(pvBuf || enmTxDir == PDMMEDIATXDIR_NONE);
+    Assert(pbSense || !cbSense);
+    AssertPtr(pbCmd);
+    Assert(cbCmd <= 16 && cbCmd >= 1);
+
+    int rc = VERR_GENERAL_FAILURE;
+    int direction;
+    struct _REQ
+    {
+        SCSI_PASS_THROUGH_DIRECT spt;
+        uint8_t aSense[64];
+    } Req;
+    DWORD cbReturned = 0;
+
+    switch (enmTxDir)
+    {
+        case PDMMEDIATXDIR_NONE:
+            direction = SCSI_IOCTL_DATA_UNSPECIFIED;
+            break;
+        case PDMMEDIATXDIR_FROM_DEVICE:
+            Assert(*pcbBuf != 0);
+            /* Make sure that the buffer is clear for commands reading
+             * data. The actually received data may be shorter than what
+             * we expect, and due to the unreliable feedback about how much
+             * data the ioctl actually transferred, it's impossible to
+             * prevent that. Returning previous buffer contents may cause
+             * security problems inside the guest OS, if users can issue
+             * commands to the CDROM device. */
+            memset(pvBuf, '\0', *pcbBuf);
+            direction = SCSI_IOCTL_DATA_IN;
+            break;
+        case PDMMEDIATXDIR_TO_DEVICE:
+            direction = SCSI_IOCTL_DATA_OUT;
+            break;
+        default:
+            AssertMsgFailed(("enmTxDir invalid!\n"));
+            direction = SCSI_IOCTL_DATA_UNSPECIFIED;
+    }
+    memset(&Req, '\0', sizeof(Req));
+    Req.spt.Length = sizeof(Req.spt);
+    Req.spt.CdbLength = 12;
+    memcpy(Req.spt.Cdb, pbCmd, Req.spt.CdbLength);
+    Req.spt.DataBuffer = pvBuf;
+    Req.spt.DataTransferLength = *pcbBuf;
+    Req.spt.DataIn = direction;
+    Req.spt.TimeOutValue = (cTimeoutMillies + 999) / 1000; /* Convert to seconds */
+    Assert(cbSense <= sizeof(Req.aSense));
+    Req.spt.SenseInfoLength = (UCHAR)RT_MIN(sizeof(Req.aSense), cbSense);
+    Req.spt.SenseInfoOffset = RT_OFFSETOF(struct _REQ, aSense);
+    if (DeviceIoControl((HANDLE)RTFileToNative(pThis->hFileDevice), IOCTL_SCSI_PASS_THROUGH_DIRECT,
+                        &Req, sizeof(Req), &Req, sizeof(Req), &cbReturned, NULL))
+    {
+        if (cbReturned > RT_OFFSETOF(struct _REQ, aSense))
+            memcpy(pbSense, Req.aSense, cbSense);
+        else
+            memset(pbSense, '\0', cbSense);
+        /* Windows shares the property of not properly reflecting the actually
+         * transferred data size. See above. Assume that everything worked ok.
+         * Except if there are sense information. */
+        rc = (pbSense[2] & 0x0f) == SCSI_SENSE_NONE
+                 ? VINF_SUCCESS
+                 : VERR_DEV_IO_ERROR;
+    }
+    else
+        rc = RTErrConvertFromWin32(GetLastError());
+    Log2(("%s: scsistatus=%d bytes returned=%d tlength=%d\n", __FUNCTION__, Req.spt.ScsiStatus, cbReturned, Req.spt.DataTransferLength));
+
+    return rc;
+}
+
