#include "qemu-option.h"
#include "qemu-config.h"
#include "sysemu.h"
+#include "hw/qdev.h"
+#include "block_int.h"
static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
+/*
+ * 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);
+
+ if (dinfo) {
+ dinfo->auto_del = 1;
+ }
+}
+
+void blockdev_auto_del(BlockDriverState *bs)
+{
+ DriveInfo *dinfo = drive_get_by_blockdev(bs);
+
+ if (dinfo && dinfo->auto_del) {
+ drive_uninit(dinfo);
+ }
+}
+
QemuOpts *drive_add(const char *file, const char *fmt, ...)
{
va_list ap;
vsnprintf(optstr, sizeof(optstr), fmt, ap);
va_end(ap);
- opts = qemu_opts_parse(&qemu_drive_opts, optstr, 0);
+ opts = qemu_opts_parse(qemu_find_opts("drive"), optstr, 0);
if (!opts) {
return NULL;
}
return NULL;
}
-DriveInfo *drive_get_by_id(const char *id)
-{
- DriveInfo *dinfo;
-
- QTAILQ_FOREACH(dinfo, &drives, next) {
- if (strcmp(id, dinfo->id))
- continue;
- return dinfo;
- }
- return NULL;
-}
-
int drive_get_max_bus(BlockInterfaceType type)
{
int max_bus;
return max_bus;
}
-const char *drive_get_serial(BlockDriverState *bdrv)
+DriveInfo *drive_get_by_blockdev(BlockDriverState *bs)
{
DriveInfo *dinfo;
QTAILQ_FOREACH(dinfo, &drives, next) {
- if (dinfo->bdrv == bdrv)
- return dinfo->serial;
+ if (dinfo->bdrv == bs) {
+ return dinfo;
+ }
}
-
- return "\0";
+ return NULL;
}
static void bdrv_format_print(void *opaque, const char *name)
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) {
- fprintf(stderr, "werror is no supported by this format\n");
+ fprintf(stderr, "werror is not supported by this format\n");
return NULL;
}
on_read_error = BLOCK_ERR_REPORT;
if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {
- if (type != IF_IDE && type != IF_VIRTIO && type != IF_NONE) {
- fprintf(stderr, "rerror is no supported by this format\n");
+ if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) {
+ fprintf(stderr, "rerror is not supported by this format\n");
return NULL;
}
break;
case IF_VIRTIO:
/* add virtio block device */
- opts = qemu_opts_create(&qemu_device_opts, NULL, 0);
+ opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
qemu_opt_set(opts, "driver", "virtio-blk-pci");
qemu_opt_set(opts, "drive", dinfo->id);
if (devaddr)
}
}
+int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ const char *device = qdict_get_str(qdict, "device");
+ const char *filename = qdict_get_try_str(qdict, "snapshot_file");
+ const char *format = qdict_get_try_str(qdict, "format");
+ BlockDriverState *bs;
+ BlockDriver *drv, *proto_drv;
+ int ret = 0;
+ int flags;
+
+ bs = bdrv_find(device);
+ if (!bs) {
+ qerror_report(QERR_DEVICE_NOT_FOUND, device);
+ ret = -1;
+ goto out;
+ }
+
+ 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,
+ bs->drv->format_name, NULL, -1, bs->open_flags);
+ if (ret) {
+ goto out;
+ }
+
+ qemu_aio_flush();
+ bdrv_flush(bs);
+
+ flags = bs->open_flags;
+ bdrv_close(bs);
+ ret = bdrv_open(bs, filename, flags, drv);
+ /*
+ * If reopening the image file we just created fails, we really
+ * are in trouble :(
+ */
+ if (ret != 0) {
+ abort();
+ }
+out:
+ if (ret) {
+ ret = -1;
+ }
+
+ return ret;
+}
+
static int eject_device(Monitor *mon, BlockDriverState *bs, int force)
{
if (!force) {
int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
BlockDriverState *bs;
- int force = qdict_get_int(qdict, "force");
+ int force = qdict_get_try_bool(qdict, "force", 0);
const char *filename = qdict_get_str(qdict, "device");
bs = bdrv_find(filename);
if (eject_device(mon, bs, 0) < 0) {
return -1;
}
- bdrv_flags = bdrv_get_type_hint(bs) == BDRV_TYPE_CDROM ? 0 : BDRV_O_RDWR;
+ bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR;
+ bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0;
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);
}
+
+int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ const char *id = qdict_get_str(qdict, "id");
+ BlockDriverState *bs;
+ BlockDriverState **ptr;
+ Property *prop;
+
+ bs = bdrv_find(id);
+ if (!bs) {
+ qerror_report(QERR_DEVICE_NOT_FOUND, id);
+ return -1;
+ }
+
+ /* quiesce block driver; prevent further io */
+ qemu_aio_flush();
+ bdrv_flush(bs);
+ bdrv_close(bs);
+
+ /* clean up guest state from pointing to host resource by
+ * finding and removing DeviceState "drive" property */
+ for (prop = bs->peer->info->props; prop && prop->name; prop++) {
+ if (prop->info->type == PROP_TYPE_DRIVE) {
+ ptr = qdev_get_prop_ptr(bs->peer, prop);
+ if ((*ptr) == bs) {
+ bdrv_detach(bs, bs->peer);
+ *ptr = NULL;
+ break;
+ }
+ }
+ }
+
+ /* clean up host side */
+ drive_uninit(drive_get_by_blockdev(bs));
+
+ return 0;
+}