2010-06-02 18:48:27 +02:00
|
|
|
/*
|
|
|
|
* QEMU host block devices
|
|
|
|
*
|
|
|
|
* Copyright (c) 2003-2008 Fabrice Bellard
|
|
|
|
*
|
|
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or
|
|
|
|
* later. See the COPYING file in the top-level directory.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "block.h"
|
|
|
|
#include "blockdev.h"
|
|
|
|
#include "monitor.h"
|
|
|
|
#include "qerror.h"
|
|
|
|
#include "qemu-option.h"
|
|
|
|
#include "qemu-config.h"
|
|
|
|
#include "sysemu.h"
|
2010-11-12 18:07:13 +01:00
|
|
|
#include "hw/qdev.h"
|
|
|
|
#include "block_int.h"
|
2010-06-02 18:48:27 +02:00
|
|
|
|
2010-06-02 18:55:22 +02:00
|
|
|
static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
|
2010-06-02 18:48:27 +02:00
|
|
|
|
2011-01-28 11:21:39 +01:00
|
|
|
static const char *const if_name[IF_COUNT] = {
|
|
|
|
[IF_NONE] = "none",
|
|
|
|
[IF_IDE] = "ide",
|
|
|
|
[IF_SCSI] = "scsi",
|
|
|
|
[IF_FLOPPY] = "floppy",
|
|
|
|
[IF_PFLASH] = "pflash",
|
|
|
|
[IF_MTD] = "mtd",
|
|
|
|
[IF_SD] = "sd",
|
|
|
|
[IF_VIRTIO] = "virtio",
|
|
|
|
[IF_XEN] = "xen",
|
|
|
|
};
|
|
|
|
|
|
|
|
static const int if_max_devs[IF_COUNT] = {
|
2011-01-28 11:21:40 +01:00
|
|
|
/*
|
|
|
|
* Do not change these numbers! They govern how drive option
|
|
|
|
* index maps to unit and bus. That mapping is ABI.
|
|
|
|
*
|
|
|
|
* All controllers used to imlement if=T drives need to support
|
|
|
|
* if_max_devs[T] units, for any T with if_max_devs[T] != 0.
|
|
|
|
* Otherwise, some index values map to "impossible" bus, unit
|
|
|
|
* values.
|
|
|
|
*
|
|
|
|
* For instance, if you change [IF_SCSI] to 255, -drive
|
|
|
|
* if=scsi,index=12 no longer means bus=1,unit=5, but
|
|
|
|
* bus=0,unit=12. With an lsi53c895a controller (7 units max),
|
|
|
|
* the drive can't be set up. Regression.
|
|
|
|
*/
|
|
|
|
[IF_IDE] = 2,
|
|
|
|
[IF_SCSI] = 7,
|
2011-01-28 11:21:39 +01:00
|
|
|
};
|
|
|
|
|
2010-06-25 08:09:10 +02:00
|
|
|
/*
|
|
|
|
* We automatically delete the drive when a device using it gets
|
|
|
|
* unplugged. Questionable feature, but we can't just drop it.
|
|
|
|
* Device models call blockdev_mark_auto_del() to schedule the
|
|
|
|
* automatic deletion, and generic qdev code calls blockdev_auto_del()
|
|
|
|
* when deletion is actually safe.
|
|
|
|
*/
|
|
|
|
void blockdev_mark_auto_del(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
DriveInfo *dinfo = drive_get_by_blockdev(bs);
|
|
|
|
|
2010-12-08 17:05:00 +01:00
|
|
|
if (dinfo) {
|
|
|
|
dinfo->auto_del = 1;
|
|
|
|
}
|
2010-06-25 08:09:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void blockdev_auto_del(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
DriveInfo *dinfo = drive_get_by_blockdev(bs);
|
|
|
|
|
2010-12-08 17:05:00 +01:00
|
|
|
if (dinfo && dinfo->auto_del) {
|
2011-01-26 15:12:32 +01:00
|
|
|
drive_put_ref(dinfo);
|
2010-06-25 08:09:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-28 11:21:43 +01:00
|
|
|
static int drive_index_to_bus_id(BlockInterfaceType type, int index)
|
|
|
|
{
|
|
|
|
int max_devs = if_max_devs[type];
|
|
|
|
return max_devs ? index / max_devs : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int drive_index_to_unit_id(BlockInterfaceType type, int index)
|
|
|
|
{
|
|
|
|
int max_devs = if_max_devs[type];
|
|
|
|
return max_devs ? index % max_devs : index;
|
|
|
|
}
|
|
|
|
|
2011-01-28 11:21:41 +01:00
|
|
|
QemuOpts *drive_def(const char *optstr)
|
|
|
|
{
|
|
|
|
return qemu_opts_parse(qemu_find_opts("drive"), optstr, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
|
2011-01-31 11:50:09 +01:00
|
|
|
const char *optstr)
|
2010-06-02 18:48:27 +02:00
|
|
|
{
|
|
|
|
QemuOpts *opts;
|
2011-01-28 11:21:41 +01:00
|
|
|
char buf[32];
|
2010-06-02 18:48:27 +02:00
|
|
|
|
2011-01-28 11:21:41 +01:00
|
|
|
opts = drive_def(optstr);
|
2010-06-02 18:48:27 +02:00
|
|
|
if (!opts) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-01-28 11:21:41 +01:00
|
|
|
if (type != IF_DEFAULT) {
|
|
|
|
qemu_opt_set(opts, "if", if_name[type]);
|
|
|
|
}
|
|
|
|
if (index >= 0) {
|
|
|
|
snprintf(buf, sizeof(buf), "%d", index);
|
|
|
|
qemu_opt_set(opts, "index", buf);
|
|
|
|
}
|
2010-06-02 18:48:27 +02:00
|
|
|
if (file)
|
|
|
|
qemu_opt_set(opts, "file", file);
|
|
|
|
return opts;
|
|
|
|
}
|
|
|
|
|
|
|
|
DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit)
|
|
|
|
{
|
|
|
|
DriveInfo *dinfo;
|
|
|
|
|
|
|
|
/* seek interface, bus and unit */
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(dinfo, &drives, next) {
|
|
|
|
if (dinfo->type == type &&
|
|
|
|
dinfo->bus == bus &&
|
|
|
|
dinfo->unit == unit)
|
|
|
|
return dinfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-01-28 11:21:44 +01:00
|
|
|
DriveInfo *drive_get_by_index(BlockInterfaceType type, int index)
|
|
|
|
{
|
|
|
|
return drive_get(type,
|
|
|
|
drive_index_to_bus_id(type, index),
|
|
|
|
drive_index_to_unit_id(type, index));
|
|
|
|
}
|
|
|
|
|
2010-06-02 18:48:27 +02:00
|
|
|
int drive_get_max_bus(BlockInterfaceType type)
|
|
|
|
{
|
|
|
|
int max_bus;
|
|
|
|
DriveInfo *dinfo;
|
|
|
|
|
|
|
|
max_bus = -1;
|
|
|
|
QTAILQ_FOREACH(dinfo, &drives, next) {
|
|
|
|
if(dinfo->type == type &&
|
|
|
|
dinfo->bus > max_bus)
|
|
|
|
max_bus = dinfo->bus;
|
|
|
|
}
|
|
|
|
return max_bus;
|
|
|
|
}
|
|
|
|
|
2011-01-28 11:21:37 +01:00
|
|
|
/* Get a block device. This should only be used for single-drive devices
|
|
|
|
(e.g. SD/Floppy/MTD). Multi-disk devices (scsi/ide) should use the
|
|
|
|
appropriate bus. */
|
|
|
|
DriveInfo *drive_get_next(BlockInterfaceType type)
|
|
|
|
{
|
|
|
|
static int next_block_unit[IF_COUNT];
|
|
|
|
|
|
|
|
return drive_get(type, 0, next_block_unit[type]++);
|
|
|
|
}
|
|
|
|
|
2010-06-24 17:25:32 +02:00
|
|
|
DriveInfo *drive_get_by_blockdev(BlockDriverState *bs)
|
2010-06-02 18:48:27 +02:00
|
|
|
{
|
|
|
|
DriveInfo *dinfo;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(dinfo, &drives, next) {
|
2010-06-24 17:25:32 +02:00
|
|
|
if (dinfo->bdrv == bs) {
|
|
|
|
return dinfo;
|
|
|
|
}
|
2010-06-02 18:48:27 +02:00
|
|
|
}
|
2010-06-24 17:25:32 +02:00
|
|
|
return NULL;
|
2010-06-02 18:48:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void bdrv_format_print(void *opaque, const char *name)
|
|
|
|
{
|
2011-01-17 19:31:27 +01:00
|
|
|
error_printf(" %s", name);
|
2010-06-02 18:48:27 +02:00
|
|
|
}
|
|
|
|
|
2011-01-26 15:12:32 +01:00
|
|
|
static void drive_uninit(DriveInfo *dinfo)
|
2010-06-02 18:48:27 +02:00
|
|
|
{
|
|
|
|
qemu_opts_del(dinfo->opts);
|
|
|
|
bdrv_delete(dinfo->bdrv);
|
2011-08-21 05:09:37 +02:00
|
|
|
g_free(dinfo->id);
|
2010-06-02 18:48:27 +02:00
|
|
|
QTAILQ_REMOVE(&drives, dinfo, next);
|
2011-08-21 05:09:37 +02:00
|
|
|
g_free(dinfo);
|
2010-06-02 18:48:27 +02:00
|
|
|
}
|
|
|
|
|
2011-01-26 15:12:32 +01:00
|
|
|
void drive_put_ref(DriveInfo *dinfo)
|
|
|
|
{
|
|
|
|
assert(dinfo->refcount);
|
|
|
|
if (--dinfo->refcount == 0) {
|
|
|
|
drive_uninit(dinfo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void drive_get_ref(DriveInfo *dinfo)
|
|
|
|
{
|
|
|
|
dinfo->refcount++;
|
|
|
|
}
|
|
|
|
|
2010-06-02 18:48:27 +02:00
|
|
|
static int parse_block_error_action(const char *buf, int is_read)
|
|
|
|
{
|
|
|
|
if (!strcmp(buf, "ignore")) {
|
|
|
|
return BLOCK_ERR_IGNORE;
|
|
|
|
} else if (!is_read && !strcmp(buf, "enospc")) {
|
|
|
|
return BLOCK_ERR_STOP_ENOSPC;
|
|
|
|
} else if (!strcmp(buf, "stop")) {
|
|
|
|
return BLOCK_ERR_STOP_ANY;
|
|
|
|
} else if (!strcmp(buf, "report")) {
|
|
|
|
return BLOCK_ERR_REPORT;
|
|
|
|
} else {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_report("'%s' invalid %s error action",
|
|
|
|
buf, is_read ? "read" : "write");
|
2010-06-02 18:48:27 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-28 11:21:46 +01:00
|
|
|
DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
2010-06-02 18:48:27 +02:00
|
|
|
{
|
|
|
|
const char *buf;
|
|
|
|
const char *file = NULL;
|
|
|
|
char devname[128];
|
|
|
|
const char *serial;
|
|
|
|
const char *mediastr = "";
|
|
|
|
BlockInterfaceType type;
|
|
|
|
enum { MEDIA_DISK, MEDIA_CDROM } media;
|
|
|
|
int bus_id, unit_id;
|
|
|
|
int cyls, heads, secs, translation;
|
|
|
|
BlockDriver *drv = NULL;
|
|
|
|
int max_devs;
|
|
|
|
int index;
|
|
|
|
int ro = 0;
|
|
|
|
int bdrv_flags = 0;
|
|
|
|
int on_read_error, on_write_error;
|
|
|
|
const char *devaddr;
|
|
|
|
DriveInfo *dinfo;
|
|
|
|
int snapshot = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
translation = BIOS_ATA_TRANSLATION_AUTO;
|
|
|
|
media = MEDIA_DISK;
|
|
|
|
|
|
|
|
/* extract parameters */
|
|
|
|
bus_id = qemu_opt_get_number(opts, "bus", 0);
|
|
|
|
unit_id = qemu_opt_get_number(opts, "unit", -1);
|
|
|
|
index = qemu_opt_get_number(opts, "index", -1);
|
|
|
|
|
|
|
|
cyls = qemu_opt_get_number(opts, "cyls", 0);
|
|
|
|
heads = qemu_opt_get_number(opts, "heads", 0);
|
|
|
|
secs = qemu_opt_get_number(opts, "secs", 0);
|
|
|
|
|
|
|
|
snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
|
|
|
|
ro = qemu_opt_get_bool(opts, "readonly", 0);
|
|
|
|
|
|
|
|
file = qemu_opt_get(opts, "file");
|
|
|
|
serial = qemu_opt_get(opts, "serial");
|
|
|
|
|
|
|
|
if ((buf = qemu_opt_get(opts, "if")) != NULL) {
|
|
|
|
pstrcpy(devname, sizeof(devname), buf);
|
2011-01-28 11:21:39 +01:00
|
|
|
for (type = 0; type < IF_COUNT && strcmp(buf, if_name[type]); type++)
|
|
|
|
;
|
|
|
|
if (type == IF_COUNT) {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_report("unsupported bus type '%s'", buf);
|
2010-06-02 18:48:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2011-07-01 15:46:12 +02:00
|
|
|
} else {
|
|
|
|
type = default_to_scsi ? IF_SCSI : IF_IDE;
|
|
|
|
pstrcpy(devname, sizeof(devname), if_name[type]);
|
2010-06-02 18:48:27 +02:00
|
|
|
}
|
2011-07-01 15:46:12 +02:00
|
|
|
|
2011-01-28 11:21:39 +01:00
|
|
|
max_devs = if_max_devs[type];
|
2010-06-02 18:48:27 +02:00
|
|
|
|
|
|
|
if (cyls || heads || secs) {
|
|
|
|
if (cyls < 1 || (type == IF_IDE && cyls > 16383)) {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_report("invalid physical cyls number");
|
2010-06-02 18:48:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (heads < 1 || (type == IF_IDE && heads > 16)) {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_report("invalid physical heads number");
|
2010-06-02 18:48:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (secs < 1 || (type == IF_IDE && secs > 63)) {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_report("invalid physical secs number");
|
2010-06-02 18:48:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((buf = qemu_opt_get(opts, "trans")) != NULL) {
|
|
|
|
if (!cyls) {
|
2011-06-22 14:03:57 +02:00
|
|
|
error_report("'%s' trans must be used with cyls, heads and secs",
|
2011-01-17 19:31:27 +01:00
|
|
|
buf);
|
2010-06-02 18:48:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (!strcmp(buf, "none"))
|
|
|
|
translation = BIOS_ATA_TRANSLATION_NONE;
|
|
|
|
else if (!strcmp(buf, "lba"))
|
|
|
|
translation = BIOS_ATA_TRANSLATION_LBA;
|
|
|
|
else if (!strcmp(buf, "auto"))
|
|
|
|
translation = BIOS_ATA_TRANSLATION_AUTO;
|
|
|
|
else {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_report("'%s' invalid translation type", buf);
|
2010-06-02 18:48:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((buf = qemu_opt_get(opts, "media")) != NULL) {
|
|
|
|
if (!strcmp(buf, "disk")) {
|
|
|
|
media = MEDIA_DISK;
|
|
|
|
} else if (!strcmp(buf, "cdrom")) {
|
|
|
|
if (cyls || secs || heads) {
|
2011-07-01 15:46:13 +02:00
|
|
|
error_report("CHS can't be set with media=%s", buf);
|
2010-06-02 18:48:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
media = MEDIA_CDROM;
|
|
|
|
} else {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_report("'%s' invalid media", buf);
|
2010-06-02 18:48:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((buf = qemu_opt_get(opts, "cache")) != NULL) {
|
|
|
|
if (!strcmp(buf, "off") || !strcmp(buf, "none")) {
|
2011-05-17 18:04:06 +02:00
|
|
|
bdrv_flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB;
|
2010-06-02 18:48:27 +02:00
|
|
|
} else if (!strcmp(buf, "writeback")) {
|
|
|
|
bdrv_flags |= BDRV_O_CACHE_WB;
|
|
|
|
} else if (!strcmp(buf, "unsafe")) {
|
|
|
|
bdrv_flags |= BDRV_O_CACHE_WB;
|
|
|
|
bdrv_flags |= BDRV_O_NO_FLUSH;
|
|
|
|
} else if (!strcmp(buf, "writethrough")) {
|
|
|
|
/* this is the default */
|
|
|
|
} else {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_report("invalid cache option");
|
2010-06-02 18:48:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_LINUX_AIO
|
|
|
|
if ((buf = qemu_opt_get(opts, "aio")) != NULL) {
|
|
|
|
if (!strcmp(buf, "native")) {
|
|
|
|
bdrv_flags |= BDRV_O_NATIVE_AIO;
|
|
|
|
} else if (!strcmp(buf, "threads")) {
|
|
|
|
/* this is the default */
|
|
|
|
} else {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_report("invalid aio option");
|
2010-06-02 18:48:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ((buf = qemu_opt_get(opts, "format")) != NULL) {
|
|
|
|
if (strcmp(buf, "?") == 0) {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_printf("Supported formats:");
|
|
|
|
bdrv_iterate_format(bdrv_format_print, NULL);
|
|
|
|
error_printf("\n");
|
|
|
|
return NULL;
|
2010-06-02 18:48:27 +02:00
|
|
|
}
|
|
|
|
drv = bdrv_find_whitelisted_format(buf);
|
|
|
|
if (!drv) {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_report("'%s' invalid format", buf);
|
2010-06-02 18:48:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
on_write_error = BLOCK_ERR_STOP_ENOSPC;
|
|
|
|
if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
|
|
|
|
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_report("werror is not supported by this bus type");
|
2010-06-02 18:48:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
on_write_error = parse_block_error_action(buf, 0);
|
|
|
|
if (on_write_error < 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
on_read_error = BLOCK_ERR_REPORT;
|
|
|
|
if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {
|
2010-10-25 14:52:21 +02:00
|
|
|
if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_report("rerror is not supported by this bus type");
|
2010-06-02 18:48:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
on_read_error = parse_block_error_action(buf, 1);
|
|
|
|
if (on_read_error < 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((devaddr = qemu_opt_get(opts, "addr")) != NULL) {
|
|
|
|
if (type != IF_VIRTIO) {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_report("addr is not supported by this bus type");
|
2010-06-02 18:48:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* compute bus and unit according index */
|
|
|
|
|
|
|
|
if (index != -1) {
|
|
|
|
if (bus_id != 0 || unit_id != -1) {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_report("index cannot be used with bus and unit");
|
2010-06-02 18:48:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2011-01-28 11:21:43 +01:00
|
|
|
bus_id = drive_index_to_bus_id(type, index);
|
|
|
|
unit_id = drive_index_to_unit_id(type, index);
|
2010-06-02 18:48:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* if user doesn't specify a unit_id,
|
|
|
|
* try to find the first free
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (unit_id == -1) {
|
|
|
|
unit_id = 0;
|
|
|
|
while (drive_get(type, bus_id, unit_id) != NULL) {
|
|
|
|
unit_id++;
|
|
|
|
if (max_devs && unit_id >= max_devs) {
|
|
|
|
unit_id -= max_devs;
|
|
|
|
bus_id++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check unit id */
|
|
|
|
|
|
|
|
if (max_devs && unit_id >= max_devs) {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_report("unit %d too big (max is %d)",
|
|
|
|
unit_id, max_devs - 1);
|
2010-06-02 18:48:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
blockdev: Reject multiple definitions for the same drive
We silently ignore multiple definitions for the same drive:
$ qemu-system-x86_64 -nodefaults -vnc :1 -S -monitor stdio -drive if=ide,index=1,file=tmp.qcow2 -drive if=ide,index=1,file=nonexistant
QEMU 0.13.50 monitor - type 'help' for more information
(qemu) info block
ide0-hd1: type=hd removable=0 file=tmp.qcow2 backing_file=tmp.img ro=0 drv=qcow2 encrypted=0
With if=none, this can become quite confusing:
$ qemu-system-x86_64 -nodefaults -vnc :1 -S -monitor stdio -drive if=none,index=1,file=tmp.qcow2,id=eins -drive if=none,index=1,file=nonexistant,id=zwei -device ide-drive,drive=eins -device ide-drive,drive=zwei
qemu-system-x86_64: -device ide-drive,drive=zwei: Property 'ide-drive.drive' can't find value 'zwei'
The second -device fails, because it refers to drive zwei, which got
silently ignored.
Make multiple drive definitions fail cleanly.
Unfortunately, there's code that relies on multiple drive definitions
being silently ignored: main() merrily adds default drives even when
the user already defined these drives. Fix that up.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2011-01-28 11:21:45 +01:00
|
|
|
* catch multiple definitions
|
2010-06-02 18:48:27 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
if (drive_get(type, bus_id, unit_id) != NULL) {
|
blockdev: Reject multiple definitions for the same drive
We silently ignore multiple definitions for the same drive:
$ qemu-system-x86_64 -nodefaults -vnc :1 -S -monitor stdio -drive if=ide,index=1,file=tmp.qcow2 -drive if=ide,index=1,file=nonexistant
QEMU 0.13.50 monitor - type 'help' for more information
(qemu) info block
ide0-hd1: type=hd removable=0 file=tmp.qcow2 backing_file=tmp.img ro=0 drv=qcow2 encrypted=0
With if=none, this can become quite confusing:
$ qemu-system-x86_64 -nodefaults -vnc :1 -S -monitor stdio -drive if=none,index=1,file=tmp.qcow2,id=eins -drive if=none,index=1,file=nonexistant,id=zwei -device ide-drive,drive=eins -device ide-drive,drive=zwei
qemu-system-x86_64: -device ide-drive,drive=zwei: Property 'ide-drive.drive' can't find value 'zwei'
The second -device fails, because it refers to drive zwei, which got
silently ignored.
Make multiple drive definitions fail cleanly.
Unfortunately, there's code that relies on multiple drive definitions
being silently ignored: main() merrily adds default drives even when
the user already defined these drives. Fix that up.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2011-01-28 11:21:45 +01:00
|
|
|
error_report("drive with bus=%d, unit=%d (index=%d) exists",
|
|
|
|
bus_id, unit_id, index);
|
2010-06-02 18:48:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* init */
|
|
|
|
|
2011-08-21 05:09:37 +02:00
|
|
|
dinfo = g_malloc0(sizeof(*dinfo));
|
2010-06-02 18:48:27 +02:00
|
|
|
if ((buf = qemu_opts_id(opts)) != NULL) {
|
2011-08-21 05:09:37 +02:00
|
|
|
dinfo->id = g_strdup(buf);
|
2010-06-02 18:48:27 +02:00
|
|
|
} else {
|
|
|
|
/* no id supplied -> create one */
|
2011-08-21 05:09:37 +02:00
|
|
|
dinfo->id = g_malloc0(32);
|
2010-06-02 18:48:27 +02:00
|
|
|
if (type == IF_IDE || type == IF_SCSI)
|
|
|
|
mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd";
|
|
|
|
if (max_devs)
|
|
|
|
snprintf(dinfo->id, 32, "%s%i%s%i",
|
|
|
|
devname, bus_id, mediastr, unit_id);
|
|
|
|
else
|
|
|
|
snprintf(dinfo->id, 32, "%s%s%i",
|
|
|
|
devname, mediastr, unit_id);
|
|
|
|
}
|
|
|
|
dinfo->bdrv = bdrv_new(dinfo->id);
|
|
|
|
dinfo->devaddr = devaddr;
|
|
|
|
dinfo->type = type;
|
|
|
|
dinfo->bus = bus_id;
|
|
|
|
dinfo->unit = unit_id;
|
|
|
|
dinfo->opts = opts;
|
2011-01-26 15:12:32 +01:00
|
|
|
dinfo->refcount = 1;
|
2010-06-02 18:48:27 +02:00
|
|
|
if (serial)
|
2010-06-02 22:46:31 +02:00
|
|
|
strncpy(dinfo->serial, serial, sizeof(dinfo->serial) - 1);
|
2010-06-02 18:48:27 +02:00
|
|
|
QTAILQ_INSERT_TAIL(&drives, dinfo, next);
|
|
|
|
|
2010-06-02 18:55:17 +02:00
|
|
|
bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
|
|
|
|
|
2010-06-02 18:48:27 +02:00
|
|
|
switch(type) {
|
|
|
|
case IF_IDE:
|
|
|
|
case IF_SCSI:
|
|
|
|
case IF_XEN:
|
|
|
|
case IF_NONE:
|
|
|
|
switch(media) {
|
|
|
|
case MEDIA_DISK:
|
|
|
|
if (cyls != 0) {
|
|
|
|
bdrv_set_geometry_hint(dinfo->bdrv, cyls, heads, secs);
|
|
|
|
bdrv_set_translation_hint(dinfo->bdrv, translation);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MEDIA_CDROM:
|
2011-05-16 15:04:57 +02:00
|
|
|
bdrv_set_removable(dinfo->bdrv, 1);
|
2011-05-16 15:04:56 +02:00
|
|
|
dinfo->media_cd = 1;
|
2010-06-02 18:48:27 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IF_SD:
|
|
|
|
/* FIXME: This isn't really a floppy, but it's a reasonable
|
|
|
|
approximation. */
|
|
|
|
case IF_FLOPPY:
|
2011-05-16 15:04:57 +02:00
|
|
|
bdrv_set_removable(dinfo->bdrv, 1);
|
2010-06-02 18:48:27 +02:00
|
|
|
break;
|
|
|
|
case IF_PFLASH:
|
|
|
|
case IF_MTD:
|
|
|
|
break;
|
|
|
|
case IF_VIRTIO:
|
|
|
|
/* add virtio block device */
|
2010-08-20 13:52:01 +02:00
|
|
|
opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
|
2011-03-29 15:29:29 +02:00
|
|
|
qemu_opt_set(opts, "driver", "virtio-blk");
|
2010-06-02 18:48:27 +02:00
|
|
|
qemu_opt_set(opts, "drive", dinfo->id);
|
|
|
|
if (devaddr)
|
|
|
|
qemu_opt_set(opts, "addr", devaddr);
|
|
|
|
break;
|
2011-01-28 11:21:41 +01:00
|
|
|
default:
|
2010-06-02 18:48:27 +02:00
|
|
|
abort();
|
|
|
|
}
|
2010-05-11 15:36:46 +02:00
|
|
|
if (!file || !*file) {
|
2011-01-28 11:21:46 +01:00
|
|
|
return dinfo;
|
2010-06-02 18:48:27 +02:00
|
|
|
}
|
|
|
|
if (snapshot) {
|
|
|
|
/* always use cache=unsafe with snapshot */
|
|
|
|
bdrv_flags &= ~BDRV_O_CACHE_MASK;
|
|
|
|
bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (media == MEDIA_CDROM) {
|
|
|
|
/* CDROM is fine for any interface, don't check. */
|
|
|
|
ro = 1;
|
|
|
|
} else if (ro == 1) {
|
|
|
|
if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY && type != IF_NONE) {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_report("readonly not supported by this bus type");
|
2011-02-08 15:12:39 +01:00
|
|
|
goto err;
|
2010-06-02 18:48:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
|
|
|
|
|
|
|
|
ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv);
|
|
|
|
if (ret < 0) {
|
2011-01-17 19:31:27 +01:00
|
|
|
error_report("could not open disk image %s: %s",
|
|
|
|
file, strerror(-ret));
|
2011-02-08 15:12:39 +01:00
|
|
|
goto err;
|
2010-06-02 18:48:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bdrv_key_required(dinfo->bdrv))
|
|
|
|
autostart = 0;
|
|
|
|
return dinfo;
|
2011-02-08 15:12:39 +01:00
|
|
|
|
|
|
|
err:
|
|
|
|
bdrv_delete(dinfo->bdrv);
|
2011-08-21 05:09:37 +02:00
|
|
|
g_free(dinfo->id);
|
2011-02-08 15:12:39 +01:00
|
|
|
QTAILQ_REMOVE(&drives, dinfo, next);
|
2011-08-21 05:09:37 +02:00
|
|
|
g_free(dinfo);
|
2011-02-08 15:12:39 +01:00
|
|
|
return NULL;
|
2010-06-02 18:48:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void do_commit(Monitor *mon, const QDict *qdict)
|
|
|
|
{
|
|
|
|
const char *device = qdict_get_str(qdict, "device");
|
2010-06-02 18:55:18 +02:00
|
|
|
BlockDriverState *bs;
|
2010-06-02 18:48:27 +02:00
|
|
|
|
2010-06-02 18:55:18 +02:00
|
|
|
if (!strcmp(device, "all")) {
|
|
|
|
bdrv_commit_all();
|
|
|
|
} else {
|
|
|
|
bs = bdrv_find(device);
|
2010-06-02 18:55:19 +02:00
|
|
|
if (!bs) {
|
|
|
|
qerror_report(QERR_DEVICE_NOT_FOUND, device);
|
|
|
|
return;
|
2010-06-02 18:55:18 +02:00
|
|
|
}
|
2010-06-02 18:55:19 +02:00
|
|
|
bdrv_commit(bs);
|
2010-06-02 18:48:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-16 13:52:16 +01:00
|
|
|
int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
|
|
|
{
|
|
|
|
const char *device = qdict_get_str(qdict, "device");
|
2011-07-11 20:01:09 +02:00
|
|
|
const char *filename = qdict_get_try_str(qdict, "snapshot-file");
|
2010-12-16 13:52:16 +01:00
|
|
|
const char *format = qdict_get_try_str(qdict, "format");
|
|
|
|
BlockDriverState *bs;
|
2011-03-09 11:20:30 +01:00
|
|
|
BlockDriver *drv, *old_drv, *proto_drv;
|
2010-12-16 13:52:16 +01:00
|
|
|
int ret = 0;
|
|
|
|
int flags;
|
2011-03-09 11:20:30 +01:00
|
|
|
char old_filename[1024];
|
2010-12-16 13:52:16 +01:00
|
|
|
|
2011-01-06 17:02:23 +01:00
|
|
|
if (!filename) {
|
2011-07-11 20:01:09 +02:00
|
|
|
qerror_report(QERR_MISSING_PARAMETER, "snapshot-file");
|
2011-01-06 17:02:23 +01:00
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-12-16 13:52:16 +01:00
|
|
|
bs = bdrv_find(device);
|
|
|
|
if (!bs) {
|
|
|
|
qerror_report(QERR_DEVICE_NOT_FOUND, device);
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2011-03-09 11:20:30 +01:00
|
|
|
pstrcpy(old_filename, sizeof(old_filename), bs->filename);
|
|
|
|
|
|
|
|
old_drv = bs->drv;
|
|
|
|
flags = bs->open_flags;
|
|
|
|
|
2010-12-16 13:52:16 +01:00
|
|
|
if (!format) {
|
|
|
|
format = "qcow2";
|
|
|
|
}
|
|
|
|
|
|
|
|
drv = bdrv_find_format(format);
|
|
|
|
if (!drv) {
|
|
|
|
qerror_report(QERR_INVALID_BLOCK_FORMAT, format);
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
proto_drv = bdrv_find_protocol(filename);
|
|
|
|
if (!proto_drv) {
|
|
|
|
qerror_report(QERR_INVALID_BLOCK_FORMAT, format);
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = bdrv_img_create(filename, format, bs->filename,
|
2011-03-09 11:20:30 +01:00
|
|
|
bs->drv->format_name, NULL, -1, flags);
|
2010-12-16 13:52:16 +01:00
|
|
|
if (ret) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
qemu_aio_flush();
|
|
|
|
bdrv_flush(bs);
|
|
|
|
|
|
|
|
bdrv_close(bs);
|
|
|
|
ret = bdrv_open(bs, filename, flags, drv);
|
|
|
|
/*
|
2011-03-09 11:20:30 +01:00
|
|
|
* If reopening the image file we just created fails, fall back
|
|
|
|
* and try to re-open the original image. If that fails too, we
|
|
|
|
* are in serious trouble.
|
2010-12-16 13:52:16 +01:00
|
|
|
*/
|
|
|
|
if (ret != 0) {
|
2011-03-09 11:20:30 +01:00
|
|
|
ret = bdrv_open(bs, old_filename, flags, old_drv);
|
|
|
|
if (ret != 0) {
|
|
|
|
qerror_report(QERR_OPEN_FILE_FAILED, old_filename);
|
|
|
|
} else {
|
|
|
|
qerror_report(QERR_OPEN_FILE_FAILED, filename);
|
|
|
|
}
|
2010-12-16 13:52:16 +01:00
|
|
|
}
|
|
|
|
out:
|
|
|
|
if (ret) {
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-06-02 18:48:27 +02:00
|
|
|
static int eject_device(Monitor *mon, BlockDriverState *bs, int force)
|
|
|
|
{
|
2011-07-20 18:23:35 +02:00
|
|
|
if (!bdrv_is_removable(bs)) {
|
|
|
|
qerror_report(QERR_DEVICE_NOT_REMOVABLE, bdrv_get_device_name(bs));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!force && bdrv_is_locked(bs)) {
|
|
|
|
qerror_report(QERR_DEVICE_LOCKED, bdrv_get_device_name(bs));
|
|
|
|
return -1;
|
2010-06-02 18:48:27 +02:00
|
|
|
}
|
2010-06-02 00:12:19 +02:00
|
|
|
bdrv_close(bs);
|
2010-06-02 18:48:27 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
Monitor: handle optional '-' arg as a bool
Historically, user monitor arguments beginning with '-' (eg. '-f')
were passed as integers down to handlers.
I've maintained this behavior in the new monitor because we didn't
have a boolean type at the very beginning of QMP. Today we have it
and this behavior is causing trouble to QMP's argument checker.
This commit fixes the problem by doing the following changes:
1. User Monitor
Before: the optional arg was represented as a QInt, we'd pass 1
down to handlers if the user specified the argument or
0 otherwise
This commit: the optional arg is represented as a QBool, we pass
true down to handlers if the user specified the
argument, otherwise _nothing_ is passed
2. QMP
Before: the client was required to pass the arg as QBool, but we'd
convert it to QInt internally. If the argument wasn't passed,
we'd pass 0 down
This commit: still require a QBool, but doesn't do any conversion and
doesn't pass any default value
3. Convert existing handlers (do_eject()/do_migrate()) to the new way
Before: Both handlers would expect a QInt value, either 0 or 1
This commit: Change the handlers to accept a QBool, they handle the
following cases:
A) true is passed: the option is enabled
B) false is passed: the option is disabled
C) nothing is passed: option not specified, use
default behavior
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2010-05-28 20:25:24 +02:00
|
|
|
int force = qdict_get_try_bool(qdict, "force", 0);
|
2010-06-02 18:48:27 +02:00
|
|
|
const char *filename = qdict_get_str(qdict, "device");
|
|
|
|
|
|
|
|
bs = bdrv_find(filename);
|
|
|
|
if (!bs) {
|
|
|
|
qerror_report(QERR_DEVICE_NOT_FOUND, filename);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return eject_device(mon, bs, force);
|
|
|
|
}
|
|
|
|
|
|
|
|
int do_block_set_passwd(Monitor *mon, const QDict *qdict,
|
|
|
|
QObject **ret_data)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
bs = bdrv_find(qdict_get_str(qdict, "device"));
|
|
|
|
if (!bs) {
|
|
|
|
qerror_report(QERR_DEVICE_NOT_FOUND, qdict_get_str(qdict, "device"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = bdrv_set_key(bs, qdict_get_str(qdict, "password"));
|
|
|
|
if (err == -EINVAL) {
|
|
|
|
qerror_report(QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs));
|
|
|
|
return -1;
|
|
|
|
} else if (err < 0) {
|
|
|
|
qerror_report(QERR_INVALID_PASSWORD);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int do_change_block(Monitor *mon, const char *device,
|
|
|
|
const char *filename, const char *fmt)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockDriver *drv = NULL;
|
|
|
|
int bdrv_flags;
|
|
|
|
|
|
|
|
bs = bdrv_find(device);
|
|
|
|
if (!bs) {
|
|
|
|
qerror_report(QERR_DEVICE_NOT_FOUND, device);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (fmt) {
|
|
|
|
drv = bdrv_find_whitelisted_format(fmt);
|
|
|
|
if (!drv) {
|
|
|
|
qerror_report(QERR_INVALID_BLOCK_FORMAT, fmt);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (eject_device(mon, bs, 0) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2010-06-25 15:26:48 +02:00
|
|
|
bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR;
|
2010-07-25 22:49:34 +02:00
|
|
|
bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0;
|
2010-06-02 18:48:27 +02:00
|
|
|
if (bdrv_open(bs, filename, bdrv_flags, drv) < 0) {
|
|
|
|
qerror_report(QERR_OPEN_FILE_FAILED, filename);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return monitor_read_bdrv_key_start(mon, bs, NULL, NULL);
|
|
|
|
}
|
2010-11-12 18:07:13 +01:00
|
|
|
|
|
|
|
int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
|
|
|
{
|
|
|
|
const char *id = qdict_get_str(qdict, "id");
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
|
|
|
bs = bdrv_find(id);
|
|
|
|
if (!bs) {
|
|
|
|
qerror_report(QERR_DEVICE_NOT_FOUND, id);
|
|
|
|
return -1;
|
|
|
|
}
|
2011-01-26 15:12:35 +01:00
|
|
|
if (bdrv_in_use(bs)) {
|
|
|
|
qerror_report(QERR_DEVICE_IN_USE, id);
|
|
|
|
return -1;
|
|
|
|
}
|
2010-11-12 18:07:13 +01:00
|
|
|
|
|
|
|
/* quiesce block driver; prevent further io */
|
|
|
|
qemu_aio_flush();
|
|
|
|
bdrv_flush(bs);
|
|
|
|
bdrv_close(bs);
|
|
|
|
|
Do not delete BlockDriverState when deleting the drive
When removing a drive from the host-side via drive_del we currently have
the following path:
drive_del
qemu_aio_flush()
bdrv_close() // zaps bs->drv, which makes any subsequent I/O get
// dropped. Works as designed
drive_uninit()
bdrv_delete() // frees the bs. Since the device is still connected to
// bs, any subsequent I/O is a use-after-free.
The value of bs->drv becomes unpredictable on free. As long as it
remains null, I/O still gets dropped, however it could become non-null
at any point after the free resulting SEGVs or other QEMU state
corruption.
To resolve this issue as simply as possible, we can chose to not
actually delete the BlockDriverState pointer. Since bdrv_close()
handles setting the drv pointer to NULL, we just need to remove the
BlockDriverState from the QLIST that is used to enumerate the block
devices. This is currently handled within bdrv_delete, so move this
into its own function, bdrv_make_anon().
The result is that we can now invoke drive_del, this closes the file
descriptors and sets BlockDriverState->drv to NULL which prevents futher
IO to the device, and since we do not free BlockDriverState, we don't
have to worry about the copy retained in the block devices.
We also don't attempt to remove the qdev property since we are no longer
deleting the BlockDriverState on drives with associated drives. This
also allows for removing Drives with no devices associated either.
Reported-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Ryan Harper <ryanh@us.ibm.com>
Acked-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2011-03-30 03:51:47 +02:00
|
|
|
/* if we have a device associated with this BlockDriverState (bs->peer)
|
|
|
|
* then we need to make the drive anonymous until the device
|
|
|
|
* can be removed. If this is a drive with no device backing
|
|
|
|
* then we can just get rid of the block driver state right here.
|
|
|
|
*/
|
2011-01-17 19:31:29 +01:00
|
|
|
if (bs->peer) {
|
Do not delete BlockDriverState when deleting the drive
When removing a drive from the host-side via drive_del we currently have
the following path:
drive_del
qemu_aio_flush()
bdrv_close() // zaps bs->drv, which makes any subsequent I/O get
// dropped. Works as designed
drive_uninit()
bdrv_delete() // frees the bs. Since the device is still connected to
// bs, any subsequent I/O is a use-after-free.
The value of bs->drv becomes unpredictable on free. As long as it
remains null, I/O still gets dropped, however it could become non-null
at any point after the free resulting SEGVs or other QEMU state
corruption.
To resolve this issue as simply as possible, we can chose to not
actually delete the BlockDriverState pointer. Since bdrv_close()
handles setting the drv pointer to NULL, we just need to remove the
BlockDriverState from the QLIST that is used to enumerate the block
devices. This is currently handled within bdrv_delete, so move this
into its own function, bdrv_make_anon().
The result is that we can now invoke drive_del, this closes the file
descriptors and sets BlockDriverState->drv to NULL which prevents futher
IO to the device, and since we do not free BlockDriverState, we don't
have to worry about the copy retained in the block devices.
We also don't attempt to remove the qdev property since we are no longer
deleting the BlockDriverState on drives with associated drives. This
also allows for removing Drives with no devices associated either.
Reported-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Ryan Harper <ryanh@us.ibm.com>
Acked-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2011-03-30 03:51:47 +02:00
|
|
|
bdrv_make_anon(bs);
|
|
|
|
} else {
|
|
|
|
drive_uninit(drive_get_by_blockdev(bs));
|
2010-11-12 18:07:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2011-01-24 13:32:33 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX: replace the QERR_UNDEFINED_ERROR errors with real values once the
|
|
|
|
* existing QERR_ macro mess is cleaned up. A good example for better
|
|
|
|
* error reports can be found in the qemu-img resize code.
|
|
|
|
*/
|
|
|
|
int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
|
|
|
{
|
|
|
|
const char *device = qdict_get_str(qdict, "device");
|
|
|
|
int64_t size = qdict_get_int(qdict, "size");
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
|
|
|
bs = bdrv_find(device);
|
|
|
|
if (!bs) {
|
|
|
|
qerror_report(QERR_DEVICE_NOT_FOUND, device);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (size < 0) {
|
|
|
|
qerror_report(QERR_UNDEFINED_ERROR);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bdrv_truncate(bs, size)) {
|
|
|
|
qerror_report(QERR_UNDEFINED_ERROR);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|