VirtualBox

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

Last change on this file since 64664 was 64317, checked in by vboxsync, 8 years ago

build fixes for Solaris

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