VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvHostBase-solaris.cpp@ 66989

Last change on this file since 66989 was 65078, checked in by vboxsync, 8 years ago

Devices/Storage/DrvHost*: Avoid using SCSI_MAX_BUFFER_SIZE (will be removed in the future) but query the maximum supported buffer size through a dedicated method from the platform specific code

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.9 KB
Line 
1/* $Id: DrvHostBase-solaris.cpp 65078 2017-01-03 13:24:58Z vboxsync $ */
2/** @file
3 * DrvHostBase - Host base drive access driver, Solaris specifics.
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
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#define LOG_GROUP LOG_GROUP_DRV_HOST_BASE
18#include <fcntl.h>
19#include <errno.h>
20#include <stropts.h>
21#include <malloc.h>
22#include <sys/dkio.h>
23#include <pwd.h>
24#include <unistd.h>
25#include <syslog.h>
26#ifdef VBOX_WITH_SUID_WRAPPER
27# include <auth_attr.h>
28#endif
29#include <sys/sockio.h>
30#include <sys/scsi/scsi.h>
31
32extern "C" char *getfullblkname(char *);
33
34#include <iprt/file.h>
35
36/** Maximum buffer size we support, check whether darwin has some real upper limit. */
37#define SOL_SCSI_MAX_BUFFER_SIZE (100 * _1K)
38
39/**
40 * Host backend specific data.
41 */
42typedef struct DRVHOSTBASEOS
43{
44 /** The filehandle of the device. */
45 RTFILE hFileDevice;
46 /** The raw filehandle of the device. */
47 RTFILE hFileRawDevice;
48 /** Device name of raw device (RTStrFree). */
49 char *pszRawDeviceOpen;
50} DRVHOSTBASEOS;
51/** Pointer to the host backend specific data. */
52typedef DRVHOSTBASEOS *PDRVHOSBASEOS;
53
54//AssertCompile(sizeof(DRVHOSTBASEOS) <= 64);
55
56#define DRVHOSTBASE_OS_INT_DECLARED
57#include "DrvHostBase.h"
58
59#ifdef VBOX_WITH_SUID_WRAPPER
60/* These functions would have to go into a separate solaris binary with
61 * the setuid permission set, which would run the user-SCSI ioctl and
62 * return the value. BUT... this might be prohibitively slow.
63 */
64
65/**
66 * Checks if the current user is authorized using Solaris' role-based access control.
67 * Made as a separate function with so that it need not be invoked each time we need
68 * to gain root access.
69 *
70 * @returns VBox error code.
71 */
72static int solarisCheckUserAuth()
73{
74 /* Uses Solaris' role-based access control (RBAC).*/
75 struct passwd *pPass = getpwuid(getuid());
76 if (pPass == NULL || chkauthattr("solaris.device.cdrw", pPass->pw_name) == 0)
77 return VERR_PERMISSION_DENIED;
78
79 return VINF_SUCCESS;
80}
81
82/**
83 * Setuid wrapper to gain root access.
84 *
85 * @returns VBox error code.
86 * @param pEffUserID Pointer to effective user ID.
87 */
88static int solarisEnterRootMode(uid_t *pEffUserID)
89{
90 /* Increase privilege if required */
91 if (*pEffUserID != 0)
92 {
93 if (seteuid(0) == 0)
94 {
95 *pEffUserID = 0;
96 return VINF_SUCCESS;
97 }
98 return VERR_PERMISSION_DENIED;
99 }
100 return VINF_SUCCESS;
101}
102
103
104/**
105 * Setuid wrapper to relinquish root access.
106 *
107 * @returns VBox error code.
108 * @param pEffUserID Pointer to effective user ID.
109 */
110static int solarisExitRootMode(uid_t *pEffUserID)
111{
112 /* Get back to user mode. */
113 if (*pEffUserID == 0)
114 {
115 uid_t realID = getuid();
116 if (seteuid(realID) == 0)
117 {
118 *pEffUserID = realID;
119 return VINF_SUCCESS;
120 }
121 return VERR_PERMISSION_DENIED;
122 }
123 return VINF_SUCCESS;
124}
125
126#endif /* VBOX_WITH_SUID_WRAPPER */
127
128DECLHIDDEN(int) drvHostBaseScsiCmdOs(PDRVHOSTBASE pThis, const uint8_t *pbCmd, size_t cbCmd, PDMMEDIATXDIR enmTxDir,
129 void *pvBuf, uint32_t *pcbBuf, uint8_t *pbSense, size_t cbSense, uint32_t cTimeoutMillies)
130{
131 /*
132 * Minimal input validation.
133 */
134 Assert(enmTxDir == PDMMEDIATXDIR_NONE || enmTxDir == PDMMEDIATXDIR_FROM_DEVICE || enmTxDir == PDMMEDIATXDIR_TO_DEVICE);
135 Assert(!pvBuf || pcbBuf);
136 Assert(pvBuf || enmTxDir == PDMMEDIATXDIR_NONE);
137 Assert(pbSense || !cbSense);
138 AssertPtr(pbCmd);
139 Assert(cbCmd <= 16 && cbCmd >= 1);
140
141 int rc = VERR_GENERAL_FAILURE;
142 struct uscsi_cmd usc;
143 union scsi_cdb scdb;
144 memset(&usc, 0, sizeof(struct uscsi_cmd));
145 memset(&scdb, 0, sizeof(scdb));
146
147 switch (enmTxDir)
148 {
149 case PDMMEDIATXDIR_NONE:
150 Assert(*pcbBuf == 0);
151 usc.uscsi_flags = USCSI_READ;
152 /* nothing to do */
153 break;
154
155 case PDMMEDIATXDIR_FROM_DEVICE:
156 Assert(*pcbBuf != 0);
157 /* Make sure that the buffer is clear for commands reading
158 * data. The actually received data may be shorter than what
159 * we expect, and due to the unreliable feedback about how much
160 * data the ioctl actually transferred, it's impossible to
161 * prevent that. Returning previous buffer contents may cause
162 * security problems inside the guest OS, if users can issue
163 * commands to the CDROM device. */
164 memset(pvBuf, '\0', *pcbBuf);
165 usc.uscsi_flags = USCSI_READ;
166 break;
167 case PDMMEDIATXDIR_TO_DEVICE:
168 Assert(*pcbBuf != 0);
169 usc.uscsi_flags = USCSI_WRITE;
170 break;
171 default:
172 AssertMsgFailedReturn(("%d\n", enmTxDir), VERR_INTERNAL_ERROR);
173 }
174 usc.uscsi_flags |= USCSI_RQENABLE;
175 usc.uscsi_rqbuf = (char *)pbSense;
176 usc.uscsi_rqlen = cbSense;
177 usc.uscsi_cdb = (caddr_t)&scdb;
178 usc.uscsi_cdblen = 12;
179 memcpy (usc.uscsi_cdb, pbCmd, usc.uscsi_cdblen);
180 usc.uscsi_bufaddr = (caddr_t)pvBuf;
181 usc.uscsi_buflen = *pcbBuf;
182 usc.uscsi_timeout = (cTimeoutMillies + 999) / 1000;
183
184 /* We need root privileges for user-SCSI under Solaris. */
185#ifdef VBOX_WITH_SUID_WRAPPER
186 uid_t effUserID = geteuid();
187 solarisEnterRootMode(&effUserID); /** @todo check return code when this really works. */
188#endif
189 rc = ioctl(RTFileToNative(pThis->Os.hFileRawDevice), USCSICMD, &usc);
190#ifdef VBOX_WITH_SUID_WRAPPER
191 solarisExitRootMode(&effUserID);
192#endif
193 if (rc < 0)
194 {
195 if (errno == EPERM)
196 return VERR_PERMISSION_DENIED;
197 if (usc.uscsi_status)
198 {
199 rc = RTErrConvertFromErrno(errno);
200 Log2(("%s: error status. rc=%Rrc\n", __FUNCTION__, rc));
201 }
202 }
203 Log2(("%s: after ioctl: residual buflen=%d original buflen=%d\n", __FUNCTION__, usc.uscsi_resid, usc.uscsi_buflen));
204
205 return rc;
206}
207
208
209DECLHIDDEN(size_t) drvHostBaseScsiCmdGetBufLimitOs(PDRVHOSTBASE pThis)
210{
211 RT_NOREF(pThis);
212
213 return SOL_SCSI_MAX_BUFFER_SIZE;
214}
215
216
217DECLHIDDEN(int) drvHostBaseGetMediaSizeOs(PDRVHOSTBASE pThis, uint64_t *pcb)
218{
219 /*
220 * Sun docs suggests using DKIOCGGEOM instead of DKIOCGMEDIAINFO, but
221 * Sun themselves use DKIOCGMEDIAINFO for DVDs/CDs, and use DKIOCGGEOM
222 * for secondary storage devices.
223 */
224 struct dk_minfo MediaInfo;
225 if (ioctl(RTFileToNative(pThis->Os.hFileRawDevice), DKIOCGMEDIAINFO, &MediaInfo) == 0)
226 {
227 *pcb = MediaInfo.dki_capacity * (uint64_t)MediaInfo.dki_lbsize;
228 return VINF_SUCCESS;
229 }
230 return RTFileSeek(pThis->Os.hFileDevice, 0, RTFILE_SEEK_END, pcb);
231}
232
233
234DECLHIDDEN(int) drvHostBaseReadOs(PDRVHOSTBASE pThis, uint64_t off, void *pvBuf, size_t cbRead)
235{
236 return RTFileReadAt(pThis->Os.hFileDevice, off, pvBuf, cbRead, NULL);
237}
238
239
240DECLHIDDEN(int) drvHostBaseWriteOs(PDRVHOSTBASE pThis, uint64_t off, const void *pvBuf, size_t cbWrite)
241{
242 return RTFileWriteAt(pThis->Os.hFileDevice, off, pvBuf, cbWrite, NULL);
243}
244
245
246DECLHIDDEN(int) drvHostBaseFlushOs(PDRVHOSTBASE pThis)
247{
248 return RTFileFlush(pThis->Os.hFileDevice);
249}
250
251
252DECLHIDDEN(int) drvHostBaseDoLockOs(PDRVHOSTBASE pThis, bool fLock)
253{
254 int rc = ioctl(RTFileToNative(pThis->Os.hFileRawDevice), fLock ? DKIOCLOCK : DKIOCUNLOCK, 0);
255 if (rc < 0)
256 {
257 if (errno == EBUSY)
258 rc = VERR_ACCESS_DENIED;
259 else if (errno == ENOTSUP || errno == ENOSYS)
260 rc = VERR_NOT_SUPPORTED;
261 else
262 rc = RTErrConvertFromErrno(errno);
263 }
264
265 return rc;
266}
267
268
269DECLHIDDEN(int) drvHostBaseEjectOs(PDRVHOSTBASE pThis)
270{
271 int rc = ioctl(RTFileToNative(pThis->Os.hFileRawDevice), DKIOCEJECT, 0);
272 if (rc < 0)
273 {
274 if (errno == EBUSY)
275 rc = VERR_PDM_MEDIA_LOCKED;
276 else if (errno == ENOSYS || errno == ENOTSUP)
277 rc = VERR_NOT_SUPPORTED;
278 else if (errno == ENODEV)
279 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
280 else
281 rc = RTErrConvertFromErrno(errno);
282 }
283
284 return rc;
285}
286
287
288DECLHIDDEN(int) drvHostBaseQueryMediaStatusOs(PDRVHOSTBASE pThis, bool *pfMediaChanged, bool *pfMediaPresent)
289{
290 *pfMediaPresent = false;
291 *pfMediaChanged = false;
292
293 /* Need to pass the previous state and DKIO_NONE for the first time. */
294 static dkio_state s_DeviceState = DKIO_NONE;
295 dkio_state PreviousState = s_DeviceState;
296 int rc = ioctl(RTFileToNative(pThis->Os.hFileRawDevice), DKIOCSTATE, &s_DeviceState);
297 if (rc == 0)
298 {
299 *pfMediaPresent = (s_DeviceState == DKIO_INSERTED);
300 if (PreviousState != s_DeviceState)
301 *pfMediaChanged = true;
302 }
303
304 return VINF_SUCCESS;
305}
306
307
308DECLHIDDEN(void) drvHostBaseInitOs(PDRVHOSTBASE pThis)
309{
310 pThis->Os.hFileDevice = NIL_RTFILE;
311 pThis->Os.hFileRawDevice = NIL_RTFILE;
312 pThis->Os.pszRawDeviceOpen = NULL;
313}
314
315
316DECLHIDDEN(int) drvHostBaseOpenOs(PDRVHOSTBASE pThis, bool fReadOnly)
317{
318#ifdef VBOX_WITH_SUID_WRAPPER /* Solaris setuid for Passthrough mode. */
319 if ( (pThis->enmType == PDMMEDIATYPE_CDROM || pThis->enmType == PDMMEDIATYPE_DVD)
320 && pThis->IMedia.pfnSendCmd)
321 {
322 rc = solarisCheckUserAuth();
323 if (RT_FAILURE(rc))
324 {
325 Log(("DVD: solarisCheckUserAuth failed. Permission denied!\n"));
326 return rc;
327 }
328 }
329#endif /* VBOX_WITH_SUID_WRAPPER */
330
331 char *pszBlockDevName = getfullblkname(pThis->pszDevice);
332 if (!pszBlockDevName)
333 return VERR_NO_MEMORY;
334 pThis->pszDeviceOpen = RTStrDup(pszBlockDevName); /* for RTStrFree() */
335 free(pszBlockDevName);
336 pThis->Os.pszRawDeviceOpen = RTStrDup(pThis->pszDevice);
337 if (!pThis->pszDeviceOpen || !pThis->Os.pszRawDeviceOpen)
338 return VERR_NO_MEMORY;
339
340 unsigned fFlags = (fReadOnly ? RTFILE_O_READ : RTFILE_O_READWRITE)
341 | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_NON_BLOCK;
342 int rc = RTFileOpen(&pThis->Os.hFileDevice, pThis->pszDeviceOpen, fFlags);
343 if (RT_SUCCESS(rc))
344 {
345 rc = RTFileOpen(&pThis->Os.hFileRawDevice, pThis->Os.pszRawDeviceOpen, fFlags);
346 if (RT_SUCCESS(rc))
347 return rc;
348
349 LogRel(("DVD: failed to open device %s rc=%Rrc\n", pThis->Os.pszRawDeviceOpen, rc));
350 RTFileClose(pThis->Os.hFileDevice);
351 }
352 else
353 LogRel(("DVD: failed to open device %s rc=%Rrc\n", pThis->pszDeviceOpen, rc));
354 return rc;
355}
356
357
358DECLHIDDEN(int) drvHostBaseMediaRefreshOs(PDRVHOSTBASE pThis)
359{
360 RT_NOREF(pThis);
361 return VINF_SUCCESS;
362}
363
364
365DECLHIDDEN(bool) drvHostBaseIsMediaPollingRequiredOs(PDRVHOSTBASE pThis)
366{
367 if (pThis->enmType == PDMMEDIATYPE_CDROM || pThis->enmType == PDMMEDIATYPE_DVD)
368 return true;
369
370 AssertMsgFailed(("Solaris supports only CD/DVD host drive access\n"));
371 return false;
372}
373
374
375DECLHIDDEN(void) drvHostBaseDestructOs(PDRVHOSTBASE pThis)
376{
377 /*
378 * Unlock the drive if we've locked it or we're in passthru mode.
379 */
380 if ( pThis->fLocked
381 && pThis->Os.hFileDevice != NIL_RTFILE
382 && pThis->pfnDoLock)
383 {
384 int rc = pThis->pfnDoLock(pThis, false);
385 if (RT_SUCCESS(rc))
386 pThis->fLocked = false;
387 }
388
389 if (pThis->Os.hFileDevice != NIL_RTFILE)
390 {
391 int rc = RTFileClose(pThis->Os.hFileDevice);
392 AssertRC(rc);
393 pThis->Os.hFileDevice = NIL_RTFILE;
394 }
395
396 if (pThis->Os.hFileRawDevice != NIL_RTFILE)
397 {
398 int rc = RTFileClose(pThis->Os.hFileRawDevice);
399 AssertRC(rc);
400 pThis->Os.hFileRawDevice = NIL_RTFILE;
401 }
402
403 if (pThis->Os.pszRawDeviceOpen)
404 {
405 RTStrFree(pThis->Os.pszRawDeviceOpen);
406 pThis->Os.pszRawDeviceOpen = NULL;
407 }
408}
409
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