VirtualBox

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

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

include/VBox/vmm/pdmifs.h: Don't include hgcmsvc.h just for VBOXHGCMSVCPARM as it drags in all kinds of stuff. bugref:9344

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette