VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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