better support of host drives

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2124 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
bellard 2006-08-19 11:45:59 +00:00
parent 66c6ef7678
commit 19cb37389f
5 changed files with 808 additions and 164 deletions

View File

@ -46,100 +46,42 @@
#ifdef __sun__
#include <sys/dkio.h>
#endif
#ifdef __linux__
#include <sys/ioctl.h>
#include <linux/cdrom.h>
#include <linux/fd.h>
#endif
//#define DEBUG_FLOPPY
#define FTYPE_FILE 0
#define FTYPE_CD 1
#define FTYPE_FD 2
/* if the FD is not accessed during that time (in ms), we try to
reopen it to see if the disk has been changed */
#define FD_OPEN_TIMEOUT 1000
typedef struct BDRVRawState {
int fd;
int type;
#if defined(__linux__)
/* linux floppy specific */
int fd_open_flags;
int64_t fd_open_time;
int64_t fd_error_time;
int fd_got_error;
int fd_media_changed;
#endif
} BDRVRawState;
#ifdef CONFIG_COCOA
static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator );
static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize );
kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator )
{
kern_return_t kernResult;
mach_port_t masterPort;
CFMutableDictionaryRef classesToMatch;
kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort );
if ( KERN_SUCCESS != kernResult ) {
printf( "IOMasterPort returned %d\n", kernResult );
}
classesToMatch = IOServiceMatching( kIOCDMediaClass );
if ( classesToMatch == NULL ) {
printf( "IOServiceMatching returned a NULL dictionary.\n" );
} else {
CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue );
}
kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator );
if ( KERN_SUCCESS != kernResult )
{
printf( "IOServiceGetMatchingServices returned %d\n", kernResult );
}
return kernResult;
}
kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize )
{
io_object_t nextMedia;
kern_return_t kernResult = KERN_FAILURE;
*bsdPath = '\0';
nextMedia = IOIteratorNext( mediaIterator );
if ( nextMedia )
{
CFTypeRef bsdPathAsCFString;
bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia, CFSTR( kIOBSDNameKey ), kCFAllocatorDefault, 0 );
if ( bsdPathAsCFString ) {
size_t devPathLength;
strcpy( bsdPath, _PATH_DEV );
strcat( bsdPath, "r" );
devPathLength = strlen( bsdPath );
if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) {
kernResult = KERN_SUCCESS;
}
CFRelease( bsdPathAsCFString );
}
IOObjectRelease( nextMedia );
}
return kernResult;
}
#endif
static int fd_open(BlockDriverState *bs);
static int raw_open(BlockDriverState *bs, const char *filename, int flags)
{
BDRVRawState *s = bs->opaque;
int fd, open_flags;
int fd, open_flags, ret;
#ifdef CONFIG_COCOA
if (strstart(filename, "/dev/cdrom", NULL)) {
kern_return_t kernResult;
io_iterator_t mediaIterator;
char bsdPath[ MAXPATHLEN ];
int fd;
kernResult = FindEjectableCDMedia( &mediaIterator );
kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) );
if ( bsdPath[ 0 ] != '\0' ) {
strcat(bsdPath,"s0");
/* some CDs don't have a partition 0 */
fd = open(bsdPath, O_RDONLY | O_BINARY | O_LARGEFILE);
if (fd < 0) {
bsdPath[strlen(bsdPath)-1] = '1';
} else {
close(fd);
}
filename = bsdPath;
}
if ( mediaIterator )
IOObjectRelease( mediaIterator );
}
#endif
open_flags = O_BINARY;
if ((flags & BDRV_O_ACCESS) == O_RDWR) {
open_flags |= O_RDWR;
@ -150,9 +92,15 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags)
if (flags & BDRV_O_CREAT)
open_flags |= O_CREAT | O_TRUNC;
s->type = FTYPE_FILE;
fd = open(filename, open_flags, 0644);
if (fd < 0)
return -errno;
if (fd < 0) {
ret = -errno;
if (ret == -EROFS)
ret = -EACCES;
return ret;
}
s->fd = fd;
return 0;
}
@ -180,6 +128,10 @@ static int raw_pread(BlockDriverState *bs, int64_t offset,
BDRVRawState *s = bs->opaque;
int ret;
ret = fd_open(bs);
if (ret < 0)
return ret;
lseek(s->fd, offset, SEEK_SET);
ret = read(s->fd, buf, count);
return ret;
@ -191,13 +143,17 @@ static int raw_pwrite(BlockDriverState *bs, int64_t offset,
BDRVRawState *s = bs->opaque;
int ret;
ret = fd_open(bs);
if (ret < 0)
return ret;
lseek(s->fd, offset, SEEK_SET);
ret = write(s->fd, buf, count);
return ret;
}
/***********************************************************/
/* Unix AOP using POSIX AIO */
/* Unix AIO using POSIX AIO */
typedef struct RawAIOCB {
BlockDriverAIOCB common;
@ -236,15 +192,18 @@ void qemu_aio_init(void)
act.sa_handler = aio_signal_handler;
sigaction(aio_sig_num, &act, NULL);
#if defined(__GLIBC__) && defined(__linux__)
{
/* XXX: aio thread exit seems to hang on RH 9 */
/* XXX: aio thread exit seems to hang on RedHat 9 and this init
seems to fix the problem. */
struct aioinit ai;
memset(&ai, 0, sizeof(ai));
ai.aio_threads = 2;
ai.aio_threads = 1;
ai.aio_num = 1;
ai.aio_idle_time = 365 * 100000;
aio_init(&ai);
}
#endif
}
void qemu_aio_poll(void)
@ -270,7 +229,7 @@ void qemu_aio_poll(void)
if (ret == acb->aiocb.aio_nbytes)
ret = 0;
else
ret = -1;
ret = -EINVAL;
} else {
ret = -ret;
}
@ -329,6 +288,9 @@ static RawAIOCB *raw_aio_setup(BlockDriverState *bs,
BDRVRawState *s = bs->opaque;
RawAIOCB *acb;
if (fd_open(bs) < 0)
return NULL;
acb = qemu_aio_get(bs, cb, opaque);
if (!acb)
return NULL;
@ -405,12 +367,17 @@ static void raw_aio_cancel(BlockDriverAIOCB *blockacb)
static void raw_close(BlockDriverState *bs)
{
BDRVRawState *s = bs->opaque;
close(s->fd);
if (s->fd >= 0) {
close(s->fd);
s->fd = -1;
}
}
static int raw_truncate(BlockDriverState *bs, int64_t offset)
{
BDRVRawState *s = bs->opaque;
if (s->type != FTYPE_FILE)
return -ENOTSUP;
if (ftruncate(s->fd, offset) < 0)
return -errno;
return 0;
@ -428,6 +395,11 @@ static int64_t raw_getlength(BlockDriverState *bs)
struct dk_minfo minfo;
int rv;
#endif
int ret;
ret = fd_open(bs);
if (ret < 0)
return ret;
#ifdef _BSD
if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) {
@ -455,12 +427,6 @@ static int64_t raw_getlength(BlockDriverState *bs)
{
size = lseek(fd, 0, SEEK_END);
}
#ifdef _WIN32
/* On Windows hosts it can happen that we're unable to get file size
for CD-ROM raw device (it's inherent limitation of the CDFS driver). */
if (size == -1)
size = LONG_LONG_MAX;
#endif
return size;
}
@ -509,13 +475,358 @@ BlockDriver bdrv_raw = {
.bdrv_getlength = raw_getlength,
};
/***********************************************/
/* host device */
#ifdef CONFIG_COCOA
static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator );
static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize );
kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator )
{
kern_return_t kernResult;
mach_port_t masterPort;
CFMutableDictionaryRef classesToMatch;
kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort );
if ( KERN_SUCCESS != kernResult ) {
printf( "IOMasterPort returned %d\n", kernResult );
}
classesToMatch = IOServiceMatching( kIOCDMediaClass );
if ( classesToMatch == NULL ) {
printf( "IOServiceMatching returned a NULL dictionary.\n" );
} else {
CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue );
}
kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator );
if ( KERN_SUCCESS != kernResult )
{
printf( "IOServiceGetMatchingServices returned %d\n", kernResult );
}
return kernResult;
}
kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize )
{
io_object_t nextMedia;
kern_return_t kernResult = KERN_FAILURE;
*bsdPath = '\0';
nextMedia = IOIteratorNext( mediaIterator );
if ( nextMedia )
{
CFTypeRef bsdPathAsCFString;
bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia, CFSTR( kIOBSDNameKey ), kCFAllocatorDefault, 0 );
if ( bsdPathAsCFString ) {
size_t devPathLength;
strcpy( bsdPath, _PATH_DEV );
strcat( bsdPath, "r" );
devPathLength = strlen( bsdPath );
if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) {
kernResult = KERN_SUCCESS;
}
CFRelease( bsdPathAsCFString );
}
IOObjectRelease( nextMedia );
}
return kernResult;
}
#endif
static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
{
BDRVRawState *s = bs->opaque;
int fd, open_flags, ret;
#ifdef CONFIG_COCOA
if (strstart(filename, "/dev/cdrom", NULL)) {
kern_return_t kernResult;
io_iterator_t mediaIterator;
char bsdPath[ MAXPATHLEN ];
int fd;
kernResult = FindEjectableCDMedia( &mediaIterator );
kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) );
if ( bsdPath[ 0 ] != '\0' ) {
strcat(bsdPath,"s0");
/* some CDs don't have a partition 0 */
fd = open(bsdPath, O_RDONLY | O_BINARY | O_LARGEFILE);
if (fd < 0) {
bsdPath[strlen(bsdPath)-1] = '1';
} else {
close(fd);
}
filename = bsdPath;
}
if ( mediaIterator )
IOObjectRelease( mediaIterator );
}
#endif
open_flags = O_BINARY;
if ((flags & BDRV_O_ACCESS) == O_RDWR) {
open_flags |= O_RDWR;
} else {
open_flags |= O_RDONLY;
bs->read_only = 1;
}
s->type = FTYPE_FILE;
#if defined(__linux__)
if (strstart(filename, "/dev/cd", NULL)) {
/* open will not fail even if no CD is inserted */
open_flags |= O_NONBLOCK;
s->type = FTYPE_CD;
} else if (strstart(filename, "/dev/fd", NULL)) {
s->type = FTYPE_FD;
s->fd_open_flags = open_flags;
/* open will not fail even if no floppy is inserted */
open_flags |= O_NONBLOCK;
}
#endif
fd = open(filename, open_flags, 0644);
if (fd < 0) {
ret = -errno;
if (ret == -EROFS)
ret = -EACCES;
return ret;
}
s->fd = fd;
#if defined(__linux__)
/* close fd so that we can reopen it as needed */
if (s->type == FTYPE_FD) {
close(s->fd);
s->fd = -1;
s->fd_media_changed = 1;
}
#endif
return 0;
}
#if defined(__linux__) && !defined(QEMU_TOOL)
/* Note: we do not have a reliable method to detect if the floppy is
present. The current method is to try to open the floppy at every
I/O and to keep it opened during a few hundreds of ms. */
static int fd_open(BlockDriverState *bs)
{
BDRVRawState *s = bs->opaque;
int last_media_present;
if (s->type != FTYPE_FD)
return 0;
last_media_present = (s->fd >= 0);
if (s->fd >= 0 &&
(qemu_get_clock(rt_clock) - s->fd_open_time) >= FD_OPEN_TIMEOUT) {
close(s->fd);
s->fd = -1;
#ifdef DEBUG_FLOPPY
printf("Floppy closed\n");
#endif
}
if (s->fd < 0) {
if (s->fd_got_error &&
(qemu_get_clock(rt_clock) - s->fd_error_time) < FD_OPEN_TIMEOUT) {
#ifdef DEBUG_FLOPPY
printf("No floppy (open delayed)\n");
#endif
return -EIO;
}
s->fd = open(bs->filename, s->fd_open_flags);
if (s->fd < 0) {
s->fd_error_time = qemu_get_clock(rt_clock);
s->fd_got_error = 1;
if (last_media_present)
s->fd_media_changed = 1;
#ifdef DEBUG_FLOPPY
printf("No floppy\n");
#endif
return -EIO;
}
#ifdef DEBUG_FLOPPY
printf("Floppy opened\n");
#endif
}
if (!last_media_present)
s->fd_media_changed = 1;
s->fd_open_time = qemu_get_clock(rt_clock);
s->fd_got_error = 0;
return 0;
}
#else
static int fd_open(BlockDriverState *bs)
{
return 0;
}
#endif
#if defined(__linux__)
static int raw_is_inserted(BlockDriverState *bs)
{
BDRVRawState *s = bs->opaque;
int ret;
switch(s->type) {
case FTYPE_CD:
ret = ioctl(s->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
if (ret == CDS_DISC_OK)
return 1;
else
return 0;
break;
case FTYPE_FD:
ret = fd_open(bs);
return (ret >= 0);
default:
return 1;
}
}
/* currently only used by fdc.c, but a CD version would be good too */
static int raw_media_changed(BlockDriverState *bs)
{
BDRVRawState *s = bs->opaque;
switch(s->type) {
case FTYPE_FD:
{
int ret;
/* XXX: we do not have a true media changed indication. It
does not work if the floppy is changed without trying
to read it */
fd_open(bs);
ret = s->fd_media_changed;
s->fd_media_changed = 0;
#ifdef DEBUG_FLOPPY
printf("Floppy changed=%d\n", ret);
#endif
return ret;
}
default:
return -ENOTSUP;
}
}
static int raw_eject(BlockDriverState *bs, int eject_flag)
{
BDRVRawState *s = bs->opaque;
switch(s->type) {
case FTYPE_CD:
if (eject_flag) {
if (ioctl (s->fd, CDROMEJECT, NULL) < 0)
perror("CDROMEJECT");
} else {
if (ioctl (s->fd, CDROMCLOSETRAY, NULL) < 0)
perror("CDROMEJECT");
}
break;
case FTYPE_FD:
{
int fd;
if (s->fd >= 0) {
close(s->fd);
s->fd = -1;
}
fd = open(bs->filename, s->fd_open_flags | O_NONBLOCK);
if (fd >= 0) {
if (ioctl(fd, FDEJECT, 0) < 0)
perror("FDEJECT");
close(fd);
}
}
break;
default:
return -ENOTSUP;
}
return 0;
}
static int raw_set_locked(BlockDriverState *bs, int locked)
{
BDRVRawState *s = bs->opaque;
switch(s->type) {
case FTYPE_CD:
if (ioctl (s->fd, CDROM_LOCKDOOR, locked) < 0) {
/* Note: an error can happen if the distribution automatically
mounts the CD-ROM */
// perror("CDROM_LOCKDOOR");
}
break;
default:
return -ENOTSUP;
}
return 0;
}
#else
static int raw_is_inserted(BlockDriverState *bs)
{
return 1;
}
static int raw_media_changed(BlockDriverState *bs)
{
return -ENOTSUP;
}
static int raw_eject(BlockDriverState *bs, int eject_flag)
{
return -ENOTSUP;
}
static int raw_set_locked(BlockDriverState *bs, int locked)
{
return -ENOTSUP;
}
#endif /* !linux */
BlockDriver bdrv_host_device = {
"host_device",
sizeof(BDRVRawState),
NULL, /* no probe for protocols */
hdev_open,
NULL,
NULL,
raw_close,
NULL,
raw_flush,
.bdrv_aio_read = raw_aio_read,
.bdrv_aio_write = raw_aio_write,
.bdrv_aio_cancel = raw_aio_cancel,
.aiocb_size = sizeof(RawAIOCB),
.bdrv_pread = raw_pread,
.bdrv_pwrite = raw_pwrite,
.bdrv_getlength = raw_getlength,
/* removable device support */
.bdrv_is_inserted = raw_is_inserted,
.bdrv_media_changed = raw_media_changed,
.bdrv_eject = raw_eject,
.bdrv_set_locked = raw_set_locked,
};
#else /* _WIN32 */
/* XXX: use another file ? */
#include <winioctl.h>
#define FTYPE_FILE 0
#define FTYPE_CD 1
typedef struct BDRVRawState {
HANDLE hfile;
int type;
char drive_letter[2];
} BDRVRawState;
typedef struct RawAIOCB {
@ -565,6 +876,23 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags)
BDRVRawState *s = bs->opaque;
int access_flags, create_flags;
DWORD overlapped;
char device_name[64];
const char *p;
if (strstart(filename, "/dev/cdrom", NULL)) {
if (find_cdrom(device_name, sizeof(device_name)) < 0)
return -ENOENT;
filename = device_name;
} else {
/* transform drive letters into device name */
if (((filename[0] >= 'a' && filename[0] <= 'z') ||
(filename[0] >= 'A' && filename[0] <= 'Z')) &&
filename[1] == ':' && filename[2] == '\0') {
snprintf(device_name, sizeof(device_name), "\\\\.\\%c:", filename[0]);
filename = device_name;
}
}
s->type = find_device_type(filename);
if ((flags & BDRV_O_ACCESS) == O_RDWR) {
access_flags = GENERIC_READ | GENERIC_WRITE;
@ -765,10 +1093,22 @@ static int64_t raw_getlength(BlockDriverState *bs)
{
BDRVRawState *s = bs->opaque;
LARGE_INTEGER l;
ULARGE_INTEGER available, total, total_free;
l.LowPart = GetFileSize(s->hfile, &l.HighPart);
if (l.LowPart == 0xffffffffUL && GetLastError() != NO_ERROR)
return -EIO;
switch(s->ftype) {
case FTYPE_FILE:
l.LowPart = GetFileSize(s->hfile, &l.HighPart);
if (l.LowPart == 0xffffffffUL && GetLastError() != NO_ERROR)
return -EIO;
break;
case FTYPE_CD:
if (!GetDiskFreeSpaceEx(s->drive_letter, &available, &total, &total_free))
return -EIO;
l = total;
break;
default:
return -EIO;
}
return l.QuadPart;
}
@ -833,4 +1173,146 @@ BlockDriver bdrv_raw = {
.bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength,
};
/***********************************************/
/* host device */
static int find_cdrom(char *cdrom_name, int cdrom_name_size)
{
char drives[256], *pdrv = drives;
UINT type;
memset(drives, 0, sizeof(drivers));
GetLogicalDriveStrings(sizeof(drives), drives);
while(pdrv[0] != '\0') {
type = GetDriveType(pdrv);
switch(type) {
case DRIVE_CDROM:
snprintf(cdrom_name, cdrom_name_size, "\\\\.\\%c:", pdrv[0]);
return 0;
break;
}
pdrv += lstrlen(pdrv) + 1;
}
return -1;
}
static int find_device_type(const char *filename)
{
UINT type;
const char *p;
if (strstart(filename, "\\\\.\\", &p) ||
strstart(filename, "//./", &p)) {
s->drive_letter[0] = p[0];
s->drive_letter[1] = '\0';
type = GetDriveType(s->drive_letter);
if (type == DRIVE_CDROM)
return FTYPE_CD;
else
return FTYPE_FILE;
} else {
return FTYPE_FILE;
}
}
static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
{
BDRVRawState *s = bs->opaque;
int access_flags, create_flags;
DWORD overlapped;
char device_name[64];
const char *p;
if (strstart(filename, "/dev/cdrom", NULL)) {
if (find_cdrom(device_name, sizeof(device_name)) < 0)
return -ENOENT;
filename = device_name;
} else {
/* transform drive letters into device name */
if (((filename[0] >= 'a' && filename[0] <= 'z') ||
(filename[0] >= 'A' && filename[0] <= 'Z')) &&
filename[1] == ':' && filename[2] == '\0') {
snprintf(device_name, sizeof(device_name), "\\\\.\\%c:", filename[0]);
filename = device_name;
}
}
s->type = find_device_type(filename);
if ((flags & BDRV_O_ACCESS) == O_RDWR) {
access_flags = GENERIC_READ | GENERIC_WRITE;
} else {
access_flags = GENERIC_READ;
}
create_flags = OPEN_EXISTING;
#ifdef QEMU_TOOL
overlapped = 0;
#else
overlapped = FILE_FLAG_OVERLAPPED;
#endif
s->hfile = CreateFile(filename, access_flags,
FILE_SHARE_READ, NULL,
create_flags, overlapped, 0);
if (s->hfile == INVALID_HANDLE_VALUE)
return -1;
return 0;
}
#if 0
/***********************************************/
/* removable device additionnal commands */
static int raw_is_inserted(BlockDriverState *bs)
{
return 1;
}
static int raw_media_changed(BlockDriverState *bs)
{
return -ENOTSUP;
}
static int raw_eject(BlockDriverState *bs, int eject_flag)
{
DWORD ret_count;
if (s->type == FTYPE_FILE)
return -ENOTSUP;
if (eject_flag) {
DeviceIoControl(s->hfile, IOCTL_STORAGE_EJECT_MEDIA,
NULL, 0, NULL, 0, &lpBytesReturned, NULL);
} else {
DeviceIoControl(s->hfile, IOCTL_STORAGE_LOAD_MEDIA,
NULL, 0, NULL, 0, &lpBytesReturned, NULL);
}
}
static int raw_set_locked(BlockDriverState *bs, int locked)
{
return -ENOTSUP;
}
#endif
BlockDriver bdrv_host_device = {
"host_device",
sizeof(BDRVRawState),
NULL, /* no probe for protocols */
hdev_open,
NULL,
NULL,
raw_close,
NULL,
raw_flush,
#if 0
.bdrv_aio_read = raw_aio_read,
.bdrv_aio_write = raw_aio_write,
.bdrv_aio_cancel = raw_aio_cancel,
.aiocb_size = sizeof(RawAIOCB);
#endif
.bdrv_pread = raw_pread,
.bdrv_pwrite = raw_pwrite,
.bdrv_getlength = raw_getlength,
};
#endif /* _WIN32 */

218
block.c
View File

@ -181,24 +181,37 @@ void get_tmp_filename(char *filename, int size)
}
#endif
#ifdef _WIN32
static int is_windows_drive(const char *filename)
{
if (((filename[0] >= 'a' && filename[0] <= 'z') ||
(filename[0] >= 'A' && filename[0] <= 'Z')) &&
filename[1] == ':' && filename[2] == '\0')
return 1;
if (strstart(filename, "\\\\.\\", NULL) ||
strstart(filename, "//./", NULL))
return 1;
return 0;
}
#endif
static BlockDriver *find_protocol(const char *filename)
{
BlockDriver *drv1;
char protocol[128];
int len;
const char *p;
#ifdef _WIN32
if (is_windows_drive(filename))
return &bdrv_raw;
#endif
p = strchr(filename, ':');
if (!p)
return &bdrv_raw;
len = p - filename;
if (len > sizeof(protocol) - 1)
len = sizeof(protocol) - 1;
#ifdef _WIN32
if (len == 1) {
/* specific win32 case for driver letters */
return &bdrv_raw;
}
#endif
memcpy(protocol, filename, len);
protocol[len] = '\0';
for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) {
@ -218,14 +231,28 @@ static BlockDriver *find_image_format(const char *filename)
uint8_t buf[2048];
BlockDriverState *bs;
/* detect host devices. By convention, /dev/cdrom[N] is always
recognized as a host CDROM */
if (strstart(filename, "/dev/cdrom", NULL))
return &bdrv_host_device;
#ifdef _WIN32
if (is_windows_drive(filename))
return &bdrv_host_device;
#else
{
struct stat st;
if (stat(filename, &st) >= 0 &&
(S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))) {
return &bdrv_host_device;
}
}
#endif
drv = find_protocol(filename);
/* no need to test disk image formats for vvfat or host specific
devices */
/* no need to test disk image formats for vvfat */
if (drv == &bdrv_vvfat)
return drv;
if (strstart(filename, "/dev/", NULL))
return &bdrv_raw;
ret = bdrv_file_open(&bs, filename, BDRV_O_RDONLY);
if (ret < 0)
return NULL;
@ -362,9 +389,8 @@ int bdrv_open2(BlockDriverState *bs, const char *filename, int flags,
goto fail;
}
bs->inserted = 1;
/* call the change callback */
bs->media_changed = 1;
if (bs->change_cb)
bs->change_cb(bs->change_opaque);
@ -373,7 +399,7 @@ int bdrv_open2(BlockDriverState *bs, const char *filename, int flags,
void bdrv_close(BlockDriverState *bs)
{
if (bs->inserted) {
if (bs->drv) {
if (bs->backing_hd)
bdrv_delete(bs->backing_hd);
bs->drv->bdrv_close(bs);
@ -385,9 +411,9 @@ void bdrv_close(BlockDriverState *bs)
#endif
bs->opaque = NULL;
bs->drv = NULL;
bs->inserted = 0;
/* call the change callback */
bs->media_changed = 1;
if (bs->change_cb)
bs->change_cb(bs->change_opaque);
}
@ -403,12 +429,13 @@ void bdrv_delete(BlockDriverState *bs)
/* commit COW file into the raw image */
int bdrv_commit(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
int64_t i, total_sectors;
int n, j;
unsigned char sector[512];
if (!bs->inserted)
return -ENOENT;
if (!drv)
return -ENOMEDIUM;
if (bs->read_only) {
return -EACCES;
@ -420,7 +447,7 @@ int bdrv_commit(BlockDriverState *bs)
total_sectors = bdrv_getlength(bs) >> SECTOR_BITS;
for (i = 0; i < total_sectors;) {
if (bs->drv->bdrv_is_allocated(bs, i, 65536, &n)) {
if (drv->bdrv_is_allocated(bs, i, 65536, &n)) {
for(j = 0; j < n; j++) {
if (bdrv_read(bs, i, sector, 1) != 0) {
return -EIO;
@ -436,20 +463,20 @@ int bdrv_commit(BlockDriverState *bs)
}
}
if (bs->drv->bdrv_make_empty)
return bs->drv->bdrv_make_empty(bs);
if (drv->bdrv_make_empty)
return drv->bdrv_make_empty(bs);
return 0;
}
/* return < 0 if error */
/* return < 0 if error. See bdrv_write() for the return codes */
int bdrv_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors)
{
BlockDriver *drv = bs->drv;
if (!bs->inserted)
return -1;
if (!drv)
return -ENOMEDIUM;
if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) {
memcpy(buf, bs->boot_sector_data, 512);
@ -466,7 +493,7 @@ int bdrv_read(BlockDriverState *bs, int64_t sector_num,
if (ret < 0)
return ret;
else if (ret != len)
return -EIO;
return -EINVAL;
else
return 0;
} else {
@ -474,15 +501,20 @@ int bdrv_read(BlockDriverState *bs, int64_t sector_num,
}
}
/* return < 0 if error */
/* Return < 0 if error. Important errors are:
-EIO generic I/O error (may happen for all errors)
-ENOMEDIUM No media inserted.
-EINVAL Invalid sector number or nb_sectors
-EACCES Trying to write a read-only device
*/
int bdrv_write(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors)
{
BlockDriver *drv = bs->drv;
if (!bs->inserted)
return -1;
if (!bs->drv)
return -ENOMEDIUM;
if (bs->read_only)
return -1;
return -EACCES;
if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) {
memcpy(bs->boot_sector_data, buf, 512);
}
@ -501,7 +533,6 @@ int bdrv_write(BlockDriverState *bs, int64_t sector_num,
}
}
/* not necessary now */
static int bdrv_pread_em(BlockDriverState *bs, int64_t offset,
uint8_t *buf, int count1)
{
@ -603,7 +634,7 @@ int bdrv_pread(BlockDriverState *bs, int64_t offset,
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOENT;
return -ENOMEDIUM;
if (!drv->bdrv_pread)
return bdrv_pread_em(bs, offset, buf1, count1);
return drv->bdrv_pread(bs, offset, buf1, count1);
@ -618,7 +649,7 @@ int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOENT;
return -ENOMEDIUM;
if (!drv->bdrv_pwrite)
return bdrv_pwrite_em(bs, offset, buf1, count1);
return drv->bdrv_pwrite(bs, offset, buf1, count1);
@ -631,7 +662,7 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset)
{
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOENT;
return -ENOMEDIUM;
if (!drv->bdrv_truncate)
return -ENOTSUP;
return drv->bdrv_truncate(bs, offset);
@ -644,7 +675,7 @@ int64_t bdrv_getlength(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOENT;
return -ENOMEDIUM;
if (!drv->bdrv_getlength) {
/* legacy mode */
return bs->total_sectors * SECTOR_SIZE;
@ -652,9 +683,16 @@ int64_t bdrv_getlength(BlockDriverState *bs)
return drv->bdrv_getlength(bs);
}
/* return 0 as number of sectors if no device present or error */
void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr)
{
*nb_sectors_ptr = bs->total_sectors;
int64_t length;
length = bdrv_getlength(bs);
if (length < 0)
length = 0;
else
length = length >> SECTOR_BITS;
*nb_sectors_ptr = length;
}
/* force a given boot sector. */
@ -715,21 +753,7 @@ int bdrv_is_read_only(BlockDriverState *bs)
return bs->read_only;
}
int bdrv_is_inserted(BlockDriverState *bs)
{
return bs->inserted;
}
int bdrv_is_locked(BlockDriverState *bs)
{
return bs->locked;
}
void bdrv_set_locked(BlockDriverState *bs, int locked)
{
bs->locked = locked;
}
/* XXX: no longer used */
void bdrv_set_change_cb(BlockDriverState *bs,
void (*change_cb)(void *opaque), void *opaque)
{
@ -761,7 +785,7 @@ int bdrv_set_key(BlockDriverState *bs, const char *key)
void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size)
{
if (!bs->inserted || !bs->drv) {
if (!bs->drv) {
buf[0] = '\0';
} else {
pstrcpy(buf, buf_size, bs->drv->format_name);
@ -833,7 +857,7 @@ void bdrv_info(void)
if (bs->removable) {
term_printf(" locked=%d", bs->locked);
}
if (bs->inserted) {
if (bs->drv) {
term_printf(" file=%s", bs->filename);
if (bs->backing_file[0] != '\0')
term_printf(" backing_file=%s", bs->backing_file);
@ -863,7 +887,7 @@ int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
{
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOENT;
return -ENOMEDIUM;
if (!drv->bdrv_write_compressed)
return -ENOTSUP;
return drv->bdrv_write_compressed(bs, sector_num, buf, nb_sectors);
@ -873,7 +897,7 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
{
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOENT;
return -ENOMEDIUM;
if (!drv->bdrv_get_info)
return -ENOTSUP;
memset(bdi, 0, sizeof(*bdi));
@ -888,7 +912,7 @@ int bdrv_snapshot_create(BlockDriverState *bs,
{
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOENT;
return -ENOMEDIUM;
if (!drv->bdrv_snapshot_create)
return -ENOTSUP;
return drv->bdrv_snapshot_create(bs, sn_info);
@ -899,7 +923,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
{
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOENT;
return -ENOMEDIUM;
if (!drv->bdrv_snapshot_goto)
return -ENOTSUP;
return drv->bdrv_snapshot_goto(bs, snapshot_id);
@ -909,7 +933,7 @@ int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
{
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOENT;
return -ENOMEDIUM;
if (!drv->bdrv_snapshot_delete)
return -ENOTSUP;
return drv->bdrv_snapshot_delete(bs, snapshot_id);
@ -920,7 +944,7 @@ int bdrv_snapshot_list(BlockDriverState *bs,
{
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOENT;
return -ENOMEDIUM;
if (!drv->bdrv_snapshot_list)
return -ENOTSUP;
return drv->bdrv_snapshot_list(bs, psn_info);
@ -1001,7 +1025,7 @@ BlockDriverAIOCB *bdrv_aio_read(BlockDriverState *bs, int64_t sector_num,
{
BlockDriver *drv = bs->drv;
if (!bs->inserted)
if (!drv)
return NULL;
/* XXX: we assume that nb_sectors == 0 is suppored by the async read */
@ -1021,7 +1045,7 @@ BlockDriverAIOCB *bdrv_aio_write(BlockDriverState *bs, int64_t sector_num,
{
BlockDriver *drv = bs->drv;
if (!bs->inserted)
if (!drv)
return NULL;
if (bs->read_only)
return NULL;
@ -1170,6 +1194,7 @@ static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num,
void bdrv_init(void)
{
bdrv_register(&bdrv_raw);
bdrv_register(&bdrv_host_device);
#ifndef _WIN32
bdrv_register(&bdrv_cow);
#endif
@ -1211,3 +1236,78 @@ void qemu_aio_release(void *p)
acb->next = drv->free_aiocb;
drv->free_aiocb = acb;
}
/**************************************************************/
/* removable device support */
/**
* Return TRUE if the media is present
*/
int bdrv_is_inserted(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
int ret;
if (!drv)
return 0;
if (!drv->bdrv_is_inserted)
return 1;
ret = drv->bdrv_is_inserted(bs);
return ret;
}
/**
* Return TRUE if the media changed since the last call to this
* function. It is currently only used for floppy disks
*/
int bdrv_media_changed(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
int ret;
if (!drv || !drv->bdrv_media_changed)
ret = -ENOTSUP;
else
ret = drv->bdrv_media_changed(bs);
if (ret == -ENOTSUP)
ret = bs->media_changed;
bs->media_changed = 0;
return ret;
}
/**
* If eject_flag is TRUE, eject the media. Otherwise, close the tray
*/
void bdrv_eject(BlockDriverState *bs, int eject_flag)
{
BlockDriver *drv = bs->drv;
int ret;
if (!drv || !drv->bdrv_eject) {
ret = -ENOTSUP;
} else {
ret = drv->bdrv_eject(bs, eject_flag);
}
if (ret == -ENOTSUP) {
if (eject_flag)
bdrv_close(bs);
}
}
int bdrv_is_locked(BlockDriverState *bs)
{
return bs->locked;
}
/**
* Lock or unlock the media (if it is locked, the user won't be able
* to eject it manually).
*/
void bdrv_set_locked(BlockDriverState *bs, int locked)
{
BlockDriver *drv = bs->drv;
bs->locked = locked;
if (drv && drv->bdrv_set_locked) {
drv->bdrv_set_locked(bs, locked);
}
}

View File

@ -70,6 +70,12 @@ struct BlockDriver {
QEMUSnapshotInfo **psn_info);
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
/* removable device specific */
int (*bdrv_is_inserted)(BlockDriverState *bs);
int (*bdrv_media_changed)(BlockDriverState *bs);
int (*bdrv_eject)(BlockDriverState *bs, int eject_flag);
int (*bdrv_set_locked)(BlockDriverState *bs, int locked);
BlockDriverAIOCB *free_aiocb;
struct BlockDriver *next;
};
@ -78,7 +84,6 @@ struct BlockDriverState {
int64_t total_sectors; /* if we are reading a disk image, give its
size in sectors */
int read_only; /* if true, the media is read only */
int inserted; /* if true, the media is present */
int removable; /* if true, the media can be removed */
int locked; /* if true, the media cannot temporarily be ejected */
int encrypted; /* if true, the media is encrypted */
@ -86,7 +91,7 @@ struct BlockDriverState {
void (*change_cb)(void *opaque);
void *change_opaque;
BlockDriver *drv;
BlockDriver *drv; /* NULL means no media */
void *opaque;
int boot_sector_enabled;
@ -96,7 +101,8 @@ struct BlockDriverState {
char backing_file[1024]; /* if non zero, the image is a diff of
this file image */
int is_temporary;
int media_changed;
BlockDriverState *backing_hd;
/* async read/write emulation */

View File

@ -206,7 +206,7 @@ Select the emulated machine (@code{-M ?} for list)
@item -fda file
@item -fdb file
Use @var{file} as floppy disk 0/1 image (@pxref{disk_images}). You can
use the host floppy by using @file{/dev/fd0} as filename.
use the host floppy by using @file{/dev/fd0} as filename (@pxref{host_drives}).
@item -hda file
@item -hdb file
@ -217,7 +217,7 @@ Use @var{file} as hard disk 0, 1, 2 or 3 image (@pxref{disk_images}).
@item -cdrom file
Use @var{file} as CD-ROM image (you cannot use @option{-hdc} and and
@option{-cdrom} at the same time). You can use the host CD-ROM by
using @file{/dev/cdrom} as filename.
using @file{/dev/cdrom} as filename (@pxref{host_drives}).
@item -boot [a|c|d]
Boot on floppy (a), hard disk (c) or CD-ROM (d). Hard disk boot is
@ -916,6 +916,7 @@ snapshots.
* disk_images_snapshot_mode:: Snapshot mode
* vm_snapshots:: VM snapshots
* qemu_img_invocation:: qemu-img Invocation
* host_drives:: Using host drives
* disk_images_fat_images:: Virtual FAT disk images
@end menu
@ -997,6 +998,57 @@ state is not saved or restored properly (in particular USB).
@include qemu-img.texi
@node host_drives
@subsection Using host drives
In addition to disk image files, QEMU can directly access host
devices. We describe here the usage for QEMU version >= 0.8.3.
@subsubsection Linux
On Linux, you can directly use the host device filename instead of a
disk image filename provided you have enough proviledge to access
it. For example, use @file{/dev/cdrom} to access to the CDROM or
@file{/dev/fd0} for the floppy.
@table
@item CD
You can specify a CDROM device even if no CDROM is loaded. QEMU has
specific code to detect CDROM insertion or removal. CDROM ejection by
the guest OS is supported. Currently only data CDs are supported.
@item Floppy
You can specify a floppy device even if no floppy is loaded. Floppy
removal is currently not detected accurately (if you change floppy
without doing floppy access while the floppy is not loaded, the guest
OS will think that the same floppy is loaded).
@item Hard disks
Hard disks can be used. Normally you must specify the whole disk
(@file{/dev/hdb} instead of @file{/dev/hdb1}) so that the guest OS can
see it as a partitioned disk. WARNING: unless you know what you do, it
is better to only make READ-ONLY accesses to the hard disk otherwise
you may corrupt your host data (use the @option{-snapshot} command
line option or modify the device permissions accordingly).
@end table
@subsubsection Windows
On Windows you can use any host drives as QEMU drive. The prefered
syntax is the driver letter (e.g. @file{d:}). The alternate syntax
@file{\\.\d:} is supported. @file{/dev/cdrom} is supported as an alias
to the first CDROM drive.
Currently there is no specific code to handle removable medias, so it
is better to use the @code{change} or @code{eject} monitor commands to
change or eject media.
@subsubsection Mac OS X
@file{/dev/cdrom} is an alias to the first CDROM.
Currently there is no specific code to handle removable medias, so it
is better to use the @code{change} or @code{eject} monitor commands to
change or eject media.
@node disk_images_fat_images
@subsection Virtual FAT disk images

4
vl.h
View File

@ -50,6 +50,7 @@
#define fsync _commit
#define lseek _lseeki64
#define ENOTSUP 4096
#define ENOMEDIUM 4097
extern int qemu_ftruncate64(int, int64_t);
#define ftruncate qemu_ftruncate64
@ -502,6 +503,7 @@ typedef struct BlockDriverState BlockDriverState;
typedef struct BlockDriver BlockDriver;
extern BlockDriver bdrv_raw;
extern BlockDriver bdrv_host_device;
extern BlockDriver bdrv_cow;
extern BlockDriver bdrv_qcow;
extern BlockDriver bdrv_vmdk;
@ -604,8 +606,10 @@ int bdrv_get_translation_hint(BlockDriverState *bs);
int bdrv_is_removable(BlockDriverState *bs);
int bdrv_is_read_only(BlockDriverState *bs);
int bdrv_is_inserted(BlockDriverState *bs);
int bdrv_media_changed(BlockDriverState *bs);
int bdrv_is_locked(BlockDriverState *bs);
void bdrv_set_locked(BlockDriverState *bs, int locked);
void bdrv_eject(BlockDriverState *bs, int eject_flag);
void bdrv_set_change_cb(BlockDriverState *bs,
void (*change_cb)(void *opaque), void *opaque);
void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size);