VirtualBox

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

Last change on this file since 78062 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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