VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvHostBase-linux.cpp@ 93492

Last change on this file since 93492 was 93115, checked in by vboxsync, 3 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-linux.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * DrvHostBase - Host base drive access driver, Linux specifics.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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 <sys/ioctl.h>
24#include <sys/fcntl.h>
25#include <errno.h>
26#include <linux/version.h>
27/* All the following crap is apparently not necessary anymore since Linux
28 * version 2.6.29. */
29#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
30/* This is a hack to work around conflicts between these linux kernel headers
31 * and the GLIBC tcpip headers. They have different declarations of the 4
32 * standard byte order functions. */
33# define _LINUX_BYTEORDER_GENERIC_H
34/* This is another hack for not bothering with C++ unfriendly byteswap macros. */
35/* Those macros that are needed are defined in the header below. */
36# include "swab.h"
37#endif
38#include <linux/fd.h>
39#include <linux/cdrom.h>
40#include <limits.h>
41
42#include <iprt/mem.h>
43#include <iprt/file.h>
44#include <iprt/string.h>
45#include <VBox/err.h>
46#include <VBox/scsi.h>
47
48
49/**
50 * Host backend specific data (needed by DrvHostBase.h).
51 */
52typedef struct DRVHOSTBASEOS
53{
54 /** The filehandle of the device. */
55 RTFILE hFileDevice;
56 /** Double buffer required for ioctl with the Linux kernel as long as we use
57 * remap_pfn_range() instead of vm_insert_page(). */
58 uint8_t *pbDoubleBuffer;
59 /** Previous disk inserted indicator for the media polling on floppy drives. */
60 bool fPrevDiskIn;
61} DRVHOSTBASEOS;
62/** Pointer to the host backend specific data. */
63typedef DRVHOSTBASEOS *PDRVHOSBASEOS;
64AssertCompile(sizeof(DRVHOSTBASEOS) <= 64);
65
66#define DRVHOSTBASE_OS_INT_DECLARED
67#include "DrvHostBase.h"
68
69
70/*********************************************************************************************************************************
71* Defined Constants And Macros *
72*********************************************************************************************************************************/
73/** Maximum buffer size supported by the kernel interface. */
74#define LNX_SCSI_MAX_BUFFER_SIZE (100 * _1K)
75
76
77
78
79
80DECLHIDDEN(int) drvHostBaseScsiCmdOs(PDRVHOSTBASE pThis, const uint8_t *pbCmd, size_t cbCmd, PDMMEDIATXDIR enmTxDir,
81 void *pvBuf, uint32_t *pcbBuf, uint8_t *pbSense, size_t cbSense, uint32_t cTimeoutMillies)
82{
83 /*
84 * Minimal input validation.
85 */
86 Assert(enmTxDir == PDMMEDIATXDIR_NONE || enmTxDir == PDMMEDIATXDIR_FROM_DEVICE || enmTxDir == PDMMEDIATXDIR_TO_DEVICE);
87 Assert(!pvBuf || pcbBuf);
88 Assert(pvBuf || enmTxDir == PDMMEDIATXDIR_NONE);
89 Assert(pbSense || !cbSense); RT_NOREF(cbSense);
90 AssertPtr(pbCmd);
91 Assert(cbCmd <= 16 && cbCmd >= 1);
92
93 /* Allocate the temporary buffer lazily. */
94 if(RT_UNLIKELY(!pThis->Os.pbDoubleBuffer))
95 {
96 pThis->Os.pbDoubleBuffer = (uint8_t *)RTMemAlloc(LNX_SCSI_MAX_BUFFER_SIZE);
97 if (!pThis->Os.pbDoubleBuffer)
98 return VERR_NO_MEMORY;
99 }
100
101 int rc = VERR_GENERAL_FAILURE;
102 int direction;
103 struct cdrom_generic_command cgc;
104
105 switch (enmTxDir)
106 {
107 case PDMMEDIATXDIR_NONE:
108 Assert(*pcbBuf == 0);
109 direction = CGC_DATA_NONE;
110 break;
111 case PDMMEDIATXDIR_FROM_DEVICE:
112 Assert(*pcbBuf != 0);
113 Assert(*pcbBuf <= LNX_SCSI_MAX_BUFFER_SIZE);
114 /* Make sure that the buffer is clear for commands reading
115 * data. The actually received data may be shorter than what
116 * we expect, and due to the unreliable feedback about how much
117 * data the ioctl actually transferred, it's impossible to
118 * prevent that. Returning previous buffer contents may cause
119 * security problems inside the guest OS, if users can issue
120 * commands to the CDROM device. */
121 memset(pThis->Os.pbDoubleBuffer, '\0', *pcbBuf);
122 direction = CGC_DATA_READ;
123 break;
124 case PDMMEDIATXDIR_TO_DEVICE:
125 Assert(*pcbBuf != 0);
126 Assert(*pcbBuf <= LNX_SCSI_MAX_BUFFER_SIZE);
127 memcpy(pThis->Os.pbDoubleBuffer, pvBuf, *pcbBuf);
128 direction = CGC_DATA_WRITE;
129 break;
130 default:
131 AssertMsgFailed(("enmTxDir invalid!\n"));
132 direction = CGC_DATA_NONE;
133 }
134 memset(&cgc, '\0', sizeof(cgc));
135 memcpy(cgc.cmd, pbCmd, RT_MIN(CDROM_PACKET_SIZE, cbCmd));
136 cgc.buffer = (unsigned char *)pThis->Os.pbDoubleBuffer;
137 cgc.buflen = *pcbBuf;
138 cgc.stat = 0;
139 Assert(cbSense >= sizeof(struct request_sense));
140 cgc.sense = (struct request_sense *)pbSense;
141 cgc.data_direction = direction;
142 cgc.quiet = false;
143 cgc.timeout = cTimeoutMillies;
144 rc = ioctl(RTFileToNative(pThis->Os.hFileDevice), CDROM_SEND_PACKET, &cgc);
145 if (rc < 0)
146 {
147 if (errno == EBUSY)
148 rc = VERR_PDM_MEDIA_LOCKED;
149 else if (errno == ENOSYS)
150 rc = VERR_NOT_SUPPORTED;
151 else
152 {
153 rc = RTErrConvertFromErrno(errno);
154 if (rc == VERR_ACCESS_DENIED && cgc.sense->sense_key == SCSI_SENSE_NONE)
155 cgc.sense->sense_key = SCSI_SENSE_ILLEGAL_REQUEST;
156 Log2(("%s: error status %d, rc=%Rrc\n", __FUNCTION__, cgc.stat, rc));
157 }
158 }
159 switch (enmTxDir)
160 {
161 case PDMMEDIATXDIR_FROM_DEVICE:
162 memcpy(pvBuf, pThis->Os.pbDoubleBuffer, *pcbBuf);
163 break;
164 default:
165 ;
166 }
167 Log2(("%s: after ioctl: cgc.buflen=%d txlen=%d\n", __FUNCTION__, cgc.buflen, *pcbBuf));
168 /* The value of cgc.buflen does not reliably reflect the actual amount
169 * of data transferred (for packet commands with little data transfer
170 * it's 0). So just assume that everything worked ok. */
171
172 return rc;
173}
174
175
176DECLHIDDEN(size_t) drvHostBaseScsiCmdGetBufLimitOs(PDRVHOSTBASE pThis)
177{
178 RT_NOREF(pThis);
179
180 return LNX_SCSI_MAX_BUFFER_SIZE;
181}
182
183
184DECLHIDDEN(int) drvHostBaseGetMediaSizeOs(PDRVHOSTBASE pThis, uint64_t *pcb)
185{
186 int rc = VERR_INVALID_STATE;
187
188 if (PDMMEDIATYPE_IS_FLOPPY(pThis->enmType))
189 {
190 rc = ioctl(RTFileToNative(pThis->Os.hFileDevice), FDFLUSH);
191 if (rc)
192 {
193 rc = RTErrConvertFromErrno (errno);
194 Log(("DrvHostFloppy: FDFLUSH ioctl(%s) failed, errno=%d rc=%Rrc\n", pThis->pszDevice, errno, rc));
195 return rc;
196 }
197
198 floppy_drive_struct DrvStat;
199 rc = ioctl(RTFileToNative(pThis->Os.hFileDevice), FDGETDRVSTAT, &DrvStat);
200 if (rc)
201 {
202 rc = RTErrConvertFromErrno(errno);
203 Log(("DrvHostFloppy: FDGETDRVSTAT ioctl(%s) failed, errno=%d rc=%Rrc\n", pThis->pszDevice, errno, rc));
204 return rc;
205 }
206 pThis->fReadOnly = !(DrvStat.flags & FD_DISK_WRITABLE);
207 rc = RTFileSeek(pThis->Os.hFileDevice, 0, RTFILE_SEEK_END, pcb);
208 }
209 else if (pThis->enmType == PDMMEDIATYPE_CDROM || pThis->enmType == PDMMEDIATYPE_DVD)
210 {
211 /* Clear the media-changed-since-last-call-thingy just to be on the safe side. */
212 ioctl(RTFileToNative(pThis->Os.hFileDevice), CDROM_MEDIA_CHANGED, CDSL_CURRENT);
213 rc = RTFileSeek(pThis->Os.hFileDevice, 0, RTFILE_SEEK_END, pcb);
214 }
215
216 return rc;
217}
218
219
220DECLHIDDEN(int) drvHostBaseReadOs(PDRVHOSTBASE pThis, uint64_t off, void *pvBuf, size_t cbRead)
221{
222 return RTFileReadAt(pThis->Os.hFileDevice, off, pvBuf, cbRead, NULL);
223}
224
225
226DECLHIDDEN(int) drvHostBaseWriteOs(PDRVHOSTBASE pThis, uint64_t off, const void *pvBuf, size_t cbWrite)
227{
228 return RTFileWriteAt(pThis->Os.hFileDevice, off, pvBuf, cbWrite, NULL);
229}
230
231
232DECLHIDDEN(int) drvHostBaseFlushOs(PDRVHOSTBASE pThis)
233{
234 return RTFileFlush(pThis->Os.hFileDevice);
235}
236
237
238DECLHIDDEN(int) drvHostBaseDoLockOs(PDRVHOSTBASE pThis, bool fLock)
239{
240 int rc = ioctl(RTFileToNative(pThis->Os.hFileDevice), CDROM_LOCKDOOR, (int)fLock);
241 if (rc < 0)
242 {
243 if (errno == EBUSY)
244 rc = VERR_ACCESS_DENIED;
245 else if (errno == EDRIVE_CANT_DO_THIS)
246 rc = VERR_NOT_SUPPORTED;
247 else
248 rc = RTErrConvertFromErrno(errno);
249 }
250
251 return rc;
252}
253
254
255DECLHIDDEN(int) drvHostBaseEjectOs(PDRVHOSTBASE pThis)
256{
257 int rc = ioctl(RTFileToNative(pThis->Os.hFileDevice), CDROMEJECT, 0);
258 if (rc < 0)
259 {
260 if (errno == EBUSY)
261 rc = VERR_PDM_MEDIA_LOCKED;
262 else if (errno == ENOSYS)
263 rc = VERR_NOT_SUPPORTED;
264 else
265 rc = RTErrConvertFromErrno(errno);
266 }
267
268 return rc;
269}
270
271
272DECLHIDDEN(int) drvHostBaseQueryMediaStatusOs(PDRVHOSTBASE pThis, bool *pfMediaChanged, bool *pfMediaPresent)
273{
274 int rc = VINF_SUCCESS;
275
276 if (PDMMEDIATYPE_IS_FLOPPY(pThis->enmType))
277 {
278 floppy_drive_struct DrvStat;
279 int rcLnx = ioctl(RTFileToNative(pThis->Os.hFileDevice), FDPOLLDRVSTAT, &DrvStat);
280 if (!rcLnx)
281 {
282 bool fDiskIn = !(DrvStat.flags & (FD_VERIFY | FD_DISK_NEWCHANGE));
283 *pfMediaPresent = fDiskIn;
284
285 if (fDiskIn != pThis->Os.fPrevDiskIn)
286 *pfMediaChanged = true;
287
288 pThis->Os.fPrevDiskIn = fDiskIn;
289 }
290 else
291 rc = RTErrConvertFromErrno(errno);
292 }
293 else
294 {
295 *pfMediaPresent = ioctl(RTFileToNative(pThis->Os.hFileDevice), CDROM_DRIVE_STATUS, CDSL_CURRENT) == CDS_DISC_OK;
296 *pfMediaChanged = false;
297 if (pThis->fMediaPresent != *pfMediaPresent)
298 *pfMediaChanged = ioctl(RTFileToNative(pThis->Os.hFileDevice), CDROM_MEDIA_CHANGED, CDSL_CURRENT) == 1;
299 }
300
301 return rc;
302}
303
304
305DECLHIDDEN(void) drvHostBaseInitOs(PDRVHOSTBASE pThis)
306{
307 pThis->Os.hFileDevice = NIL_RTFILE;
308 pThis->Os.pbDoubleBuffer = NULL;
309 pThis->Os.fPrevDiskIn = false;
310}
311
312
313DECLHIDDEN(int) drvHostBaseOpenOs(PDRVHOSTBASE pThis, bool fReadOnly)
314{
315 uint32_t fFlags = (fReadOnly ? RTFILE_O_READ : RTFILE_O_READWRITE) | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_NON_BLOCK;
316 return RTFileOpen(&pThis->Os.hFileDevice, pThis->pszDevice, fFlags);
317}
318
319
320DECLHIDDEN(int) drvHostBaseMediaRefreshOs(PDRVHOSTBASE pThis)
321{
322 /*
323 * Need to re-open the device because it will kill off any cached data
324 * that Linux for some peculiar reason thinks should survive a media change.
325 */
326 if (pThis->Os.hFileDevice != NIL_RTFILE)
327 {
328 RTFileClose(pThis->Os.hFileDevice);
329 pThis->Os.hFileDevice = NIL_RTFILE;
330 }
331 int rc = drvHostBaseOpenOs(pThis, pThis->fReadOnlyConfig);
332 if (RT_FAILURE(rc))
333 {
334 if (!pThis->fReadOnlyConfig)
335 {
336 LogFlow(("%s-%d: drvHostBaseMediaRefreshOs: '%s' - retry readonly (%Rrc)\n",
337 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->pszDevice, rc));
338 rc = drvHostBaseOpenOs(pThis, true);
339 }
340 if (RT_FAILURE(rc))
341 {
342 LogFlow(("%s-%d: failed to open device '%s', rc=%Rrc\n",
343 pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance, pThis->pszDevice, rc));
344 return rc;
345 }
346 pThis->fReadOnly = true;
347 }
348 else
349 pThis->fReadOnly = pThis->fReadOnlyConfig;
350
351 return rc;
352}
353
354
355DECLHIDDEN(bool) drvHostBaseIsMediaPollingRequiredOs(PDRVHOSTBASE pThis)
356{
357 RT_NOREF(pThis);
358 return true; /* On Linux we always use media polling. */
359}
360
361
362DECLHIDDEN(void) drvHostBaseDestructOs(PDRVHOSTBASE pThis)
363{
364 /*
365 * Unlock the drive if we've locked it or we're in passthru mode.
366 */
367 if ( pThis->fLocked
368 && pThis->Os.hFileDevice != NIL_RTFILE
369 && pThis->pfnDoLock)
370 {
371 int rc = pThis->pfnDoLock(pThis, false);
372 if (RT_SUCCESS(rc))
373 pThis->fLocked = false;
374 }
375
376 if (pThis->Os.pbDoubleBuffer)
377 {
378 RTMemFree(pThis->Os.pbDoubleBuffer);
379 pThis->Os.pbDoubleBuffer = NULL;
380 }
381
382 if (pThis->Os.hFileDevice != NIL_RTFILE)
383 {
384 int rc = RTFileClose(pThis->Os.hFileDevice);
385 AssertRC(rc);
386 pThis->Os.hFileDevice = NIL_RTFILE;
387 }
388}
389
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