VirtualBox

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

最後變更 在這個檔案從76382是 76382,由 vboxsync 提交於 6 年 前

include/VBox/vmm/pdmifs.h: Don't include hgcmsvc.h just for VBOXHGCMSVCPARM as it drags in all kinds of stuff. bugref:9344

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette