This patch adds a QEMU monitor command may switch the domain owning the
physical cdrom, such that an eject command on the guest command line
will physically eject the drive door.
Monitor command example:
set_cdrom_owner 1 hdd
(for the time being the "hdd" parameter is a constant, as we only
support a singular CD-Rom)
NOTE: This was also submitted to the Xen list, with a couple additional
xen-specific changes, that are not necessarily applicable here (xenstore
entries, etc)
Signed-off-by: Ben Guthro <[EMAIL PROTECTED]>
Signed-off-by: Joshua Nicholas <[EMAIL PROTECTED]>
diff -r 106bc46793ca block.c
--- a/block.c Mon Aug 27 16:20:33 2007 -0400
+++ b/block.c Tue Aug 28 09:48:05 2007 -0400
@@ -56,6 +56,8 @@ static BlockDriverState *bdrv_first;
static BlockDriverState *bdrv_first;
static BlockDriver *first_drv;
+static BlockDriver *get_bdrv_raw(void);
+
int path_is_absolute(const char *path)
{
const char *p;
@@ -323,6 +325,7 @@ int bdrv_open2(BlockDriverState *bs, con
{
int ret, open_flags;
char tmp_filename[PATH_MAX];
+ int is_media_inserted;
char backing_filename[PATH_MAX];
bs->read_only = 0;
@@ -359,6 +362,10 @@ int bdrv_open2(BlockDriverState *bs, con
}
pstrcpy(bs->filename, sizeof(bs->filename), filename);
+ if (bs->type == BDRV_TYPE_CDROM && !drv && bs->is_shared) {
+ drv = get_bdrv_raw();
+ }else{
+
if (flags & BDRV_O_FILE) {
drv = find_protocol(filename);
if (!drv)
@@ -370,16 +377,27 @@ int bdrv_open2(BlockDriverState *bs, con
return -1;
}
}
+
+ }
bs->drv = drv;
bs->opaque = qemu_mallocz(drv->instance_size);
if (bs->opaque == NULL && drv->instance_size > 0)
return -1;
+ is_media_inserted = 1;
/* Note: for compatibility, we open disk image files as RDWR, and
RDONLY as fallback */
if (!(flags & BDRV_O_FILE))
open_flags = BDRV_O_RDWR;
else
open_flags = flags & ~(BDRV_O_FILE | BDRV_O_SNAPSHOT);
+
+ if (bs->type == BDRV_TYPE_CDROM && bs->is_shared) {
+ if (!bs->is_owner) {
+ bs->total_sectors = 0;
+ return ENOMEDIUM;
+ }
+ }
+
ret = drv->bdrv_open(bs, filename, open_flags);
if (ret == -EACCES && !(flags & BDRV_O_FILE)) {
ret = drv->bdrv_open(bs, filename, BDRV_O_RDONLY);
@@ -391,6 +409,20 @@ int bdrv_open2(BlockDriverState *bs, con
bs->drv = NULL;
return ret;
}
+
+ if (bs->type == BDRV_TYPE_CDROM && ret < 1) {
+ if (!bs->is_owner) {
+ bs->total_sectors = 0;
+ if (errno == ENOMEDIUM )
+ return ENOMEDIUM;
+ else
+ return -1;
+ }
+ }
+
+ if (bs->type == BDRV_TYPE_CDROM && ret == ENOMEDIUM)
+ is_media_inserted = 0;
+
if (drv->bdrv_getlength) {
bs->total_sectors = bdrv_getlength(bs) >> SECTOR_BITS;
}
@@ -413,8 +445,12 @@ int bdrv_open2(BlockDriverState *bs, con
goto fail;
}
+ if (is_media_inserted)
+ bs->media_changed = 1;
+ else
+ bs->media_changed = 0;
+
/* call the change callback */
- bs->media_changed = 1;
if (bs->change_cb)
bs->change_cb(bs->change_opaque);
@@ -427,19 +463,20 @@ void bdrv_close(BlockDriverState *bs)
if (bs->backing_hd)
bdrv_delete(bs->backing_hd);
bs->drv->bdrv_close(bs);
- qemu_free(bs->opaque);
+ bs->drv = NULL;
#ifdef _WIN32
if (bs->is_temporary) {
unlink(bs->filename);
}
#endif
- bs->opaque = NULL;
- bs->drv = NULL;
/* call the change callback */
bs->media_changed = 1;
if (bs->change_cb)
bs->change_cb(bs->change_opaque);
+
+ qemu_free(bs->opaque);
+ bs->opaque = NULL;
}
}
@@ -749,6 +786,28 @@ void bdrv_set_translation_hint(BlockDriv
bs->translation = translation;
}
+void bdrv_set_shared_owner_hint(BlockDriverState *bs, int is_owner)
+{
+ bs->is_shared = 1;
+ bs->is_owner = is_owner;
+}
+
+void bdrv_change_shared_owner(BlockDriverState *bs, int new_owner_state)
+{
+ if (!bs->is_shared)
+ return;
+
+ if (bs->is_owner == new_owner_state)
+ return;
+
+ bs->is_owner = new_owner_state;
+
+ if (!new_owner_state) {
+ bdrv_close(bs);
+ return;
+ }
+}
+
void bdrv_get_geometry_hint(BlockDriverState *bs,
int *pcyls, int *pheads, int *psecs)
{
@@ -765,6 +824,16 @@ int bdrv_get_translation_hint(BlockDrive
int bdrv_get_translation_hint(BlockDriverState *bs)
{
return bs->translation;
+}
+
+int bdrv_is_shared(BlockDriverState *bs)
+{
+ return bs->is_shared;
+}
+
+int bdrv_is_owner(BlockDriverState *bs)
+{
+ return bs->is_owner;
}
int bdrv_is_removable(BlockDriverState *bs)
@@ -849,6 +918,11 @@ const char *bdrv_get_device_name(BlockDr
const char *bdrv_get_device_name(BlockDriverState *bs)
{
return bs->device_name;
+}
+
+const char *bdrv_get_filename(BlockDriverState *bs)
+{
+ return bs->filename;
}
void bdrv_flush(BlockDriverState *bs)
@@ -1226,6 +1300,11 @@ static int bdrv_write_em(BlockDriverStat
return async_ret;
}
+static BlockDriver *get_bdrv_raw(void)
+{
+ return &bdrv_raw ;
+}
+
void bdrv_init(void)
{
bdrv_register(&bdrv_raw);
diff -r 106bc46793ca block_int.h
--- a/block_int.h Mon Aug 27 16:20:33 2007 -0400
+++ b/block_int.h Tue Aug 28 09:48:05 2007 -0400
@@ -87,6 +87,8 @@ struct BlockDriverState {
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 */
+ int is_shared; /* if true, the media is shared across multiple QEMU instances */
+ int is_owner; /* if true, the media is sharable and currently owned by this QEMU instance */
/* event callback when inserting/removing */
void (*change_cb)(void *opaque);
void *change_opaque;
diff -r 106bc46793ca hw/ide.c
--- a/hw/ide.c Mon Aug 27 16:20:33 2007 -0400
+++ b/hw/ide.c Tue Aug 28 09:56:21 2007 -0400
@@ -24,11 +24,25 @@
*/
#include "vl.h"
+#include <linux/cdrom.h>
+#include <linux/fs.h>
+#include <scsi/sg.h>
+#include <scsi/scsi.h>
+#include <sys/ioctl.h>
+
/* debug IDE devices */
//#define DEBUG_IDE
//#define DEBUG_IDE_ATAPI
//#define DEBUG_AIO
#define USE_DMA_CDROM
+
+#define DEBUG_CDROM
+#ifdef DEBUG_CDROM
+#include "exec-all.h" // for FILE* logfile
+#define DEBUG_CDROM_PRINT( formatCstr, args... ) fprintf( logfile, formatCstr, ##args ); fflush( logfile )
+#else
+#define DEBUG_CDROM_PRINT( formatCstr, args... )
+#endif
/* Bits of HD_STATUS */
#define ERR_STAT 0x01
@@ -306,6 +320,7 @@ typedef struct IDEState {
typedef struct IDEState {
/* ide config */
int is_cdrom;
+ int is_physical_media_present;
int is_cf;
int cylinders, heads, sectors;
int64_t nb_sectors;
@@ -1054,7 +1069,11 @@ static int cd_read_sector(BlockDriverSta
int sector_size)
{
int ret;
-
+ if (bdrv_is_shared(bs) && !bdrv_is_owner(bs)) {
+ DEBUG_CDROM_PRINT("CDrom : own : cd_read_sector(%d %ld %d) not owner = ignored | fn: %s devN: %s\n", lba, (long)((int64_t)lba << 2), sector_size, bdrv_get_filename(bs), bdrv_get_device_name(bs));
+ memset(buf, -1, sector_size); // Make sure the data looks bad
+ return;
+ }
switch(sector_size) {
case 2048:
ret = bdrv_read(bs, (int64_t)lba << 2, buf, 4);
@@ -1295,6 +1314,12 @@ static void ide_atapi_cmd_read(IDEState
printf("read %s: LBA=%d nb_sectors=%d\n", s->atapi_dma ? "dma" : "pio",
lba, nb_sectors);
#endif
+ if (s->is_cdrom && bdrv_is_shared(s->bs) && !bdrv_is_owner(s->bs)) {
+ DEBUG_CDROM_PRINT("CDrom : own : ide_atapi_cmd_read() not owner = request denied | fn: %s devN: %s\n", bdrv_get_filename(s->bs), bdrv_get_device_name(s->bs));
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIUM_NOT_PRESENT);
+ return;
+ }
if (s->atapi_dma) {
ide_atapi_cmd_read_dma(s, lba, nb_sectors, sector_size);
} else {
@@ -1302,11 +1327,160 @@ static void ide_atapi_cmd_read(IDEState
}
}
+static void unlock_physical_cdrom_door(const char* filename)
+{
+ int status;
+ int cdrom_fd;
+
+ cdrom_fd = open(filename, O_RDONLY | O_NONBLOCK);
+ if (cdrom_fd >= 0) {
+ status = ioctl(cdrom_fd, CDROM_LOCKDOOR, 0); // Always unlock the door regardless of ownership
+ if (status < 0)
+ DEBUG_CDROM_PRINT( "CDrom : %s : ERROR? : could NOT unlock CD door : %s : %d %s\n", __FUNCTION__, filename, errno, strerror(errno));
+ close(cdrom_fd);
+ } else {
+ DEBUG_CDROM_PRINT("CDrom : %s : open(%s) failed : door NOT unlocked : %d %s\n", __FUNCTION__, filename, errno, strerror(errno));
+ }
+}
+
+/* Eject using SCSI SG_IO commands. Return 1 if successful, 0 otherwise. From /usr/bin/eject sources */
+static int perform_physical_cdrom_eject_via_scsi(int fd)
+{
+ int status, k;
+ sg_io_hdr_t io_hdr;
+ unsigned char allowRmBlk[6] = {ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 0, 0};
+ unsigned char startStop1Blk[6] = {START_STOP, 0, 0, 0, 1, 0};
+ unsigned char startStop2Blk[6] = {START_STOP, 0, 0, 0, 2, 0};
+ unsigned char inqBuff[2];
+ unsigned char sense_buffer[32];
+
+ if ((ioctl(fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
+ return ENODEV; // not an sg device, or old sg driver
+ }
+
+ memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+ io_hdr.interface_id = 'S';
+ io_hdr.cmd_len = 6;
+ io_hdr.mx_sb_len = sizeof(sense_buffer);
+ io_hdr.dxfer_direction = SG_DXFER_NONE;
+ io_hdr.dxfer_len = 0;
+ io_hdr.dxferp = inqBuff;
+ io_hdr.sbp = sense_buffer;
+ io_hdr.timeout = 2000;
+
+ io_hdr.cmdp = allowRmBlk;
+ status = ioctl(fd, SG_IO, (void *)&io_hdr);
+ if (status < 0)
+ return ((errno)?errno:status);
+
+ io_hdr.cmdp = startStop1Blk;
+ status = ioctl(fd, SG_IO, (void *)&io_hdr);
+ if (status < 0)
+ return ((errno)?errno:status);
+
+ io_hdr.cmdp = startStop2Blk;
+ status = ioctl(fd, SG_IO, (void *)&io_hdr);
+ if (status < 0)
+ return ((errno)?errno:status);
+
+ /* force kernel to reread partition table when new disc inserted */
+ status = ioctl(fd, BLKRRPART);
+ return 0;
+}
+
+static int perform_physical_cdrom_eject(const char* filename)
+{
+ int status;
+ int cdrom_fd;
+
+ status = 1;
+ cdrom_fd = open(filename, O_RDONLY | O_NONBLOCK);
+ if ( cdrom_fd >= 0 ) {
+ status = ioctl(cdrom_fd, CDROMEJECT, CDSL_CURRENT); // bdrv MUST be closed
+ if (status < 0) { // NOTE: ioctl fails when door is open
+ DEBUG_CDROM_PRINT("CDrom : ej : ioctl(%s, CDROMEJECT,CDSL_CURRENT) failed %d, trying eject_cdrom_via_scsi()\n", filename, errno);
+ status = perform_physical_cdrom_eject_via_scsi(cdrom_fd); // last resort
+ }
+ close(cdrom_fd);
+ }
+
+ return status;
+}
+
+static int is_cdrom_physical_media_present(const char* filename)
+{
+ int status;
+ int cdrom_fd;
+
+ cdrom_fd = open(filename, O_RDONLY | O_NONBLOCK); // O_NONBLOCK ignores ENOMEDIUM
+ if (cdrom_fd >= 0) {
+ status = ioctl(cdrom_fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
+ if (status >= 0) {
+ if (status == CDS_DISC_OK) {
+ if ( ioctl(cdrom_fd, CDROM_MEDIA_CHANGED, CDSL_CURRENT) == 0 // trick to
+ || ioctl(cdrom_fd, CDROM_MEDIA_CHANGED, CDSL_CURRENT) == 0 // verify media
+ ) {
+ close(cdrom_fd);
+ return 1;
+ }
+ }
+ } else {
+ DEBUG_CDROM_PRINT("CDrom : %s : ioctl(%s, CDROM_MEDIA_CHANGED) failed %d %s\n", __FUNCTION__, filename, errno, strerror(errno));
+ }
+ } else {
+ DEBUG_CDROM_PRINT("CDrom : %s : open(%s, O_RDONLY|O_NONBLOCK) failed %d %s\n", __FUNCTION__, filename, errno, strerror(errno));
+ }
+
+ close(cdrom_fd);
+ return 0;
+}
+
+static int ascertain_is_media_present(IDEState *s)
+{
+ int is_physical_media_present_now;
+
+ if (!s->is_cdrom || !bdrv_is_shared(s->bs)) {
+ if (bdrv_is_inserted(s->bs))
+ return 1;
+ return 0;
+ }
+ if (!bdrv_is_owner(s->bs))
+ return 0;
+
+ is_physical_media_present_now = is_cdrom_physical_media_present(bdrv_get_filename(s->bs));
+ if (is_physical_media_present_now) {
+ if (s->is_physical_media_present) {
+// DEBUG_CDROM_PRINT("CDrom : present : media still inserted | fn: %s devN: %s\n", bdrv_get_filename(s->bs), bdrv_get_device_name(s->bs));
+ return 1;
+ }
+ s->is_physical_media_present = 1;
+
+ if (!bdrv_is_inserted(s->bs))
+ bdrv_open(s->bs, bdrv_get_filename(s->bs), 0);
+ unlock_physical_cdrom_door(bdrv_get_filename(s->bs));
+
+ DEBUG_CDROM_PRINT("CDrom : present : media inserted | fn: %s devN: %s\n", bdrv_get_filename(s->bs), bdrv_get_device_name(s->bs));
+ return -1;
+ }
+
+ if (!s->is_physical_media_present) {
+// DEBUG_CDROM_PRINT("CDrom : present : media is still NOT inserted | fn: %s devN: %s\n", bdrv_get_filename(s->bs), bdrv_get_device_name(s->bs));
+ return 0;
+ }
+ s->is_physical_media_present = 0;
+
+ bdrv_close(s->bs);
+
+ DEBUG_CDROM_PRINT("CDrom : present : media is NOT inserted | fn: %s devN: %s\n", bdrv_get_filename(s->bs), bdrv_get_device_name(s->bs));
+ return 0;
+}
+
static void ide_atapi_cmd(IDEState *s)
{
const uint8_t *packet;
uint8_t *buf;
int max_len;
+ int is_media_present;
packet = s->io_buffer;
buf = s->io_buffer;
@@ -1499,7 +1673,16 @@ static void ide_atapi_cmd(IDEState *s)
if (eject && !start) {
/* eject the disk */
- bdrv_eject(s->bs, 1);
+ bdrv_close(s->bs); // close the device ( allows for eject AND flushes system cache )
+ if (bdrv_is_shared(s->bs) && bdrv_is_owner(s->bs)) {
+ if (perform_physical_cdrom_eject(bdrv_get_filename(s->bs)) == 0) {
+ s->is_physical_media_present = 0;
+ DEBUG_CDROM_PRINT("CDrom : ej : sucesss | fn: %s devN: %s\n", bdrv_get_filename(s->bs), bdrv_get_device_name(s->bs));
+ } else {
+ DEBUG_CDROM_PRINT("CDrom : ej : FAILED | fn: %s devN: %s\n", bdrv_get_filename(s->bs), bdrv_get_device_name(s->bs));
+ }
+ }
+
} else if (eject && start) {
/* close the tray */
bdrv_eject(s->bs, 0);
@@ -1662,9 +1845,31 @@ static void cdrom_change_cb(void *opaque
{
IDEState *s = opaque;
int64_t nb_sectors;
+ int cylinders;
+
+ if (bdrv_is_shared(s->bs) && !bdrv_is_owner(s->bs)) {
+ s->is_physical_media_present = 0;
+ return;
+ }
+ if (!bdrv_is_inserted(s->bs))
+ return;
/* XXX: send interrupt too */
bdrv_get_geometry(s->bs, &nb_sectors);
+
+ /* if no geometry, use a standard physical disk geometry */
+ cylinders = nb_sectors / (16 * 63);
+ if (cylinders > 16383)
+ cylinders = 16383;
+ else if (cylinders < 2)
+ cylinders = 2;
+ s->cylinders = cylinders;
+ s->heads = 16;
+ s->sectors = 63;
+ bdrv_set_geometry_hint(s->bs, s->cylinders, s->heads, s->sectors); // Reset the geometry in case the media changed
+
+ DEBUG_CDROM_PRINT("CDrom : geometry change %ld [ %ld ] %d %d %d | fn: %s devN: %s\n", (long)nb_sectors, (long)s->nb_sectors, s->cylinders, s->heads, s->sectors, bdrv_get_filename(s->bs), bdrv_get_device_name(s->bs));
+
s->nb_sectors = nb_sectors;
}
@@ -2413,6 +2618,11 @@ static void ide_init2(IDEState *ide_stat
if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) {
s->is_cdrom = 1;
bdrv_set_change_cb(s->bs, cdrom_change_cb, s);
+ if (bdrv_is_shared(s->bs)) {
+ unlock_physical_cdrom_door(bdrv_get_filename(s->bs));
+ DEBUG_CDROM_PRINT("CDrom : init : geometry [ %ld ] %d %d %d | fn: %s devN: %s\n", (long)s->nb_sectors, s->cylinders, s->heads, s->sectors, bdrv_get_filename(s->bs), bdrv_get_device_name(s->bs));
+ }
+
}
}
s->drive_serial = drive_serial++;
diff -r 106bc46793ca monitor.c
--- a/monitor.c Mon Aug 27 16:20:33 2007 -0400
+++ b/monitor.c Tue Aug 28 09:48:05 2007 -0400
@@ -192,6 +192,40 @@ static void help_cmd(const char *name)
}
}
}
+}
+
+static void do_set_cdrom_owner(const char* new_owner_state_cstr, const char *device)
+{
+ BlockDriverState *bs;
+ int new_owner_state;
+
+ if (!new_owner_state_cstr || !device) {
+ term_printf("[set_cdrom_owner] ERROR : insufficient args (NULL) !?!\n");
+ return;
+ }
+
+ if (sscanf(new_owner_state_cstr, "%d", &new_owner_state) != 1) {
+ term_printf("[set_cdrom_owner] ERROR : bad arg '%s'\n", new_owner_state_cstr);
+ return;
+ }
+
+ if (new_owner_state) // normalize given input
+ new_owner_state = 1;
+
+ bs = bdrv_find(device);
+ if (!bs) {
+ term_printf("[set_cdrom_owner] ERROR : cdrom device '%s' not found %d\n", device, new_owner_state);
+ return;
+ }
+
+ if (bdrv_is_owner(bs) == new_owner_state) {
+ term_printf("[set_cdrom_owner] IGNORED : no change to ownership %d\n", new_owner_state);
+ return;
+ }
+
+ bdrv_change_shared_owner(bs, new_owner_state);
+
+ term_printf("[set_cdrom_owner] done : (%d)\n", new_owner_state);
}
static void do_help(const char *name)
@@ -1250,6 +1284,8 @@ static term_cmd_t term_cmds[] = {
"tag|id", "save a VM snapshot. If no tag or id are provided, a new snapshot is created" },
{ "loadvm", "s", do_loadvm,
"tag|id", "restore a VM snapshot from its tag or id" },
+ { "set_cdrom_owner", "sB", do_set_cdrom_owner,
+ "<1|0> device", "1 to become the owner and acquire the physical drive or 0 to release it and no longer be the owner" },
{ "delvm", "s", do_delvm,
"tag|id", "delete a VM snapshot from its tag or id" },
{ "stop", "", do_stop,
diff -r 106bc46793ca vl.c
--- a/vl.c Mon Aug 27 16:20:33 2007 -0400
+++ b/vl.c Tue Aug 28 09:48:05 2007 -0400
@@ -7110,6 +7110,7 @@ enum {
QEMU_OPTION_hdc,
QEMU_OPTION_hdd,
QEMU_OPTION_cdrom,
+ QEMU_OPTION_cdrom_shared_with_eject,
QEMU_OPTION_mtdblock,
QEMU_OPTION_sd,
QEMU_OPTION_pflash,
@@ -7197,6 +7198,7 @@ const QEMUOption qemu_options[] = {
{ "hdc", HAS_ARG, QEMU_OPTION_hdc },
{ "hdd", HAS_ARG, QEMU_OPTION_hdd },
{ "cdrom", HAS_ARG, QEMU_OPTION_cdrom },
+ { "cdrom-shared-with-eject", 0, QEMU_OPTION_cdrom_shared_with_eject },
{ "mtdblock", HAS_ARG, QEMU_OPTION_mtdblock },
{ "sd", HAS_ARG, QEMU_OPTION_sd },
{ "pflash", HAS_ARG, QEMU_OPTION_pflash },
@@ -7778,6 +7780,9 @@ int main(int argc, char **argv)
hd_filename[cdrom_index] = optarg;
}
break;
+ case QEMU_OPTION_cdrom_shared_with_eject:
+ // XXX Temporarily ignore this option
+ break;
case QEMU_OPTION_boot:
boot_device = optarg[0];
if (boot_device != 'a' &&
diff -r 106bc46793ca vl.h
--- a/vl.h Mon Aug 27 16:20:33 2007 -0400
+++ b/vl.h Tue Aug 28 09:48:05 2007 -0400
@@ -661,10 +661,14 @@ void bdrv_set_geometry_hint(BlockDriverS
int cyls, int heads, int secs);
void bdrv_set_type_hint(BlockDriverState *bs, int type);
void bdrv_set_translation_hint(BlockDriverState *bs, int translation);
+void bdrv_set_shared_owner_hint(BlockDriverState *bs, int is_owner);
+void bdrv_change_shared_owner(BlockDriverState *bs, int new_owner_state);
void bdrv_get_geometry_hint(BlockDriverState *bs,
int *pcyls, int *pheads, int *psecs);
int bdrv_get_type_hint(BlockDriverState *bs);
int bdrv_get_translation_hint(BlockDriverState *bs);
+int bdrv_is_shared(BlockDriverState *bs);
+int bdrv_is_owner(BlockDriverState *bs);
int bdrv_is_removable(BlockDriverState *bs);
int bdrv_is_read_only(BlockDriverState *bs);
int bdrv_is_inserted(BlockDriverState *bs);
@@ -683,6 +687,7 @@ void bdrv_iterate_format(void (*it)(void
void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
void *opaque);
const char *bdrv_get_device_name(BlockDriverState *bs);
+const char *bdrv_get_filename(BlockDriverState *bs);
int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors);
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);