VirtualBox

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

Last change on this file since 69496 was 69496, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

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