VirtualBox

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

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

Make scm happy.

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