2003-06-30 14:03:06 +04:00
|
|
|
/*
|
|
|
|
* QEMU System Emulator block driver
|
2007-09-17 01:08:06 +04:00
|
|
|
*
|
2003-06-30 14:03:06 +04:00
|
|
|
* Copyright (c) 2003 Fabrice Bellard
|
2007-09-17 01:08:06 +04:00
|
|
|
*
|
2003-06-30 14:03:06 +04:00
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*/
|
2008-12-05 20:53:21 +03:00
|
|
|
#include "config-host.h"
|
2007-11-11 05:51:17 +03:00
|
|
|
#include "qemu-common.h"
|
2010-05-22 21:15:08 +04:00
|
|
|
#include "trace.h"
|
2009-03-06 02:01:23 +03:00
|
|
|
#include "monitor.h"
|
2004-08-02 01:59:26 +04:00
|
|
|
#include "block_int.h"
|
2009-05-10 02:03:42 +04:00
|
|
|
#include "module.h"
|
2009-12-10 22:16:06 +03:00
|
|
|
#include "qemu-objects.h"
|
2003-06-30 14:03:06 +04:00
|
|
|
|
2009-07-27 18:12:56 +04:00
|
|
|
#ifdef CONFIG_BSD
|
2005-04-27 01:59:26 +04:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/ioctl.h>
|
2009-09-12 11:36:22 +04:00
|
|
|
#include <sys/queue.h>
|
2009-03-07 23:06:23 +03:00
|
|
|
#ifndef __DragonFly__
|
2005-04-27 01:59:26 +04:00
|
|
|
#include <sys/disk.h>
|
|
|
|
#endif
|
2009-03-07 23:06:23 +03:00
|
|
|
#endif
|
2005-04-27 01:59:26 +04:00
|
|
|
|
2009-03-08 19:26:59 +03:00
|
|
|
#ifdef _WIN32
|
|
|
|
#include <windows.h>
|
|
|
|
#endif
|
|
|
|
|
2009-04-07 22:43:24 +04:00
|
|
|
static BlockDriverAIOCB *bdrv_aio_readv_em(BlockDriverState *bs,
|
|
|
|
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
2009-04-07 22:43:20 +04:00
|
|
|
BlockDriverCompletionFunc *cb, void *opaque);
|
2009-04-07 22:43:24 +04:00
|
|
|
static BlockDriverAIOCB *bdrv_aio_writev_em(BlockDriverState *bs,
|
|
|
|
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
2006-08-07 06:38:06 +04:00
|
|
|
BlockDriverCompletionFunc *cb, void *opaque);
|
2009-09-04 21:01:49 +04:00
|
|
|
static BlockDriverAIOCB *bdrv_aio_flush_em(BlockDriverState *bs,
|
|
|
|
BlockDriverCompletionFunc *cb, void *opaque);
|
2010-05-26 19:51:49 +04:00
|
|
|
static BlockDriverAIOCB *bdrv_aio_noop_em(BlockDriverState *bs,
|
|
|
|
BlockDriverCompletionFunc *cb, void *opaque);
|
2007-09-17 01:08:06 +04:00
|
|
|
static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num,
|
2006-08-01 20:21:11 +04:00
|
|
|
uint8_t *buf, int nb_sectors);
|
|
|
|
static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num,
|
|
|
|
const uint8_t *buf, int nb_sectors);
|
2006-04-26 02:36:06 +04:00
|
|
|
|
2010-04-10 10:02:42 +04:00
|
|
|
static QTAILQ_HEAD(, BlockDriverState) bdrv_states =
|
|
|
|
QTAILQ_HEAD_INITIALIZER(bdrv_states);
|
2008-09-17 23:04:14 +04:00
|
|
|
|
2010-04-13 13:29:33 +04:00
|
|
|
static QLIST_HEAD(, BlockDriver) bdrv_drivers =
|
|
|
|
QLIST_HEAD_INITIALIZER(bdrv_drivers);
|
2004-08-02 01:59:26 +04:00
|
|
|
|
2010-06-25 12:33:39 +04:00
|
|
|
/* The device to use for VM snapshots */
|
|
|
|
static BlockDriverState *bs_snapshots;
|
|
|
|
|
2009-10-27 20:41:44 +03:00
|
|
|
/* If non-zero, use only whitelisted block drivers */
|
|
|
|
static int use_bdrv_whitelist;
|
|
|
|
|
2010-12-09 14:53:00 +03:00
|
|
|
#ifdef _WIN32
|
|
|
|
static int is_windows_drive_prefix(const char *filename)
|
|
|
|
{
|
|
|
|
return (((filename[0] >= 'a' && filename[0] <= 'z') ||
|
|
|
|
(filename[0] >= 'A' && filename[0] <= 'Z')) &&
|
|
|
|
filename[1] == ':');
|
|
|
|
}
|
|
|
|
|
|
|
|
int is_windows_drive(const char *filename)
|
|
|
|
{
|
|
|
|
if (is_windows_drive_prefix(filename) &&
|
|
|
|
filename[2] == '\0')
|
|
|
|
return 1;
|
|
|
|
if (strstart(filename, "\\\\.\\", NULL) ||
|
|
|
|
strstart(filename, "//./", NULL))
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* check if the path starts with "<protocol>:" */
|
|
|
|
static int path_has_protocol(const char *path)
|
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (is_windows_drive(path) ||
|
|
|
|
is_windows_drive_prefix(path)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return strchr(path, ':') != NULL;
|
|
|
|
}
|
|
|
|
|
2006-08-01 20:21:11 +04:00
|
|
|
int path_is_absolute(const char *path)
|
2005-10-30 21:30:10 +03:00
|
|
|
{
|
2006-08-01 20:21:11 +04:00
|
|
|
const char *p;
|
2007-01-07 21:22:37 +03:00
|
|
|
#ifdef _WIN32
|
|
|
|
/* specific case for names like: "\\.\d:" */
|
|
|
|
if (*path == '/' || *path == '\\')
|
|
|
|
return 1;
|
|
|
|
#endif
|
2006-08-01 20:21:11 +04:00
|
|
|
p = strchr(path, ':');
|
|
|
|
if (p)
|
|
|
|
p++;
|
|
|
|
else
|
|
|
|
p = path;
|
2007-01-07 20:27:07 +03:00
|
|
|
#ifdef _WIN32
|
|
|
|
return (*p == '/' || *p == '\\');
|
|
|
|
#else
|
|
|
|
return (*p == '/');
|
|
|
|
#endif
|
2005-10-30 21:30:10 +03:00
|
|
|
}
|
|
|
|
|
2006-08-01 20:21:11 +04:00
|
|
|
/* if filename is absolute, just copy it to dest. Otherwise, build a
|
|
|
|
path to it by considering it is relative to base_path. URL are
|
|
|
|
supported. */
|
|
|
|
void path_combine(char *dest, int dest_size,
|
|
|
|
const char *base_path,
|
|
|
|
const char *filename)
|
2005-10-30 21:30:10 +03:00
|
|
|
{
|
2006-08-01 20:21:11 +04:00
|
|
|
const char *p, *p1;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (dest_size <= 0)
|
|
|
|
return;
|
|
|
|
if (path_is_absolute(filename)) {
|
|
|
|
pstrcpy(dest, dest_size, filename);
|
|
|
|
} else {
|
|
|
|
p = strchr(base_path, ':');
|
|
|
|
if (p)
|
|
|
|
p++;
|
|
|
|
else
|
|
|
|
p = base_path;
|
2007-01-07 20:27:07 +03:00
|
|
|
p1 = strrchr(base_path, '/');
|
|
|
|
#ifdef _WIN32
|
|
|
|
{
|
|
|
|
const char *p2;
|
|
|
|
p2 = strrchr(base_path, '\\');
|
|
|
|
if (!p1 || p2 > p1)
|
|
|
|
p1 = p2;
|
|
|
|
}
|
|
|
|
#endif
|
2006-08-01 20:21:11 +04:00
|
|
|
if (p1)
|
|
|
|
p1++;
|
|
|
|
else
|
|
|
|
p1 = base_path;
|
|
|
|
if (p1 > p)
|
|
|
|
p = p1;
|
|
|
|
len = p - base_path;
|
|
|
|
if (len > dest_size - 1)
|
|
|
|
len = dest_size - 1;
|
|
|
|
memcpy(dest, base_path, len);
|
|
|
|
dest[len] = '\0';
|
|
|
|
pstrcat(dest, dest_size, filename);
|
2005-10-30 21:30:10 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-10 02:03:42 +04:00
|
|
|
void bdrv_register(BlockDriver *bdrv)
|
2004-08-02 01:59:26 +04:00
|
|
|
{
|
2009-04-07 22:43:24 +04:00
|
|
|
if (!bdrv->bdrv_aio_readv) {
|
2006-08-01 20:21:11 +04:00
|
|
|
/* add AIO emulation layer */
|
2009-04-07 22:43:24 +04:00
|
|
|
bdrv->bdrv_aio_readv = bdrv_aio_readv_em;
|
|
|
|
bdrv->bdrv_aio_writev = bdrv_aio_writev_em;
|
2009-03-12 22:57:16 +03:00
|
|
|
} else if (!bdrv->bdrv_read) {
|
2006-08-01 20:21:11 +04:00
|
|
|
/* add synchronous IO emulation layer */
|
|
|
|
bdrv->bdrv_read = bdrv_read_em;
|
|
|
|
bdrv->bdrv_write = bdrv_write_em;
|
|
|
|
}
|
2009-09-04 21:01:49 +04:00
|
|
|
|
|
|
|
if (!bdrv->bdrv_aio_flush)
|
|
|
|
bdrv->bdrv_aio_flush = bdrv_aio_flush_em;
|
|
|
|
|
2010-04-13 13:29:33 +04:00
|
|
|
QLIST_INSERT_HEAD(&bdrv_drivers, bdrv, list);
|
2004-08-02 01:59:26 +04:00
|
|
|
}
|
2004-03-15 00:38:54 +03:00
|
|
|
|
|
|
|
/* create a new block device (by default it is empty) */
|
|
|
|
BlockDriverState *bdrv_new(const char *device_name)
|
|
|
|
{
|
2010-04-10 10:02:42 +04:00
|
|
|
BlockDriverState *bs;
|
2004-03-15 00:38:54 +03:00
|
|
|
|
|
|
|
bs = qemu_mallocz(sizeof(BlockDriverState));
|
|
|
|
pstrcpy(bs->device_name, sizeof(bs->device_name), device_name);
|
2004-08-02 01:59:26 +04:00
|
|
|
if (device_name[0] != '\0') {
|
2010-04-10 10:02:42 +04:00
|
|
|
QTAILQ_INSERT_TAIL(&bdrv_states, bs, list);
|
2004-08-02 01:59:26 +04:00
|
|
|
}
|
2004-03-15 00:38:54 +03:00
|
|
|
return bs;
|
|
|
|
}
|
|
|
|
|
2004-08-02 01:59:26 +04:00
|
|
|
BlockDriver *bdrv_find_format(const char *format_name)
|
|
|
|
{
|
|
|
|
BlockDriver *drv1;
|
2010-04-13 13:29:33 +04:00
|
|
|
QLIST_FOREACH(drv1, &bdrv_drivers, list) {
|
|
|
|
if (!strcmp(drv1->format_name, format_name)) {
|
2004-08-02 01:59:26 +04:00
|
|
|
return drv1;
|
2010-04-13 13:29:33 +04:00
|
|
|
}
|
2004-08-02 01:59:26 +04:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-10-27 20:41:44 +03:00
|
|
|
static int bdrv_is_whitelisted(BlockDriver *drv)
|
|
|
|
{
|
|
|
|
static const char *whitelist[] = {
|
|
|
|
CONFIG_BDRV_WHITELIST
|
|
|
|
};
|
|
|
|
const char **p;
|
|
|
|
|
|
|
|
if (!whitelist[0])
|
|
|
|
return 1; /* no whitelist, anything goes */
|
|
|
|
|
|
|
|
for (p = whitelist; *p; p++) {
|
|
|
|
if (!strcmp(drv->format_name, *p)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
BlockDriver *bdrv_find_whitelisted_format(const char *format_name)
|
|
|
|
{
|
|
|
|
BlockDriver *drv = bdrv_find_format(format_name);
|
|
|
|
return drv && bdrv_is_whitelisted(drv) ? drv : NULL;
|
|
|
|
}
|
|
|
|
|
2009-05-18 18:42:10 +04:00
|
|
|
int bdrv_create(BlockDriver *drv, const char* filename,
|
|
|
|
QEMUOptionParameter *options)
|
2004-08-02 01:59:26 +04:00
|
|
|
{
|
|
|
|
if (!drv->bdrv_create)
|
|
|
|
return -ENOTSUP;
|
2009-05-18 18:42:10 +04:00
|
|
|
|
|
|
|
return drv->bdrv_create(filename, options);
|
2004-08-02 01:59:26 +04:00
|
|
|
}
|
|
|
|
|
2010-04-08 00:30:24 +04:00
|
|
|
int bdrv_create_file(const char* filename, QEMUOptionParameter *options)
|
|
|
|
{
|
|
|
|
BlockDriver *drv;
|
|
|
|
|
2010-05-26 06:35:36 +04:00
|
|
|
drv = bdrv_find_protocol(filename);
|
2010-04-08 00:30:24 +04:00
|
|
|
if (drv == NULL) {
|
2010-11-30 18:14:14 +03:00
|
|
|
return -ENOENT;
|
2010-04-08 00:30:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return bdrv_create(drv, filename, options);
|
|
|
|
}
|
|
|
|
|
2004-08-04 01:14:23 +04:00
|
|
|
#ifdef _WIN32
|
2005-12-18 21:28:15 +03:00
|
|
|
void get_tmp_filename(char *filename, int size)
|
2004-08-04 01:14:23 +04:00
|
|
|
{
|
2007-01-07 20:27:07 +03:00
|
|
|
char temp_dir[MAX_PATH];
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2007-01-07 20:27:07 +03:00
|
|
|
GetTempPath(MAX_PATH, temp_dir);
|
|
|
|
GetTempFileName(temp_dir, "qem", 0, filename);
|
2004-08-04 01:14:23 +04:00
|
|
|
}
|
|
|
|
#else
|
2005-12-18 21:28:15 +03:00
|
|
|
void get_tmp_filename(char *filename, int size)
|
2003-06-30 14:03:06 +04:00
|
|
|
{
|
2004-04-01 03:37:16 +04:00
|
|
|
int fd;
|
2008-09-14 10:45:34 +04:00
|
|
|
const char *tmpdir;
|
2004-08-04 01:14:23 +04:00
|
|
|
/* XXX: race condition possible */
|
2008-03-10 03:05:34 +03:00
|
|
|
tmpdir = getenv("TMPDIR");
|
|
|
|
if (!tmpdir)
|
|
|
|
tmpdir = "/tmp";
|
|
|
|
snprintf(filename, size, "%s/vl.XXXXXX", tmpdir);
|
2004-08-02 01:59:26 +04:00
|
|
|
fd = mkstemp(filename);
|
|
|
|
close(fd);
|
|
|
|
}
|
2004-08-04 01:14:23 +04:00
|
|
|
#endif
|
2003-06-30 14:03:06 +04:00
|
|
|
|
2010-04-08 00:30:24 +04:00
|
|
|
/*
|
|
|
|
* Detect host devices. By convention, /dev/cdrom[N] is always
|
|
|
|
* recognized as a host CDROM.
|
|
|
|
*/
|
|
|
|
static BlockDriver *find_hdev_driver(const char *filename)
|
|
|
|
{
|
|
|
|
int score_max = 0, score;
|
|
|
|
BlockDriver *drv = NULL, *d;
|
|
|
|
|
|
|
|
QLIST_FOREACH(d, &bdrv_drivers, list) {
|
|
|
|
if (d->bdrv_probe_device) {
|
|
|
|
score = d->bdrv_probe_device(filename);
|
|
|
|
if (score > score_max) {
|
|
|
|
score_max = score;
|
|
|
|
drv = d;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return drv;
|
|
|
|
}
|
|
|
|
|
2010-05-26 06:35:36 +04:00
|
|
|
BlockDriver *bdrv_find_protocol(const char *filename)
|
2006-08-01 20:21:11 +04:00
|
|
|
{
|
|
|
|
BlockDriver *drv1;
|
|
|
|
char protocol[128];
|
2009-07-02 17:12:26 +04:00
|
|
|
int len;
|
2006-08-01 20:21:11 +04:00
|
|
|
const char *p;
|
2006-08-19 15:45:59 +04:00
|
|
|
|
2010-04-14 16:17:38 +04:00
|
|
|
/* TODO Drivers without bdrv_file_open must be specified explicitly */
|
|
|
|
|
2010-06-23 14:25:17 +04:00
|
|
|
/*
|
|
|
|
* XXX(hch): we really should not let host device detection
|
|
|
|
* override an explicit protocol specification, but moving this
|
|
|
|
* later breaks access to device names with colons in them.
|
|
|
|
* Thanks to the brain-dead persistent naming schemes on udev-
|
|
|
|
* based Linux systems those actually are quite common.
|
|
|
|
*/
|
|
|
|
drv1 = find_hdev_driver(filename);
|
|
|
|
if (drv1) {
|
|
|
|
return drv1;
|
|
|
|
}
|
|
|
|
|
2010-12-09 14:53:00 +03:00
|
|
|
if (!path_has_protocol(filename)) {
|
2010-06-23 14:25:17 +04:00
|
|
|
return bdrv_find_format("file");
|
2010-04-08 00:30:24 +04:00
|
|
|
}
|
2010-12-09 14:53:00 +03:00
|
|
|
p = strchr(filename, ':');
|
|
|
|
assert(p != NULL);
|
2009-07-02 17:12:26 +04:00
|
|
|
len = p - filename;
|
|
|
|
if (len > sizeof(protocol) - 1)
|
|
|
|
len = sizeof(protocol) - 1;
|
|
|
|
memcpy(protocol, filename, len);
|
|
|
|
protocol[len] = '\0';
|
2010-04-13 13:29:33 +04:00
|
|
|
QLIST_FOREACH(drv1, &bdrv_drivers, list) {
|
2007-09-17 01:08:06 +04:00
|
|
|
if (drv1->protocol_name &&
|
2010-04-13 13:29:33 +04:00
|
|
|
!strcmp(drv1->protocol_name, protocol)) {
|
2006-08-01 20:21:11 +04:00
|
|
|
return drv1;
|
2010-04-13 13:29:33 +04:00
|
|
|
}
|
2006-08-01 20:21:11 +04:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-07-21 23:51:51 +04:00
|
|
|
static int find_image_format(const char *filename, BlockDriver **pdrv)
|
2009-06-15 15:55:19 +04:00
|
|
|
{
|
|
|
|
int ret, score, score_max;
|
|
|
|
BlockDriver *drv1, *drv;
|
|
|
|
uint8_t buf[2048];
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
2010-01-17 17:48:13 +03:00
|
|
|
ret = bdrv_file_open(&bs, filename, 0);
|
2010-07-21 23:51:51 +04:00
|
|
|
if (ret < 0) {
|
|
|
|
*pdrv = NULL;
|
|
|
|
return ret;
|
|
|
|
}
|
2010-05-17 20:45:57 +04:00
|
|
|
|
2010-06-01 20:37:31 +04:00
|
|
|
/* Return the raw BlockDriver * to scsi-generic devices or empty drives */
|
|
|
|
if (bs->sg || !bdrv_is_inserted(bs)) {
|
2010-05-27 19:56:28 +04:00
|
|
|
bdrv_delete(bs);
|
2010-07-21 23:51:51 +04:00
|
|
|
drv = bdrv_find_format("raw");
|
|
|
|
if (!drv) {
|
|
|
|
ret = -ENOENT;
|
|
|
|
}
|
|
|
|
*pdrv = drv;
|
|
|
|
return ret;
|
2010-05-27 19:56:28 +04:00
|
|
|
}
|
2010-05-17 20:45:57 +04:00
|
|
|
|
2006-08-01 20:21:11 +04:00
|
|
|
ret = bdrv_pread(bs, 0, buf, sizeof(buf));
|
|
|
|
bdrv_delete(bs);
|
|
|
|
if (ret < 0) {
|
2010-07-21 23:51:51 +04:00
|
|
|
*pdrv = NULL;
|
|
|
|
return ret;
|
2006-08-01 20:21:11 +04:00
|
|
|
}
|
|
|
|
|
2004-08-02 01:59:26 +04:00
|
|
|
score_max = 0;
|
2010-04-08 00:30:24 +04:00
|
|
|
drv = NULL;
|
2010-04-13 13:29:33 +04:00
|
|
|
QLIST_FOREACH(drv1, &bdrv_drivers, list) {
|
2006-08-01 20:21:11 +04:00
|
|
|
if (drv1->bdrv_probe) {
|
|
|
|
score = drv1->bdrv_probe(buf, ret, filename);
|
|
|
|
if (score > score_max) {
|
|
|
|
score_max = score;
|
|
|
|
drv = drv1;
|
|
|
|
}
|
2003-07-01 03:17:31 +04:00
|
|
|
}
|
2003-06-30 14:03:06 +04:00
|
|
|
}
|
2010-07-21 23:51:51 +04:00
|
|
|
if (!drv) {
|
|
|
|
ret = -ENOENT;
|
|
|
|
}
|
|
|
|
*pdrv = drv;
|
|
|
|
return ret;
|
2004-08-02 01:59:26 +04:00
|
|
|
}
|
|
|
|
|
2010-04-19 19:56:41 +04:00
|
|
|
/**
|
|
|
|
* Set the current 'total_sectors' value
|
|
|
|
*/
|
|
|
|
static int refresh_total_sectors(BlockDriverState *bs, int64_t hint)
|
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
|
|
2010-05-17 20:46:04 +04:00
|
|
|
/* Do not attempt drv->bdrv_getlength() on scsi-generic devices */
|
|
|
|
if (bs->sg)
|
|
|
|
return 0;
|
|
|
|
|
2010-04-19 19:56:41 +04:00
|
|
|
/* query actual device if possible, otherwise just trust the hint */
|
|
|
|
if (drv->bdrv_getlength) {
|
|
|
|
int64_t length = drv->bdrv_getlength(bs);
|
|
|
|
if (length < 0) {
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
hint = length >> BDRV_SECTOR_BITS;
|
|
|
|
}
|
|
|
|
|
|
|
|
bs->total_sectors = hint;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-04-14 17:24:50 +04:00
|
|
|
/*
|
|
|
|
* Common part for opening disk images and files
|
|
|
|
*/
|
|
|
|
static int bdrv_open_common(BlockDriverState *bs, const char *filename,
|
|
|
|
int flags, BlockDriver *drv)
|
|
|
|
{
|
|
|
|
int ret, open_flags;
|
|
|
|
|
|
|
|
assert(drv != NULL);
|
|
|
|
|
2010-04-14 16:17:38 +04:00
|
|
|
bs->file = NULL;
|
2010-04-19 19:56:41 +04:00
|
|
|
bs->total_sectors = 0;
|
2010-04-14 17:24:50 +04:00
|
|
|
bs->encrypted = 0;
|
|
|
|
bs->valid_key = 0;
|
|
|
|
bs->open_flags = flags;
|
|
|
|
/* buffer_alignment defaulted to 512, drivers can change this value */
|
|
|
|
bs->buffer_alignment = 512;
|
|
|
|
|
|
|
|
pstrcpy(bs->filename, sizeof(bs->filename), filename);
|
|
|
|
|
|
|
|
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv)) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
bs->drv = drv;
|
|
|
|
bs->opaque = qemu_mallocz(drv->instance_size);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Yes, BDRV_O_NOCACHE aka O_DIRECT means we have to present a
|
|
|
|
* write cache to the guest. We do need the fdatasync to flush
|
|
|
|
* out transactions for block allocations, and we maybe have a
|
|
|
|
* volatile write cache in our backing device to deal with.
|
|
|
|
*/
|
|
|
|
if (flags & (BDRV_O_CACHE_WB|BDRV_O_NOCACHE))
|
|
|
|
bs->enable_write_cache = 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear flags that are internal to the block layer before opening the
|
|
|
|
* image.
|
|
|
|
*/
|
|
|
|
open_flags = flags & ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
|
|
|
|
|
|
|
|
/*
|
2011-04-26 12:29:36 +04:00
|
|
|
* Snapshots should be writable.
|
2010-04-14 17:24:50 +04:00
|
|
|
*/
|
|
|
|
if (bs->is_temporary) {
|
|
|
|
open_flags |= BDRV_O_RDWR;
|
|
|
|
}
|
|
|
|
|
2010-04-14 16:17:38 +04:00
|
|
|
/* Open the image, either directly or using a protocol */
|
|
|
|
if (drv->bdrv_file_open) {
|
|
|
|
ret = drv->bdrv_file_open(bs, filename, open_flags);
|
|
|
|
} else {
|
|
|
|
ret = bdrv_file_open(&bs->file, filename, open_flags);
|
|
|
|
if (ret >= 0) {
|
|
|
|
ret = drv->bdrv_open(bs, open_flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-14 17:24:50 +04:00
|
|
|
if (ret < 0) {
|
|
|
|
goto free_and_fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
bs->keep_read_only = bs->read_only = !(open_flags & BDRV_O_RDWR);
|
2010-04-19 19:56:41 +04:00
|
|
|
|
|
|
|
ret = refresh_total_sectors(bs, bs->total_sectors);
|
|
|
|
if (ret < 0) {
|
|
|
|
goto free_and_fail;
|
2010-04-14 17:24:50 +04:00
|
|
|
}
|
2010-04-19 19:56:41 +04:00
|
|
|
|
2010-04-14 17:24:50 +04:00
|
|
|
#ifndef _WIN32
|
|
|
|
if (bs->is_temporary) {
|
|
|
|
unlink(filename);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
free_and_fail:
|
2010-04-14 16:17:38 +04:00
|
|
|
if (bs->file) {
|
|
|
|
bdrv_delete(bs->file);
|
|
|
|
bs->file = NULL;
|
|
|
|
}
|
2010-04-14 17:24:50 +04:00
|
|
|
qemu_free(bs->opaque);
|
|
|
|
bs->opaque = NULL;
|
|
|
|
bs->drv = NULL;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-04-12 18:37:13 +04:00
|
|
|
/*
|
|
|
|
* Opens a file using a protocol (file, host_device, nbd, ...)
|
|
|
|
*/
|
2006-08-01 20:21:11 +04:00
|
|
|
int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags)
|
2004-08-02 01:59:26 +04:00
|
|
|
{
|
2006-08-01 20:21:11 +04:00
|
|
|
BlockDriverState *bs;
|
2010-04-05 18:53:57 +04:00
|
|
|
BlockDriver *drv;
|
2006-08-01 20:21:11 +04:00
|
|
|
int ret;
|
|
|
|
|
2010-05-26 06:35:36 +04:00
|
|
|
drv = bdrv_find_protocol(filename);
|
2010-04-05 18:53:57 +04:00
|
|
|
if (!drv) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2006-08-01 20:21:11 +04:00
|
|
|
bs = bdrv_new("");
|
2010-04-12 18:37:13 +04:00
|
|
|
ret = bdrv_open_common(bs, filename, flags, drv);
|
2006-08-01 20:21:11 +04:00
|
|
|
if (ret < 0) {
|
|
|
|
bdrv_delete(bs);
|
|
|
|
return ret;
|
2005-10-30 21:30:10 +03:00
|
|
|
}
|
2009-03-03 20:37:16 +03:00
|
|
|
bs->growable = 1;
|
2006-08-01 20:21:11 +04:00
|
|
|
*pbs = bs;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-04-12 18:37:13 +04:00
|
|
|
/*
|
|
|
|
* Opens a disk image (raw, qcow2, vmdk, ...)
|
|
|
|
*/
|
2010-03-31 16:40:27 +04:00
|
|
|
int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
|
|
|
BlockDriver *drv)
|
2004-08-02 01:59:26 +04:00
|
|
|
{
|
2010-04-12 18:37:13 +04:00
|
|
|
int ret;
|
2005-04-29 01:09:32 +04:00
|
|
|
|
2006-08-01 20:21:11 +04:00
|
|
|
if (flags & BDRV_O_SNAPSHOT) {
|
2004-08-02 01:59:26 +04:00
|
|
|
BlockDriverState *bs1;
|
|
|
|
int64_t total_size;
|
2008-09-12 21:54:13 +04:00
|
|
|
int is_protocol = 0;
|
2009-05-27 16:48:06 +04:00
|
|
|
BlockDriver *bdrv_qcow2;
|
|
|
|
QEMUOptionParameter *options;
|
2010-04-12 18:37:13 +04:00
|
|
|
char tmp_filename[PATH_MAX];
|
|
|
|
char backing_filename[PATH_MAX];
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2004-08-02 01:59:26 +04:00
|
|
|
/* if snapshot, we create a temporary backing file and open it
|
|
|
|
instead of opening 'filename' directly */
|
2003-07-06 21:15:21 +04:00
|
|
|
|
2004-08-02 01:59:26 +04:00
|
|
|
/* if there is a backing file, use it */
|
|
|
|
bs1 = bdrv_new("");
|
2010-03-31 16:40:27 +04:00
|
|
|
ret = bdrv_open(bs1, filename, 0, drv);
|
2009-03-06 02:00:29 +03:00
|
|
|
if (ret < 0) {
|
2004-08-02 01:59:26 +04:00
|
|
|
bdrv_delete(bs1);
|
2009-03-06 02:00:29 +03:00
|
|
|
return ret;
|
2004-08-02 01:59:26 +04:00
|
|
|
}
|
2010-05-27 18:20:30 +04:00
|
|
|
total_size = bdrv_getlength(bs1) & BDRV_SECTOR_MASK;
|
2008-09-12 21:54:13 +04:00
|
|
|
|
|
|
|
if (bs1->drv && bs1->drv->protocol_name)
|
|
|
|
is_protocol = 1;
|
|
|
|
|
2004-08-02 01:59:26 +04:00
|
|
|
bdrv_delete(bs1);
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2004-08-02 01:59:26 +04:00
|
|
|
get_tmp_filename(tmp_filename, sizeof(tmp_filename));
|
2008-09-12 21:54:13 +04:00
|
|
|
|
|
|
|
/* Real path is meaningless for protocols */
|
|
|
|
if (is_protocol)
|
|
|
|
snprintf(backing_filename, sizeof(backing_filename),
|
|
|
|
"%s", filename);
|
2009-12-25 21:19:22 +03:00
|
|
|
else if (!realpath(filename, backing_filename))
|
|
|
|
return -errno;
|
2008-09-12 21:54:13 +04:00
|
|
|
|
2009-05-27 16:48:06 +04:00
|
|
|
bdrv_qcow2 = bdrv_find_format("qcow2");
|
|
|
|
options = parse_option_parameters("", bdrv_qcow2->create_options, NULL);
|
|
|
|
|
2010-05-27 18:20:30 +04:00
|
|
|
set_option_parameter_int(options, BLOCK_OPT_SIZE, total_size);
|
2009-05-27 16:48:06 +04:00
|
|
|
set_option_parameter(options, BLOCK_OPT_BACKING_FILE, backing_filename);
|
|
|
|
if (drv) {
|
|
|
|
set_option_parameter(options, BLOCK_OPT_BACKING_FMT,
|
|
|
|
drv->format_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = bdrv_create(bdrv_qcow2, tmp_filename, options);
|
2010-04-29 20:24:50 +04:00
|
|
|
free_option_parameters(options);
|
2009-03-06 02:00:29 +03:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
2004-08-02 01:59:26 +04:00
|
|
|
}
|
2009-05-27 16:48:06 +04:00
|
|
|
|
2004-08-02 01:59:26 +04:00
|
|
|
filename = tmp_filename;
|
2009-05-27 16:48:06 +04:00
|
|
|
drv = bdrv_qcow2;
|
2004-08-02 01:59:26 +04:00
|
|
|
bs->is_temporary = 1;
|
|
|
|
}
|
2005-04-29 01:09:32 +04:00
|
|
|
|
2010-04-12 18:37:13 +04:00
|
|
|
/* Find the right image format driver */
|
2010-04-05 18:53:57 +04:00
|
|
|
if (!drv) {
|
2010-07-21 23:51:51 +04:00
|
|
|
ret = find_image_format(filename, &drv);
|
2009-03-06 02:00:29 +03:00
|
|
|
}
|
2010-01-20 20:13:25 +03:00
|
|
|
|
2009-03-06 02:00:29 +03:00
|
|
|
if (!drv) {
|
|
|
|
goto unlink_and_fail;
|
2004-08-02 01:59:26 +04:00
|
|
|
}
|
2010-04-12 18:37:13 +04:00
|
|
|
|
|
|
|
/* Open the image */
|
|
|
|
ret = bdrv_open_common(bs, filename, flags, drv);
|
|
|
|
if (ret < 0) {
|
2010-01-20 20:13:25 +03:00
|
|
|
goto unlink_and_fail;
|
|
|
|
}
|
|
|
|
|
2010-04-12 18:37:13 +04:00
|
|
|
/* If there is a backing file, use it */
|
|
|
|
if ((flags & BDRV_O_NO_BACKING) == 0 && bs->backing_file[0] != '\0') {
|
|
|
|
char backing_filename[PATH_MAX];
|
|
|
|
int back_flags;
|
|
|
|
BlockDriver *back_drv = NULL;
|
|
|
|
|
|
|
|
bs->backing_hd = bdrv_new("");
|
2010-12-02 19:54:13 +03:00
|
|
|
|
|
|
|
if (path_has_protocol(bs->backing_file)) {
|
|
|
|
pstrcpy(backing_filename, sizeof(backing_filename),
|
|
|
|
bs->backing_file);
|
|
|
|
} else {
|
|
|
|
path_combine(backing_filename, sizeof(backing_filename),
|
|
|
|
filename, bs->backing_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bs->backing_format[0] != '\0') {
|
2010-04-12 18:37:13 +04:00
|
|
|
back_drv = bdrv_find_format(bs->backing_format);
|
2010-12-02 19:54:13 +03:00
|
|
|
}
|
2010-04-12 18:37:13 +04:00
|
|
|
|
|
|
|
/* backing files always opened read-only */
|
|
|
|
back_flags =
|
|
|
|
flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
|
|
|
|
|
|
|
|
ret = bdrv_open(bs->backing_hd, backing_filename, back_flags, back_drv);
|
|
|
|
if (ret < 0) {
|
|
|
|
bdrv_close(bs);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
if (bs->is_temporary) {
|
|
|
|
bs->backing_hd->keep_read_only = !(flags & BDRV_O_RDWR);
|
|
|
|
} else {
|
|
|
|
/* base image inherits from "parent" */
|
|
|
|
bs->backing_hd->keep_read_only = bs->keep_read_only;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bdrv_key_required(bs)) {
|
|
|
|
/* call the change callback */
|
|
|
|
bs->media_changed = 1;
|
|
|
|
if (bs->change_cb)
|
2011-01-24 15:32:41 +03:00
|
|
|
bs->change_cb(bs->change_opaque, CHANGE_MEDIA);
|
2010-04-12 18:37:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
unlink_and_fail:
|
|
|
|
if (bs->is_temporary) {
|
|
|
|
unlink(filename);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2003-06-30 14:03:06 +04:00
|
|
|
void bdrv_close(BlockDriverState *bs)
|
|
|
|
{
|
2006-08-19 15:45:59 +04:00
|
|
|
if (bs->drv) {
|
2010-06-25 12:33:39 +04:00
|
|
|
if (bs == bs_snapshots) {
|
|
|
|
bs_snapshots = NULL;
|
|
|
|
}
|
2010-04-17 13:49:06 +04:00
|
|
|
if (bs->backing_hd) {
|
2004-08-02 01:59:26 +04:00
|
|
|
bdrv_delete(bs->backing_hd);
|
2010-04-17 13:49:06 +04:00
|
|
|
bs->backing_hd = NULL;
|
|
|
|
}
|
2004-08-02 01:59:26 +04:00
|
|
|
bs->drv->bdrv_close(bs);
|
|
|
|
qemu_free(bs->opaque);
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (bs->is_temporary) {
|
|
|
|
unlink(bs->filename);
|
|
|
|
}
|
2004-04-01 03:37:16 +04:00
|
|
|
#endif
|
2004-08-02 01:59:26 +04:00
|
|
|
bs->opaque = NULL;
|
|
|
|
bs->drv = NULL;
|
2004-03-15 00:38:54 +03:00
|
|
|
|
2010-04-14 16:17:38 +04:00
|
|
|
if (bs->file != NULL) {
|
|
|
|
bdrv_close(bs->file);
|
|
|
|
}
|
|
|
|
|
2004-03-15 00:38:54 +03:00
|
|
|
/* call the change callback */
|
2006-08-19 15:45:59 +04:00
|
|
|
bs->media_changed = 1;
|
2004-03-15 00:38:54 +03:00
|
|
|
if (bs->change_cb)
|
2011-01-24 15:32:41 +03:00
|
|
|
bs->change_cb(bs->change_opaque, CHANGE_MEDIA);
|
2004-03-15 00:38:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-28 06:44:57 +04:00
|
|
|
void bdrv_close_all(void)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(bs, &bdrv_states, list) {
|
|
|
|
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 05:51:47 +04:00
|
|
|
/* make a BlockDriverState anonymous by removing from bdrv_state list.
|
|
|
|
Also, NULL terminate the device_name to prevent double remove */
|
|
|
|
void bdrv_make_anon(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
if (bs->device_name[0] != '\0') {
|
|
|
|
QTAILQ_REMOVE(&bdrv_states, bs, list);
|
|
|
|
}
|
|
|
|
bs->device_name[0] = '\0';
|
|
|
|
}
|
|
|
|
|
2004-03-15 00:38:54 +03:00
|
|
|
void bdrv_delete(BlockDriverState *bs)
|
|
|
|
{
|
2010-06-29 18:58:30 +04:00
|
|
|
assert(!bs->peer);
|
|
|
|
|
2010-04-10 10:02:42 +04:00
|
|
|
/* remove from list, if necessary */
|
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 05:51:47 +04:00
|
|
|
bdrv_make_anon(bs);
|
2008-04-08 23:51:21 +04:00
|
|
|
|
2004-03-15 00:38:54 +03:00
|
|
|
bdrv_close(bs);
|
2010-04-14 16:17:38 +04:00
|
|
|
if (bs->file != NULL) {
|
|
|
|
bdrv_delete(bs->file);
|
|
|
|
}
|
|
|
|
|
2010-06-25 12:33:39 +04:00
|
|
|
assert(bs != bs_snapshots);
|
2004-03-15 00:38:54 +03:00
|
|
|
qemu_free(bs);
|
2003-06-30 14:03:06 +04:00
|
|
|
}
|
|
|
|
|
2010-06-29 18:58:30 +04:00
|
|
|
int bdrv_attach(BlockDriverState *bs, DeviceState *qdev)
|
|
|
|
{
|
|
|
|
if (bs->peer) {
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
bs->peer = qdev;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bdrv_detach(BlockDriverState *bs, DeviceState *qdev)
|
|
|
|
{
|
|
|
|
assert(bs->peer == qdev);
|
|
|
|
bs->peer = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
DeviceState *bdrv_get_attached(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
return bs->peer;
|
|
|
|
}
|
|
|
|
|
2009-04-22 03:11:50 +04:00
|
|
|
/*
|
|
|
|
* Run consistency checks on an image
|
|
|
|
*
|
2010-06-29 13:43:13 +04:00
|
|
|
* Returns 0 if the check could be completed (it doesn't mean that the image is
|
2011-04-28 19:20:38 +04:00
|
|
|
* free of errors) or -errno when an internal error occurred. The results of the
|
2010-06-29 13:43:13 +04:00
|
|
|
* check are stored in res.
|
2009-04-22 03:11:50 +04:00
|
|
|
*/
|
2010-06-29 13:43:13 +04:00
|
|
|
int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res)
|
2009-04-22 03:11:50 +04:00
|
|
|
{
|
|
|
|
if (bs->drv->bdrv_check == NULL) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2010-06-29 13:43:13 +04:00
|
|
|
memset(res, 0, sizeof(*res));
|
2010-06-29 14:37:54 +04:00
|
|
|
return bs->drv->bdrv_check(bs, res);
|
2009-04-22 03:11:50 +04:00
|
|
|
}
|
|
|
|
|
2010-07-16 19:17:01 +04:00
|
|
|
#define COMMIT_BUF_SECTORS 2048
|
|
|
|
|
2003-07-06 21:15:21 +04:00
|
|
|
/* commit COW file into the raw image */
|
|
|
|
int bdrv_commit(BlockDriverState *bs)
|
|
|
|
{
|
2006-08-19 15:45:59 +04:00
|
|
|
BlockDriver *drv = bs->drv;
|
2010-08-05 15:05:22 +04:00
|
|
|
BlockDriver *backing_drv;
|
2010-07-16 19:17:01 +04:00
|
|
|
int64_t sector, total_sectors;
|
|
|
|
int n, ro, open_flags;
|
2010-02-14 14:39:18 +03:00
|
|
|
int ret = 0, rw_ret = 0;
|
2010-07-16 19:17:01 +04:00
|
|
|
uint8_t *buf;
|
2010-02-14 14:39:18 +03:00
|
|
|
char filename[1024];
|
|
|
|
BlockDriverState *bs_rw, *bs_ro;
|
2003-07-06 21:15:21 +04:00
|
|
|
|
2006-08-19 15:45:59 +04:00
|
|
|
if (!drv)
|
|
|
|
return -ENOMEDIUM;
|
2010-02-14 14:39:18 +03:00
|
|
|
|
|
|
|
if (!bs->backing_hd) {
|
|
|
|
return -ENOTSUP;
|
2003-07-06 21:15:21 +04:00
|
|
|
}
|
|
|
|
|
2010-02-14 14:39:18 +03:00
|
|
|
if (bs->backing_hd->keep_read_only) {
|
|
|
|
return -EACCES;
|
|
|
|
}
|
2010-08-05 15:05:22 +04:00
|
|
|
|
|
|
|
backing_drv = bs->backing_hd->drv;
|
2010-02-14 14:39:18 +03:00
|
|
|
ro = bs->backing_hd->read_only;
|
|
|
|
strncpy(filename, bs->backing_hd->filename, sizeof(filename));
|
|
|
|
open_flags = bs->backing_hd->open_flags;
|
|
|
|
|
|
|
|
if (ro) {
|
|
|
|
/* re-open as RW */
|
|
|
|
bdrv_delete(bs->backing_hd);
|
|
|
|
bs->backing_hd = NULL;
|
|
|
|
bs_rw = bdrv_new("");
|
2010-08-05 15:05:22 +04:00
|
|
|
rw_ret = bdrv_open(bs_rw, filename, open_flags | BDRV_O_RDWR,
|
|
|
|
backing_drv);
|
2010-02-14 14:39:18 +03:00
|
|
|
if (rw_ret < 0) {
|
|
|
|
bdrv_delete(bs_rw);
|
|
|
|
/* try to re-open read-only */
|
|
|
|
bs_ro = bdrv_new("");
|
2010-08-05 15:05:22 +04:00
|
|
|
ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR,
|
|
|
|
backing_drv);
|
2010-02-14 14:39:18 +03:00
|
|
|
if (ret < 0) {
|
|
|
|
bdrv_delete(bs_ro);
|
|
|
|
/* drive not functional anymore */
|
|
|
|
bs->drv = NULL;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
bs->backing_hd = bs_ro;
|
|
|
|
return rw_ret;
|
|
|
|
}
|
|
|
|
bs->backing_hd = bs_rw;
|
2004-08-02 01:59:26 +04:00
|
|
|
}
|
2003-07-06 21:15:21 +04:00
|
|
|
|
2009-11-30 20:21:19 +03:00
|
|
|
total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
|
2010-07-16 19:17:01 +04:00
|
|
|
buf = qemu_malloc(COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE);
|
|
|
|
|
|
|
|
for (sector = 0; sector < total_sectors; sector += n) {
|
|
|
|
if (drv->bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n)) {
|
|
|
|
|
|
|
|
if (bdrv_read(bs, sector, buf, n) != 0) {
|
|
|
|
ret = -EIO;
|
|
|
|
goto ro_cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bdrv_write(bs->backing_hd, sector, buf, n) != 0) {
|
|
|
|
ret = -EIO;
|
|
|
|
goto ro_cleanup;
|
|
|
|
}
|
2004-08-02 01:59:26 +04:00
|
|
|
}
|
2003-07-06 21:15:21 +04:00
|
|
|
}
|
2005-12-18 21:28:15 +03:00
|
|
|
|
2010-01-17 14:32:30 +03:00
|
|
|
if (drv->bdrv_make_empty) {
|
|
|
|
ret = drv->bdrv_make_empty(bs);
|
|
|
|
bdrv_flush(bs);
|
|
|
|
}
|
2005-12-18 21:28:15 +03:00
|
|
|
|
2010-01-12 15:49:23 +03:00
|
|
|
/*
|
|
|
|
* Make sure all data we wrote to the backing device is actually
|
|
|
|
* stable on disk.
|
|
|
|
*/
|
|
|
|
if (bs->backing_hd)
|
|
|
|
bdrv_flush(bs->backing_hd);
|
2010-02-14 14:39:18 +03:00
|
|
|
|
|
|
|
ro_cleanup:
|
2010-07-16 19:17:01 +04:00
|
|
|
qemu_free(buf);
|
2010-02-14 14:39:18 +03:00
|
|
|
|
|
|
|
if (ro) {
|
|
|
|
/* re-open as RO */
|
|
|
|
bdrv_delete(bs->backing_hd);
|
|
|
|
bs->backing_hd = NULL;
|
|
|
|
bs_ro = bdrv_new("");
|
2010-08-05 15:05:22 +04:00
|
|
|
ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR,
|
|
|
|
backing_drv);
|
2010-02-14 14:39:18 +03:00
|
|
|
if (ret < 0) {
|
|
|
|
bdrv_delete(bs_ro);
|
|
|
|
/* drive not functional anymore */
|
|
|
|
bs->drv = NULL;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
bs->backing_hd = bs_ro;
|
|
|
|
bs->backing_hd->keep_read_only = 0;
|
|
|
|
}
|
|
|
|
|
2010-01-17 14:32:30 +03:00
|
|
|
return ret;
|
2003-07-06 21:15:21 +04:00
|
|
|
}
|
|
|
|
|
2010-06-02 20:55:18 +04:00
|
|
|
void bdrv_commit_all(void)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(bs, &bdrv_states, list) {
|
|
|
|
bdrv_commit(bs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-12 14:55:17 +03:00
|
|
|
/*
|
|
|
|
* Return values:
|
|
|
|
* 0 - success
|
|
|
|
* -EINVAL - backing format specified, but no file
|
|
|
|
* -ENOSPC - can't update the backing file because no space is left in the
|
|
|
|
* image file header
|
|
|
|
* -ENOTSUP - format driver doesn't support changing the backing file
|
|
|
|
*/
|
|
|
|
int bdrv_change_backing_file(BlockDriverState *bs,
|
|
|
|
const char *backing_file, const char *backing_fmt)
|
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
|
|
|
|
|
if (drv->bdrv_change_backing_file != NULL) {
|
|
|
|
return drv->bdrv_change_backing_file(bs, backing_file, backing_fmt);
|
|
|
|
} else {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-03 20:37:16 +03:00
|
|
|
static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset,
|
|
|
|
size_t size)
|
|
|
|
{
|
|
|
|
int64_t len;
|
|
|
|
|
|
|
|
if (!bdrv_is_inserted(bs))
|
|
|
|
return -ENOMEDIUM;
|
|
|
|
|
|
|
|
if (bs->growable)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
len = bdrv_getlength(bs);
|
|
|
|
|
2009-05-08 16:47:24 +04:00
|
|
|
if (offset < 0)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
if ((offset > len) || (len - offset < size))
|
2009-03-03 20:37:16 +03:00
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bdrv_check_request(BlockDriverState *bs, int64_t sector_num,
|
|
|
|
int nb_sectors)
|
|
|
|
{
|
2010-05-27 18:20:31 +04:00
|
|
|
return bdrv_check_byte_request(bs, sector_num * BDRV_SECTOR_SIZE,
|
|
|
|
nb_sectors * BDRV_SECTOR_SIZE);
|
2009-03-03 20:37:16 +03:00
|
|
|
}
|
|
|
|
|
2006-08-19 15:45:59 +04:00
|
|
|
/* return < 0 if error. See bdrv_write() for the return codes */
|
2007-09-17 01:08:06 +04:00
|
|
|
int bdrv_read(BlockDriverState *bs, int64_t sector_num,
|
2003-06-30 14:03:06 +04:00
|
|
|
uint8_t *buf, int nb_sectors)
|
|
|
|
{
|
2004-08-02 01:59:26 +04:00
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
|
|
2006-08-19 15:45:59 +04:00
|
|
|
if (!drv)
|
|
|
|
return -ENOMEDIUM;
|
2009-03-03 20:37:16 +03:00
|
|
|
if (bdrv_check_request(bs, sector_num, nb_sectors))
|
|
|
|
return -EIO;
|
2004-03-15 00:38:54 +03:00
|
|
|
|
2009-03-12 22:57:16 +03:00
|
|
|
return drv->bdrv_read(bs, sector_num, buf, nb_sectors);
|
2003-06-30 14:03:06 +04:00
|
|
|
}
|
|
|
|
|
2009-11-02 16:40:41 +03:00
|
|
|
static void set_dirty_bitmap(BlockDriverState *bs, int64_t sector_num,
|
2009-11-30 20:21:19 +03:00
|
|
|
int nb_sectors, int dirty)
|
2009-11-02 16:40:41 +03:00
|
|
|
{
|
|
|
|
int64_t start, end;
|
2009-11-30 20:21:20 +03:00
|
|
|
unsigned long val, idx, bit;
|
2009-11-30 20:21:19 +03:00
|
|
|
|
2009-11-30 20:21:19 +03:00
|
|
|
start = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK;
|
2009-11-30 20:21:20 +03:00
|
|
|
end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK;
|
2009-11-30 20:21:19 +03:00
|
|
|
|
|
|
|
for (; start <= end; start++) {
|
2009-11-30 20:21:20 +03:00
|
|
|
idx = start / (sizeof(unsigned long) * 8);
|
|
|
|
bit = start % (sizeof(unsigned long) * 8);
|
|
|
|
val = bs->dirty_bitmap[idx];
|
|
|
|
if (dirty) {
|
2010-11-08 22:02:54 +03:00
|
|
|
if (!(val & (1UL << bit))) {
|
2010-01-26 11:31:48 +03:00
|
|
|
bs->dirty_count++;
|
2010-11-08 22:02:54 +03:00
|
|
|
val |= 1UL << bit;
|
2010-01-26 11:31:48 +03:00
|
|
|
}
|
2009-11-30 20:21:20 +03:00
|
|
|
} else {
|
2010-11-08 22:02:54 +03:00
|
|
|
if (val & (1UL << bit)) {
|
2010-01-26 11:31:48 +03:00
|
|
|
bs->dirty_count--;
|
2010-11-08 22:02:54 +03:00
|
|
|
val &= ~(1UL << bit);
|
2010-01-26 11:31:48 +03:00
|
|
|
}
|
2009-11-30 20:21:20 +03:00
|
|
|
}
|
|
|
|
bs->dirty_bitmap[idx] = val;
|
2009-11-02 16:40:41 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-17 01:08:06 +04:00
|
|
|
/* Return < 0 if error. Important errors are:
|
2006-08-19 15:45:59 +04:00
|
|
|
-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
|
|
|
|
*/
|
2007-09-17 01:08:06 +04:00
|
|
|
int bdrv_write(BlockDriverState *bs, int64_t sector_num,
|
2003-06-30 14:03:06 +04:00
|
|
|
const uint8_t *buf, int nb_sectors)
|
|
|
|
{
|
2006-08-01 20:21:11 +04:00
|
|
|
BlockDriver *drv = bs->drv;
|
2006-08-19 15:45:59 +04:00
|
|
|
if (!bs->drv)
|
|
|
|
return -ENOMEDIUM;
|
2003-07-01 03:17:31 +04:00
|
|
|
if (bs->read_only)
|
2006-08-19 15:45:59 +04:00
|
|
|
return -EACCES;
|
2009-03-03 20:37:16 +03:00
|
|
|
if (bdrv_check_request(bs, sector_num, nb_sectors))
|
|
|
|
return -EIO;
|
2009-11-30 20:21:19 +03:00
|
|
|
|
2009-11-30 20:21:20 +03:00
|
|
|
if (bs->dirty_bitmap) {
|
2009-11-02 16:40:41 +03:00
|
|
|
set_dirty_bitmap(bs, sector_num, nb_sectors, 1);
|
|
|
|
}
|
2009-11-30 20:21:19 +03:00
|
|
|
|
2010-04-28 16:34:01 +04:00
|
|
|
if (bs->wr_highest_sector < sector_num + nb_sectors - 1) {
|
|
|
|
bs->wr_highest_sector = sector_num + nb_sectors - 1;
|
|
|
|
}
|
|
|
|
|
2009-01-15 23:43:39 +03:00
|
|
|
return drv->bdrv_write(bs, sector_num, buf, nb_sectors);
|
2006-08-01 20:21:11 +04:00
|
|
|
}
|
|
|
|
|
2009-03-12 22:57:16 +03:00
|
|
|
int bdrv_pread(BlockDriverState *bs, int64_t offset,
|
|
|
|
void *buf, int count1)
|
2006-08-01 20:21:11 +04:00
|
|
|
{
|
2009-11-30 20:21:19 +03:00
|
|
|
uint8_t tmp_buf[BDRV_SECTOR_SIZE];
|
2006-08-01 20:21:11 +04:00
|
|
|
int len, nb_sectors, count;
|
|
|
|
int64_t sector_num;
|
2010-01-20 17:03:02 +03:00
|
|
|
int ret;
|
2006-08-01 20:21:11 +04:00
|
|
|
|
|
|
|
count = count1;
|
|
|
|
/* first read to align to sector start */
|
2009-11-30 20:21:19 +03:00
|
|
|
len = (BDRV_SECTOR_SIZE - offset) & (BDRV_SECTOR_SIZE - 1);
|
2006-08-01 20:21:11 +04:00
|
|
|
if (len > count)
|
|
|
|
len = count;
|
2009-11-30 20:21:19 +03:00
|
|
|
sector_num = offset >> BDRV_SECTOR_BITS;
|
2006-08-01 20:21:11 +04:00
|
|
|
if (len > 0) {
|
2010-01-20 17:03:02 +03:00
|
|
|
if ((ret = bdrv_read(bs, sector_num, tmp_buf, 1)) < 0)
|
|
|
|
return ret;
|
2009-11-30 20:21:19 +03:00
|
|
|
memcpy(buf, tmp_buf + (offset & (BDRV_SECTOR_SIZE - 1)), len);
|
2006-08-01 20:21:11 +04:00
|
|
|
count -= len;
|
|
|
|
if (count == 0)
|
|
|
|
return count1;
|
|
|
|
sector_num++;
|
|
|
|
buf += len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read the sectors "in place" */
|
2009-11-30 20:21:19 +03:00
|
|
|
nb_sectors = count >> BDRV_SECTOR_BITS;
|
2006-08-01 20:21:11 +04:00
|
|
|
if (nb_sectors > 0) {
|
2010-01-20 17:03:02 +03:00
|
|
|
if ((ret = bdrv_read(bs, sector_num, buf, nb_sectors)) < 0)
|
|
|
|
return ret;
|
2006-08-01 20:21:11 +04:00
|
|
|
sector_num += nb_sectors;
|
2009-11-30 20:21:19 +03:00
|
|
|
len = nb_sectors << BDRV_SECTOR_BITS;
|
2006-08-01 20:21:11 +04:00
|
|
|
buf += len;
|
|
|
|
count -= len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add data from the last sector */
|
|
|
|
if (count > 0) {
|
2010-01-20 17:03:02 +03:00
|
|
|
if ((ret = bdrv_read(bs, sector_num, tmp_buf, 1)) < 0)
|
|
|
|
return ret;
|
2006-08-01 20:21:11 +04:00
|
|
|
memcpy(buf, tmp_buf, count);
|
|
|
|
}
|
|
|
|
return count1;
|
|
|
|
}
|
|
|
|
|
2009-03-12 22:57:16 +03:00
|
|
|
int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
|
|
|
|
const void *buf, int count1)
|
2006-08-01 20:21:11 +04:00
|
|
|
{
|
2009-11-30 20:21:19 +03:00
|
|
|
uint8_t tmp_buf[BDRV_SECTOR_SIZE];
|
2006-08-01 20:21:11 +04:00
|
|
|
int len, nb_sectors, count;
|
|
|
|
int64_t sector_num;
|
2010-01-20 17:03:02 +03:00
|
|
|
int ret;
|
2006-08-01 20:21:11 +04:00
|
|
|
|
|
|
|
count = count1;
|
|
|
|
/* first write to align to sector start */
|
2009-11-30 20:21:19 +03:00
|
|
|
len = (BDRV_SECTOR_SIZE - offset) & (BDRV_SECTOR_SIZE - 1);
|
2006-08-01 20:21:11 +04:00
|
|
|
if (len > count)
|
|
|
|
len = count;
|
2009-11-30 20:21:19 +03:00
|
|
|
sector_num = offset >> BDRV_SECTOR_BITS;
|
2006-08-01 20:21:11 +04:00
|
|
|
if (len > 0) {
|
2010-01-20 17:03:02 +03:00
|
|
|
if ((ret = bdrv_read(bs, sector_num, tmp_buf, 1)) < 0)
|
|
|
|
return ret;
|
2009-11-30 20:21:19 +03:00
|
|
|
memcpy(tmp_buf + (offset & (BDRV_SECTOR_SIZE - 1)), buf, len);
|
2010-01-20 17:03:02 +03:00
|
|
|
if ((ret = bdrv_write(bs, sector_num, tmp_buf, 1)) < 0)
|
|
|
|
return ret;
|
2006-08-01 20:21:11 +04:00
|
|
|
count -= len;
|
|
|
|
if (count == 0)
|
|
|
|
return count1;
|
|
|
|
sector_num++;
|
|
|
|
buf += len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* write the sectors "in place" */
|
2009-11-30 20:21:19 +03:00
|
|
|
nb_sectors = count >> BDRV_SECTOR_BITS;
|
2006-08-01 20:21:11 +04:00
|
|
|
if (nb_sectors > 0) {
|
2010-01-20 17:03:02 +03:00
|
|
|
if ((ret = bdrv_write(bs, sector_num, buf, nb_sectors)) < 0)
|
|
|
|
return ret;
|
2006-08-01 20:21:11 +04:00
|
|
|
sector_num += nb_sectors;
|
2009-11-30 20:21:19 +03:00
|
|
|
len = nb_sectors << BDRV_SECTOR_BITS;
|
2006-08-01 20:21:11 +04:00
|
|
|
buf += len;
|
|
|
|
count -= len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add data from the last sector */
|
|
|
|
if (count > 0) {
|
2010-01-20 17:03:02 +03:00
|
|
|
if ((ret = bdrv_read(bs, sector_num, tmp_buf, 1)) < 0)
|
|
|
|
return ret;
|
2006-08-01 20:21:11 +04:00
|
|
|
memcpy(tmp_buf, buf, count);
|
2010-01-20 17:03:02 +03:00
|
|
|
if ((ret = bdrv_write(bs, sector_num, tmp_buf, 1)) < 0)
|
|
|
|
return ret;
|
2006-08-01 20:21:11 +04:00
|
|
|
}
|
|
|
|
return count1;
|
|
|
|
}
|
|
|
|
|
2010-06-16 18:38:15 +04:00
|
|
|
/*
|
|
|
|
* Writes to the file and ensures that no writes are reordered across this
|
|
|
|
* request (acts as a barrier)
|
|
|
|
*
|
|
|
|
* Returns 0 on success, -errno in error cases.
|
|
|
|
*/
|
|
|
|
int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset,
|
|
|
|
const void *buf, int count)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = bdrv_pwrite(bs, offset, buf, count);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No flush needed for cache=writethrough, it uses O_DSYNC */
|
|
|
|
if ((bs->open_flags & BDRV_O_CACHE_MASK) != 0) {
|
|
|
|
bdrv_flush(bs);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Writes to the file and ensures that no writes are reordered across this
|
|
|
|
* request (acts as a barrier)
|
|
|
|
*
|
|
|
|
* Returns 0 on success, -errno in error cases.
|
|
|
|
*/
|
|
|
|
int bdrv_write_sync(BlockDriverState *bs, int64_t sector_num,
|
|
|
|
const uint8_t *buf, int nb_sectors)
|
|
|
|
{
|
|
|
|
return bdrv_pwrite_sync(bs, BDRV_SECTOR_SIZE * sector_num,
|
|
|
|
buf, BDRV_SECTOR_SIZE * nb_sectors);
|
|
|
|
}
|
|
|
|
|
2006-08-01 20:21:11 +04:00
|
|
|
/**
|
|
|
|
* Truncate file to 'offset' bytes (needed only for file protocols)
|
|
|
|
*/
|
|
|
|
int bdrv_truncate(BlockDriverState *bs, int64_t offset)
|
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
2010-04-19 19:56:41 +04:00
|
|
|
int ret;
|
2006-08-01 20:21:11 +04:00
|
|
|
if (!drv)
|
2006-08-19 15:45:59 +04:00
|
|
|
return -ENOMEDIUM;
|
2006-08-01 20:21:11 +04:00
|
|
|
if (!drv->bdrv_truncate)
|
|
|
|
return -ENOTSUP;
|
2009-10-26 17:25:16 +03:00
|
|
|
if (bs->read_only)
|
|
|
|
return -EACCES;
|
2011-01-26 17:12:35 +03:00
|
|
|
if (bdrv_in_use(bs))
|
|
|
|
return -EBUSY;
|
2010-04-19 19:56:41 +04:00
|
|
|
ret = drv->bdrv_truncate(bs, offset);
|
|
|
|
if (ret == 0) {
|
|
|
|
ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
|
2011-01-24 15:32:41 +03:00
|
|
|
if (bs->change_cb) {
|
|
|
|
bs->change_cb(bs->change_opaque, CHANGE_SIZE);
|
|
|
|
}
|
2010-04-19 19:56:41 +04:00
|
|
|
}
|
|
|
|
return ret;
|
2006-08-01 20:21:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Length of a file in bytes. Return < 0 if error or unknown.
|
|
|
|
*/
|
|
|
|
int64_t bdrv_getlength(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
|
if (!drv)
|
2006-08-19 15:45:59 +04:00
|
|
|
return -ENOMEDIUM;
|
2010-04-19 19:56:41 +04:00
|
|
|
|
2011-03-29 23:04:41 +04:00
|
|
|
if (bs->growable || bs->removable) {
|
|
|
|
if (drv->bdrv_getlength) {
|
|
|
|
return drv->bdrv_getlength(bs);
|
|
|
|
}
|
2006-08-01 20:21:11 +04:00
|
|
|
}
|
2011-03-29 23:04:41 +04:00
|
|
|
return bs->total_sectors * BDRV_SECTOR_SIZE;
|
2003-06-30 14:03:06 +04:00
|
|
|
}
|
|
|
|
|
2006-08-19 15:45:59 +04:00
|
|
|
/* return 0 as number of sectors if no device present or error */
|
2007-12-17 04:35:20 +03:00
|
|
|
void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr)
|
2003-06-30 14:03:06 +04:00
|
|
|
{
|
2006-08-19 15:45:59 +04:00
|
|
|
int64_t length;
|
|
|
|
length = bdrv_getlength(bs);
|
|
|
|
if (length < 0)
|
|
|
|
length = 0;
|
|
|
|
else
|
2009-11-30 20:21:19 +03:00
|
|
|
length = length >> BDRV_SECTOR_BITS;
|
2006-08-19 15:45:59 +04:00
|
|
|
*nb_sectors_ptr = length;
|
2003-06-30 14:03:06 +04:00
|
|
|
}
|
2004-02-17 00:56:36 +03:00
|
|
|
|
2008-11-26 00:50:24 +03:00
|
|
|
struct partition {
|
|
|
|
uint8_t boot_ind; /* 0x80 - active */
|
|
|
|
uint8_t head; /* starting head */
|
|
|
|
uint8_t sector; /* starting sector */
|
|
|
|
uint8_t cyl; /* starting cylinder */
|
|
|
|
uint8_t sys_ind; /* What partition type */
|
|
|
|
uint8_t end_head; /* end head */
|
|
|
|
uint8_t end_sector; /* end sector */
|
|
|
|
uint8_t end_cyl; /* end cylinder */
|
|
|
|
uint32_t start_sect; /* starting sector counting from 0 */
|
|
|
|
uint32_t nr_sects; /* nr of sectors in partition */
|
|
|
|
} __attribute__((packed));
|
|
|
|
|
|
|
|
/* try to guess the disk logical geometry from the MSDOS partition table. Return 0 if OK, -1 if could not guess */
|
|
|
|
static int guess_disk_lchs(BlockDriverState *bs,
|
|
|
|
int *pcylinders, int *pheads, int *psectors)
|
|
|
|
{
|
2010-05-27 18:20:31 +04:00
|
|
|
uint8_t buf[BDRV_SECTOR_SIZE];
|
2008-11-26 00:50:24 +03:00
|
|
|
int ret, i, heads, sectors, cylinders;
|
|
|
|
struct partition *p;
|
|
|
|
uint32_t nr_sects;
|
2008-12-05 20:56:40 +03:00
|
|
|
uint64_t nb_sectors;
|
2008-11-26 00:50:24 +03:00
|
|
|
|
|
|
|
bdrv_get_geometry(bs, &nb_sectors);
|
|
|
|
|
|
|
|
ret = bdrv_read(bs, 0, buf, 1);
|
|
|
|
if (ret < 0)
|
|
|
|
return -1;
|
|
|
|
/* test msdos magic */
|
|
|
|
if (buf[510] != 0x55 || buf[511] != 0xaa)
|
|
|
|
return -1;
|
|
|
|
for(i = 0; i < 4; i++) {
|
|
|
|
p = ((struct partition *)(buf + 0x1be)) + i;
|
|
|
|
nr_sects = le32_to_cpu(p->nr_sects);
|
|
|
|
if (nr_sects && p->end_head) {
|
|
|
|
/* We make the assumption that the partition terminates on
|
|
|
|
a cylinder boundary */
|
|
|
|
heads = p->end_head + 1;
|
|
|
|
sectors = p->end_sector & 63;
|
|
|
|
if (sectors == 0)
|
|
|
|
continue;
|
|
|
|
cylinders = nb_sectors / (heads * sectors);
|
|
|
|
if (cylinders < 1 || cylinders > 16383)
|
|
|
|
continue;
|
|
|
|
*pheads = heads;
|
|
|
|
*psectors = sectors;
|
|
|
|
*pcylinders = cylinders;
|
|
|
|
#if 0
|
|
|
|
printf("guessed geometry: LCHS=%d %d %d\n",
|
|
|
|
cylinders, heads, sectors);
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs)
|
|
|
|
{
|
|
|
|
int translation, lba_detected = 0;
|
|
|
|
int cylinders, heads, secs;
|
2008-12-05 20:56:40 +03:00
|
|
|
uint64_t nb_sectors;
|
2008-11-26 00:50:24 +03:00
|
|
|
|
|
|
|
/* if a geometry hint is available, use it */
|
|
|
|
bdrv_get_geometry(bs, &nb_sectors);
|
|
|
|
bdrv_get_geometry_hint(bs, &cylinders, &heads, &secs);
|
|
|
|
translation = bdrv_get_translation_hint(bs);
|
|
|
|
if (cylinders != 0) {
|
|
|
|
*pcyls = cylinders;
|
|
|
|
*pheads = heads;
|
|
|
|
*psecs = secs;
|
|
|
|
} else {
|
|
|
|
if (guess_disk_lchs(bs, &cylinders, &heads, &secs) == 0) {
|
|
|
|
if (heads > 16) {
|
|
|
|
/* if heads > 16, it means that a BIOS LBA
|
|
|
|
translation was active, so the default
|
|
|
|
hardware geometry is OK */
|
|
|
|
lba_detected = 1;
|
|
|
|
goto default_geometry;
|
|
|
|
} else {
|
|
|
|
*pcyls = cylinders;
|
|
|
|
*pheads = heads;
|
|
|
|
*psecs = secs;
|
|
|
|
/* disable any translation to be in sync with
|
|
|
|
the logical geometry */
|
|
|
|
if (translation == BIOS_ATA_TRANSLATION_AUTO) {
|
|
|
|
bdrv_set_translation_hint(bs,
|
|
|
|
BIOS_ATA_TRANSLATION_NONE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
default_geometry:
|
|
|
|
/* 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;
|
|
|
|
*pcyls = cylinders;
|
|
|
|
*pheads = 16;
|
|
|
|
*psecs = 63;
|
|
|
|
if ((lba_detected == 1) && (translation == BIOS_ATA_TRANSLATION_AUTO)) {
|
|
|
|
if ((*pcyls * *pheads) <= 131072) {
|
|
|
|
bdrv_set_translation_hint(bs,
|
|
|
|
BIOS_ATA_TRANSLATION_LARGE);
|
|
|
|
} else {
|
|
|
|
bdrv_set_translation_hint(bs,
|
|
|
|
BIOS_ATA_TRANSLATION_LBA);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bdrv_set_geometry_hint(bs, *pcyls, *pheads, *psecs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-17 01:08:06 +04:00
|
|
|
void bdrv_set_geometry_hint(BlockDriverState *bs,
|
2004-03-15 00:38:54 +03:00
|
|
|
int cyls, int heads, int secs)
|
|
|
|
{
|
|
|
|
bs->cyls = cyls;
|
|
|
|
bs->heads = heads;
|
|
|
|
bs->secs = secs;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bdrv_set_type_hint(BlockDriverState *bs, int type)
|
|
|
|
{
|
|
|
|
bs->type = type;
|
|
|
|
bs->removable = ((type == BDRV_TYPE_CDROM ||
|
|
|
|
type == BDRV_TYPE_FLOPPY));
|
|
|
|
}
|
|
|
|
|
2004-11-16 04:45:27 +03:00
|
|
|
void bdrv_set_translation_hint(BlockDriverState *bs, int translation)
|
|
|
|
{
|
|
|
|
bs->translation = translation;
|
|
|
|
}
|
|
|
|
|
2007-09-17 01:08:06 +04:00
|
|
|
void bdrv_get_geometry_hint(BlockDriverState *bs,
|
2004-03-15 00:38:54 +03:00
|
|
|
int *pcyls, int *pheads, int *psecs)
|
|
|
|
{
|
|
|
|
*pcyls = bs->cyls;
|
|
|
|
*pheads = bs->heads;
|
|
|
|
*psecs = bs->secs;
|
|
|
|
}
|
|
|
|
|
2011-02-12 23:43:32 +03:00
|
|
|
/* Recognize floppy formats */
|
|
|
|
typedef struct FDFormat {
|
|
|
|
FDriveType drive;
|
|
|
|
uint8_t last_sect;
|
|
|
|
uint8_t max_track;
|
|
|
|
uint8_t max_head;
|
|
|
|
} FDFormat;
|
|
|
|
|
|
|
|
static const FDFormat fd_formats[] = {
|
|
|
|
/* First entry is default format */
|
|
|
|
/* 1.44 MB 3"1/2 floppy disks */
|
|
|
|
{ FDRIVE_DRV_144, 18, 80, 1, },
|
|
|
|
{ FDRIVE_DRV_144, 20, 80, 1, },
|
|
|
|
{ FDRIVE_DRV_144, 21, 80, 1, },
|
|
|
|
{ FDRIVE_DRV_144, 21, 82, 1, },
|
|
|
|
{ FDRIVE_DRV_144, 21, 83, 1, },
|
|
|
|
{ FDRIVE_DRV_144, 22, 80, 1, },
|
|
|
|
{ FDRIVE_DRV_144, 23, 80, 1, },
|
|
|
|
{ FDRIVE_DRV_144, 24, 80, 1, },
|
|
|
|
/* 2.88 MB 3"1/2 floppy disks */
|
|
|
|
{ FDRIVE_DRV_288, 36, 80, 1, },
|
|
|
|
{ FDRIVE_DRV_288, 39, 80, 1, },
|
|
|
|
{ FDRIVE_DRV_288, 40, 80, 1, },
|
|
|
|
{ FDRIVE_DRV_288, 44, 80, 1, },
|
|
|
|
{ FDRIVE_DRV_288, 48, 80, 1, },
|
|
|
|
/* 720 kB 3"1/2 floppy disks */
|
|
|
|
{ FDRIVE_DRV_144, 9, 80, 1, },
|
|
|
|
{ FDRIVE_DRV_144, 10, 80, 1, },
|
|
|
|
{ FDRIVE_DRV_144, 10, 82, 1, },
|
|
|
|
{ FDRIVE_DRV_144, 10, 83, 1, },
|
|
|
|
{ FDRIVE_DRV_144, 13, 80, 1, },
|
|
|
|
{ FDRIVE_DRV_144, 14, 80, 1, },
|
|
|
|
/* 1.2 MB 5"1/4 floppy disks */
|
|
|
|
{ FDRIVE_DRV_120, 15, 80, 1, },
|
|
|
|
{ FDRIVE_DRV_120, 18, 80, 1, },
|
|
|
|
{ FDRIVE_DRV_120, 18, 82, 1, },
|
|
|
|
{ FDRIVE_DRV_120, 18, 83, 1, },
|
|
|
|
{ FDRIVE_DRV_120, 20, 80, 1, },
|
|
|
|
/* 720 kB 5"1/4 floppy disks */
|
|
|
|
{ FDRIVE_DRV_120, 9, 80, 1, },
|
|
|
|
{ FDRIVE_DRV_120, 11, 80, 1, },
|
|
|
|
/* 360 kB 5"1/4 floppy disks */
|
|
|
|
{ FDRIVE_DRV_120, 9, 40, 1, },
|
|
|
|
{ FDRIVE_DRV_120, 9, 40, 0, },
|
|
|
|
{ FDRIVE_DRV_120, 10, 41, 1, },
|
|
|
|
{ FDRIVE_DRV_120, 10, 42, 1, },
|
|
|
|
/* 320 kB 5"1/4 floppy disks */
|
|
|
|
{ FDRIVE_DRV_120, 8, 40, 1, },
|
|
|
|
{ FDRIVE_DRV_120, 8, 40, 0, },
|
|
|
|
/* 360 kB must match 5"1/4 better than 3"1/2... */
|
|
|
|
{ FDRIVE_DRV_144, 9, 80, 0, },
|
|
|
|
/* end */
|
|
|
|
{ FDRIVE_DRV_NONE, -1, -1, 0, },
|
|
|
|
};
|
|
|
|
|
|
|
|
void bdrv_get_floppy_geometry_hint(BlockDriverState *bs, int *nb_heads,
|
|
|
|
int *max_track, int *last_sect,
|
|
|
|
FDriveType drive_in, FDriveType *drive)
|
|
|
|
{
|
|
|
|
const FDFormat *parse;
|
|
|
|
uint64_t nb_sectors, size;
|
|
|
|
int i, first_match, match;
|
|
|
|
|
|
|
|
bdrv_get_geometry_hint(bs, nb_heads, max_track, last_sect);
|
|
|
|
if (*nb_heads != 0 && *max_track != 0 && *last_sect != 0) {
|
|
|
|
/* User defined disk */
|
|
|
|
} else {
|
|
|
|
bdrv_get_geometry(bs, &nb_sectors);
|
|
|
|
match = -1;
|
|
|
|
first_match = -1;
|
|
|
|
for (i = 0; ; i++) {
|
|
|
|
parse = &fd_formats[i];
|
|
|
|
if (parse->drive == FDRIVE_DRV_NONE) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (drive_in == parse->drive ||
|
|
|
|
drive_in == FDRIVE_DRV_NONE) {
|
|
|
|
size = (parse->max_head + 1) * parse->max_track *
|
|
|
|
parse->last_sect;
|
|
|
|
if (nb_sectors == size) {
|
|
|
|
match = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (first_match == -1) {
|
|
|
|
first_match = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (match == -1) {
|
|
|
|
if (first_match == -1) {
|
|
|
|
match = 1;
|
|
|
|
} else {
|
|
|
|
match = first_match;
|
|
|
|
}
|
|
|
|
parse = &fd_formats[match];
|
|
|
|
}
|
|
|
|
*nb_heads = parse->max_head + 1;
|
|
|
|
*max_track = parse->max_track;
|
|
|
|
*last_sect = parse->last_sect;
|
|
|
|
*drive = parse->drive;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-03-15 00:38:54 +03:00
|
|
|
int bdrv_get_type_hint(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
return bs->type;
|
|
|
|
}
|
|
|
|
|
2004-11-16 04:45:27 +03:00
|
|
|
int bdrv_get_translation_hint(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
return bs->translation;
|
|
|
|
}
|
|
|
|
|
2010-06-02 20:55:17 +04:00
|
|
|
void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error,
|
|
|
|
BlockErrorAction on_write_error)
|
|
|
|
{
|
|
|
|
bs->on_read_error = on_read_error;
|
|
|
|
bs->on_write_error = on_write_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
BlockErrorAction bdrv_get_on_error(BlockDriverState *bs, int is_read)
|
|
|
|
{
|
|
|
|
return is_read ? bs->on_read_error : bs->on_write_error;
|
|
|
|
}
|
|
|
|
|
block: Fix virtual media change for if=none
BlockDriverState member removable controls whether virtual media
change (monitor commands change, eject) is allowed. It is set when
the "type hint" is BDRV_TYPE_CDROM or BDRV_TYPE_FLOPPY.
The type hint is only set by drive_init(). It sets BDRV_TYPE_FLOPPY
for if=floppy. It sets BDRV_TYPE_CDROM for media=cdrom and if=ide,
scsi, xen, or none.
if=ide and if=scsi work, because the type hint makes it a CD-ROM.
if=xen likewise, I think.
For the same reason, if=none works when it's used by ide-drive or
scsi-disk. For other guest devices, there are problems:
* fdc: you can't change virtual media
$ qemu [...] -drive if=none,id=foo,... -global isa-fdc.driveA=foo
QEMU 0.12.50 monitor - type 'help' for more information
(qemu) eject foo
Device 'foo' is not removable
unless you add media=cdrom, but that makes it readonly.
* virtio: if you add media=cdrom, you can change virtual media. If
you eject, the guest gets I/O errors. If you change, the guest sees
the drive's contents suddenly change.
* scsi-generic: if you add media=cdrom, you can change virtual media.
I didn't test what that does to the guest or the physical device,
but it can't be pretty.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2010-06-25 15:42:14 +04:00
|
|
|
void bdrv_set_removable(BlockDriverState *bs, int removable)
|
|
|
|
{
|
|
|
|
bs->removable = removable;
|
|
|
|
if (removable && bs == bs_snapshots) {
|
|
|
|
bs_snapshots = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-03-15 00:38:54 +03:00
|
|
|
int bdrv_is_removable(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
return bs->removable;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bdrv_is_read_only(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
return bs->read_only;
|
|
|
|
}
|
|
|
|
|
2007-12-24 19:10:43 +03:00
|
|
|
int bdrv_is_sg(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
return bs->sg;
|
|
|
|
}
|
|
|
|
|
2009-09-04 21:01:15 +04:00
|
|
|
int bdrv_enable_write_cache(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
return bs->enable_write_cache;
|
|
|
|
}
|
|
|
|
|
2006-08-19 15:45:59 +04:00
|
|
|
/* XXX: no longer used */
|
2007-09-17 01:08:06 +04:00
|
|
|
void bdrv_set_change_cb(BlockDriverState *bs,
|
2011-01-24 15:32:41 +03:00
|
|
|
void (*change_cb)(void *opaque, int reason),
|
|
|
|
void *opaque)
|
2004-03-15 00:38:54 +03:00
|
|
|
{
|
|
|
|
bs->change_cb = change_cb;
|
|
|
|
bs->change_opaque = opaque;
|
|
|
|
}
|
|
|
|
|
2004-08-02 01:59:26 +04:00
|
|
|
int bdrv_is_encrypted(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
if (bs->backing_hd && bs->backing_hd->encrypted)
|
|
|
|
return 1;
|
|
|
|
return bs->encrypted;
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:01:01 +03:00
|
|
|
int bdrv_key_required(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
BlockDriverState *backing_hd = bs->backing_hd;
|
|
|
|
|
|
|
|
if (backing_hd && backing_hd->encrypted && !backing_hd->valid_key)
|
|
|
|
return 1;
|
|
|
|
return (bs->encrypted && !bs->valid_key);
|
|
|
|
}
|
|
|
|
|
2004-08-02 01:59:26 +04:00
|
|
|
int bdrv_set_key(BlockDriverState *bs, const char *key)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
if (bs->backing_hd && bs->backing_hd->encrypted) {
|
|
|
|
ret = bdrv_set_key(bs->backing_hd, key);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
if (!bs->encrypted)
|
|
|
|
return 0;
|
|
|
|
}
|
2010-03-06 01:26:13 +03:00
|
|
|
if (!bs->encrypted) {
|
|
|
|
return -EINVAL;
|
|
|
|
} else if (!bs->drv || !bs->drv->bdrv_set_key) {
|
|
|
|
return -ENOMEDIUM;
|
|
|
|
}
|
2009-03-06 02:01:01 +03:00
|
|
|
ret = bs->drv->bdrv_set_key(bs, key);
|
2009-03-06 02:01:15 +03:00
|
|
|
if (ret < 0) {
|
|
|
|
bs->valid_key = 0;
|
|
|
|
} else if (!bs->valid_key) {
|
|
|
|
bs->valid_key = 1;
|
|
|
|
/* call the change callback now, we skipped it on open */
|
|
|
|
bs->media_changed = 1;
|
|
|
|
if (bs->change_cb)
|
2011-01-24 15:32:41 +03:00
|
|
|
bs->change_cb(bs->change_opaque, CHANGE_MEDIA);
|
2009-03-06 02:01:15 +03:00
|
|
|
}
|
2009-03-06 02:01:01 +03:00
|
|
|
return ret;
|
2004-08-02 01:59:26 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size)
|
|
|
|
{
|
2006-08-19 15:45:59 +04:00
|
|
|
if (!bs->drv) {
|
2004-08-02 01:59:26 +04:00
|
|
|
buf[0] = '\0';
|
|
|
|
} else {
|
|
|
|
pstrcpy(buf, buf_size, bs->drv->format_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-17 01:08:06 +04:00
|
|
|
void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
|
2004-08-02 01:59:26 +04:00
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
BlockDriver *drv;
|
|
|
|
|
2010-04-13 13:29:33 +04:00
|
|
|
QLIST_FOREACH(drv, &bdrv_drivers, list) {
|
2004-08-02 01:59:26 +04:00
|
|
|
it(opaque, drv->format_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-03-15 00:38:54 +03:00
|
|
|
BlockDriverState *bdrv_find(const char *name)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
2010-04-10 10:02:42 +04:00
|
|
|
QTAILQ_FOREACH(bs, &bdrv_states, list) {
|
|
|
|
if (!strcmp(name, bs->device_name)) {
|
2004-03-15 00:38:54 +03:00
|
|
|
return bs;
|
2010-04-10 10:02:42 +04:00
|
|
|
}
|
2004-03-15 00:38:54 +03:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-06-02 20:55:20 +04:00
|
|
|
BlockDriverState *bdrv_next(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
if (!bs) {
|
|
|
|
return QTAILQ_FIRST(&bdrv_states);
|
|
|
|
}
|
|
|
|
return QTAILQ_NEXT(bs, list);
|
|
|
|
}
|
|
|
|
|
2009-03-06 02:00:43 +03:00
|
|
|
void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs), void *opaque)
|
2004-07-14 21:21:37 +04:00
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
2010-04-10 10:02:42 +04:00
|
|
|
QTAILQ_FOREACH(bs, &bdrv_states, list) {
|
2009-03-06 02:00:43 +03:00
|
|
|
it(opaque, bs);
|
2004-07-14 21:21:37 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-08-02 01:59:26 +04:00
|
|
|
const char *bdrv_get_device_name(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
return bs->device_name;
|
|
|
|
}
|
|
|
|
|
2010-10-21 18:43:43 +04:00
|
|
|
int bdrv_flush(BlockDriverState *bs)
|
2006-06-04 15:39:07 +04:00
|
|
|
{
|
2010-05-26 19:51:49 +04:00
|
|
|
if (bs->open_flags & BDRV_O_NO_FLUSH) {
|
2010-10-21 18:43:43 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bs->drv && bs->drv->bdrv_flush) {
|
|
|
|
return bs->drv->bdrv_flush(bs);
|
2010-05-26 19:51:49 +04:00
|
|
|
}
|
|
|
|
|
2010-10-21 18:43:43 +04:00
|
|
|
/*
|
|
|
|
* Some block drivers always operate in either writethrough or unsafe mode
|
|
|
|
* and don't support bdrv_flush therefore. Usually qemu doesn't know how
|
|
|
|
* the server works (because the behaviour is hardcoded or depends on
|
|
|
|
* server-side configuration), so we can't ensure that everything is safe
|
|
|
|
* on disk. Returning an error doesn't work because that would break guests
|
|
|
|
* even if the server operates in writethrough mode.
|
|
|
|
*
|
|
|
|
* Let's hope the user knows what he's doing.
|
|
|
|
*/
|
|
|
|
return 0;
|
2006-06-04 15:39:07 +04:00
|
|
|
}
|
|
|
|
|
2008-10-06 17:55:43 +04:00
|
|
|
void bdrv_flush_all(void)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
2010-04-10 10:02:42 +04:00
|
|
|
QTAILQ_FOREACH(bs, &bdrv_states, list) {
|
|
|
|
if (bs->drv && !bdrv_is_read_only(bs) &&
|
|
|
|
(!bdrv_is_removable(bs) || bdrv_is_inserted(bs))) {
|
2008-10-06 17:55:43 +04:00
|
|
|
bdrv_flush(bs);
|
2010-04-10 10:02:42 +04:00
|
|
|
}
|
|
|
|
}
|
2008-10-06 17:55:43 +04:00
|
|
|
}
|
|
|
|
|
2010-04-14 19:30:35 +04:00
|
|
|
int bdrv_has_zero_init(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
assert(bs->drv);
|
|
|
|
|
2010-07-28 13:26:29 +04:00
|
|
|
if (bs->drv->bdrv_has_zero_init) {
|
|
|
|
return bs->drv->bdrv_has_zero_init(bs);
|
2010-04-14 19:30:35 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-12-16 21:36:31 +03:00
|
|
|
int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
|
|
|
|
{
|
|
|
|
if (!bs->drv) {
|
|
|
|
return -ENOMEDIUM;
|
|
|
|
}
|
|
|
|
if (!bs->drv->bdrv_discard) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return bs->drv->bdrv_discard(bs, sector_num, nb_sectors);
|
|
|
|
}
|
|
|
|
|
2008-06-06 01:53:49 +04:00
|
|
|
/*
|
|
|
|
* Returns true iff the specified sector is present in the disk image. Drivers
|
|
|
|
* not implementing the functionality are assumed to not support backing files,
|
|
|
|
* hence all their sectors are reported as allocated.
|
|
|
|
*
|
|
|
|
* 'pnum' is set to the number of sectors (including and immediately following
|
|
|
|
* the specified sector) that are known to be in the same
|
|
|
|
* allocated/unallocated state.
|
|
|
|
*
|
|
|
|
* 'nb_sectors' is the max value 'pnum' should be set to.
|
|
|
|
*/
|
|
|
|
int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
|
|
|
int *pnum)
|
|
|
|
{
|
|
|
|
int64_t n;
|
|
|
|
if (!bs->drv->bdrv_is_allocated) {
|
|
|
|
if (sector_num >= bs->total_sectors) {
|
|
|
|
*pnum = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
n = bs->total_sectors - sector_num;
|
|
|
|
*pnum = (n < nb_sectors) ? (n) : (nb_sectors);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return bs->drv->bdrv_is_allocated(bs, sector_num, nb_sectors, pnum);
|
|
|
|
}
|
|
|
|
|
2010-02-03 17:41:01 +03:00
|
|
|
void bdrv_mon_event(const BlockDriverState *bdrv,
|
|
|
|
BlockMonEventAction action, int is_read)
|
|
|
|
{
|
|
|
|
QObject *data;
|
|
|
|
const char *action_str;
|
|
|
|
|
|
|
|
switch (action) {
|
|
|
|
case BDRV_ACTION_REPORT:
|
|
|
|
action_str = "report";
|
|
|
|
break;
|
|
|
|
case BDRV_ACTION_IGNORE:
|
|
|
|
action_str = "ignore";
|
|
|
|
break;
|
|
|
|
case BDRV_ACTION_STOP:
|
|
|
|
action_str = "stop";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
data = qobject_from_jsonf("{ 'device': %s, 'action': %s, 'operation': %s }",
|
|
|
|
bdrv->device_name,
|
|
|
|
action_str,
|
|
|
|
is_read ? "read" : "write");
|
|
|
|
monitor_protocol_event(QEVENT_BLOCK_IO_ERROR, data);
|
|
|
|
|
|
|
|
qobject_decref(data);
|
|
|
|
}
|
|
|
|
|
2009-12-10 22:16:06 +03:00
|
|
|
static void bdrv_print_dict(QObject *obj, void *opaque)
|
2004-03-15 00:38:54 +03:00
|
|
|
{
|
2009-12-10 22:16:06 +03:00
|
|
|
QDict *bs_dict;
|
|
|
|
Monitor *mon = opaque;
|
|
|
|
|
|
|
|
bs_dict = qobject_to_qdict(obj);
|
|
|
|
|
|
|
|
monitor_printf(mon, "%s: type=%s removable=%d",
|
|
|
|
qdict_get_str(bs_dict, "device"),
|
|
|
|
qdict_get_str(bs_dict, "type"),
|
|
|
|
qdict_get_bool(bs_dict, "removable"));
|
|
|
|
|
|
|
|
if (qdict_get_bool(bs_dict, "removable")) {
|
|
|
|
monitor_printf(mon, " locked=%d", qdict_get_bool(bs_dict, "locked"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qdict_haskey(bs_dict, "inserted")) {
|
|
|
|
QDict *qdict = qobject_to_qdict(qdict_get(bs_dict, "inserted"));
|
|
|
|
|
|
|
|
monitor_printf(mon, " file=");
|
|
|
|
monitor_print_filename(mon, qdict_get_str(qdict, "file"));
|
|
|
|
if (qdict_haskey(qdict, "backing_file")) {
|
|
|
|
monitor_printf(mon, " backing_file=");
|
|
|
|
monitor_print_filename(mon, qdict_get_str(qdict, "backing_file"));
|
|
|
|
}
|
|
|
|
monitor_printf(mon, " ro=%d drv=%s encrypted=%d",
|
|
|
|
qdict_get_bool(qdict, "ro"),
|
|
|
|
qdict_get_str(qdict, "drv"),
|
|
|
|
qdict_get_bool(qdict, "encrypted"));
|
|
|
|
} else {
|
|
|
|
monitor_printf(mon, " [not inserted]");
|
|
|
|
}
|
|
|
|
|
|
|
|
monitor_printf(mon, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void bdrv_info_print(Monitor *mon, const QObject *data)
|
|
|
|
{
|
|
|
|
qlist_iter(qobject_to_qlist(data), bdrv_print_dict, mon);
|
|
|
|
}
|
|
|
|
|
|
|
|
void bdrv_info(Monitor *mon, QObject **ret_data)
|
|
|
|
{
|
|
|
|
QList *bs_list;
|
2004-03-15 00:38:54 +03:00
|
|
|
BlockDriverState *bs;
|
|
|
|
|
2009-12-10 22:16:06 +03:00
|
|
|
bs_list = qlist_new();
|
|
|
|
|
2010-04-10 10:02:42 +04:00
|
|
|
QTAILQ_FOREACH(bs, &bdrv_states, list) {
|
2009-12-10 22:16:06 +03:00
|
|
|
QObject *bs_obj;
|
|
|
|
const char *type = "unknown";
|
|
|
|
|
2004-03-15 00:38:54 +03:00
|
|
|
switch(bs->type) {
|
|
|
|
case BDRV_TYPE_HD:
|
2009-12-10 22:16:06 +03:00
|
|
|
type = "hd";
|
2004-03-15 00:38:54 +03:00
|
|
|
break;
|
|
|
|
case BDRV_TYPE_CDROM:
|
2009-12-10 22:16:06 +03:00
|
|
|
type = "cdrom";
|
2004-03-15 00:38:54 +03:00
|
|
|
break;
|
|
|
|
case BDRV_TYPE_FLOPPY:
|
2009-12-10 22:16:06 +03:00
|
|
|
type = "floppy";
|
2004-03-15 00:38:54 +03:00
|
|
|
break;
|
|
|
|
}
|
2009-12-10 22:16:06 +03:00
|
|
|
|
|
|
|
bs_obj = qobject_from_jsonf("{ 'device': %s, 'type': %s, "
|
|
|
|
"'removable': %i, 'locked': %i }",
|
|
|
|
bs->device_name, type, bs->removable,
|
|
|
|
bs->locked);
|
|
|
|
|
2006-08-19 15:45:59 +04:00
|
|
|
if (bs->drv) {
|
2009-12-10 22:16:06 +03:00
|
|
|
QObject *obj;
|
|
|
|
QDict *bs_dict = qobject_to_qdict(bs_obj);
|
|
|
|
|
|
|
|
obj = qobject_from_jsonf("{ 'file': %s, 'ro': %i, 'drv': %s, "
|
|
|
|
"'encrypted': %i }",
|
|
|
|
bs->filename, bs->read_only,
|
|
|
|
bs->drv->format_name,
|
|
|
|
bdrv_is_encrypted(bs));
|
2006-12-22 17:11:32 +03:00
|
|
|
if (bs->backing_file[0] != '\0') {
|
2009-12-10 22:16:06 +03:00
|
|
|
QDict *qdict = qobject_to_qdict(obj);
|
|
|
|
qdict_put(qdict, "backing_file",
|
|
|
|
qstring_from_str(bs->backing_file));
|
2009-03-06 02:01:23 +03:00
|
|
|
}
|
2009-12-10 22:16:06 +03:00
|
|
|
|
|
|
|
qdict_put_obj(bs_dict, "inserted", obj);
|
2004-03-15 00:38:54 +03:00
|
|
|
}
|
2009-12-10 22:16:06 +03:00
|
|
|
qlist_append_obj(bs_list, bs_obj);
|
2004-03-15 00:38:54 +03:00
|
|
|
}
|
2009-12-10 22:16:06 +03:00
|
|
|
|
|
|
|
*ret_data = QOBJECT(bs_list);
|
2004-03-15 00:38:54 +03:00
|
|
|
}
|
2007-12-02 08:18:19 +03:00
|
|
|
|
2009-12-10 22:16:07 +03:00
|
|
|
static void bdrv_stats_iter(QObject *data, void *opaque)
|
2007-12-02 08:18:19 +03:00
|
|
|
{
|
2009-12-10 22:16:07 +03:00
|
|
|
QDict *qdict;
|
|
|
|
Monitor *mon = opaque;
|
|
|
|
|
|
|
|
qdict = qobject_to_qdict(data);
|
|
|
|
monitor_printf(mon, "%s:", qdict_get_str(qdict, "device"));
|
|
|
|
|
|
|
|
qdict = qobject_to_qdict(qdict_get(qdict, "stats"));
|
|
|
|
monitor_printf(mon, " rd_bytes=%" PRId64
|
|
|
|
" wr_bytes=%" PRId64
|
|
|
|
" rd_operations=%" PRId64
|
|
|
|
" wr_operations=%" PRId64
|
|
|
|
"\n",
|
|
|
|
qdict_get_int(qdict, "rd_bytes"),
|
|
|
|
qdict_get_int(qdict, "wr_bytes"),
|
|
|
|
qdict_get_int(qdict, "rd_operations"),
|
|
|
|
qdict_get_int(qdict, "wr_operations"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void bdrv_stats_print(Monitor *mon, const QObject *data)
|
|
|
|
{
|
|
|
|
qlist_iter(qobject_to_qlist(data), bdrv_stats_iter, mon);
|
|
|
|
}
|
|
|
|
|
2010-04-28 16:34:01 +04:00
|
|
|
static QObject* bdrv_info_stats_bs(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
QObject *res;
|
|
|
|
QDict *dict;
|
|
|
|
|
|
|
|
res = qobject_from_jsonf("{ 'stats': {"
|
|
|
|
"'rd_bytes': %" PRId64 ","
|
|
|
|
"'wr_bytes': %" PRId64 ","
|
|
|
|
"'rd_operations': %" PRId64 ","
|
|
|
|
"'wr_operations': %" PRId64 ","
|
|
|
|
"'wr_highest_offset': %" PRId64
|
|
|
|
"} }",
|
|
|
|
bs->rd_bytes, bs->wr_bytes,
|
|
|
|
bs->rd_ops, bs->wr_ops,
|
2010-06-14 22:55:33 +04:00
|
|
|
bs->wr_highest_sector *
|
|
|
|
(uint64_t)BDRV_SECTOR_SIZE);
|
2010-04-28 16:34:01 +04:00
|
|
|
dict = qobject_to_qdict(res);
|
|
|
|
|
|
|
|
if (*bs->device_name) {
|
|
|
|
qdict_put(dict, "device", qstring_from_str(bs->device_name));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bs->file) {
|
|
|
|
QObject *parent = bdrv_info_stats_bs(bs->file);
|
|
|
|
qdict_put_obj(dict, "parent", parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2009-12-10 22:16:07 +03:00
|
|
|
void bdrv_info_stats(Monitor *mon, QObject **ret_data)
|
|
|
|
{
|
|
|
|
QObject *obj;
|
|
|
|
QList *devices;
|
2007-12-02 08:18:19 +03:00
|
|
|
BlockDriverState *bs;
|
|
|
|
|
2009-12-10 22:16:07 +03:00
|
|
|
devices = qlist_new();
|
|
|
|
|
2010-04-10 10:02:42 +04:00
|
|
|
QTAILQ_FOREACH(bs, &bdrv_states, list) {
|
2010-04-28 16:34:01 +04:00
|
|
|
obj = bdrv_info_stats_bs(bs);
|
2009-12-10 22:16:07 +03:00
|
|
|
qlist_append_obj(devices, obj);
|
2007-12-02 08:18:19 +03:00
|
|
|
}
|
2009-12-10 22:16:07 +03:00
|
|
|
|
|
|
|
*ret_data = QOBJECT(devices);
|
2007-12-02 08:18:19 +03:00
|
|
|
}
|
2004-08-02 01:59:26 +04:00
|
|
|
|
2009-03-06 02:00:48 +03:00
|
|
|
const char *bdrv_get_encrypted_filename(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
if (bs->backing_hd && bs->backing_hd->encrypted)
|
|
|
|
return bs->backing_file;
|
|
|
|
else if (bs->encrypted)
|
|
|
|
return bs->filename;
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2007-09-17 01:08:06 +04:00
|
|
|
void bdrv_get_backing_filename(BlockDriverState *bs,
|
2006-08-01 20:21:11 +04:00
|
|
|
char *filename, int filename_size)
|
|
|
|
{
|
2010-01-12 14:55:16 +03:00
|
|
|
if (!bs->backing_file) {
|
2006-08-01 20:21:11 +04:00
|
|
|
pstrcpy(filename, filename_size, "");
|
|
|
|
} else {
|
|
|
|
pstrcpy(filename, filename_size, bs->backing_file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-17 01:08:06 +04:00
|
|
|
int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
2006-08-06 01:31:00 +04:00
|
|
|
const uint8_t *buf, int nb_sectors)
|
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
|
if (!drv)
|
2006-08-19 15:45:59 +04:00
|
|
|
return -ENOMEDIUM;
|
2006-08-06 01:31:00 +04:00
|
|
|
if (!drv->bdrv_write_compressed)
|
|
|
|
return -ENOTSUP;
|
2009-05-08 16:47:24 +04:00
|
|
|
if (bdrv_check_request(bs, sector_num, nb_sectors))
|
|
|
|
return -EIO;
|
2009-11-30 20:21:19 +03:00
|
|
|
|
2009-11-30 20:21:20 +03:00
|
|
|
if (bs->dirty_bitmap) {
|
2009-11-02 16:40:41 +03:00
|
|
|
set_dirty_bitmap(bs, sector_num, nb_sectors, 1);
|
|
|
|
}
|
2009-11-30 20:21:19 +03:00
|
|
|
|
2006-08-06 01:31:00 +04:00
|
|
|
return drv->bdrv_write_compressed(bs, sector_num, buf, nb_sectors);
|
|
|
|
}
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2006-08-06 01:31:00 +04:00
|
|
|
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
|
if (!drv)
|
2006-08-19 15:45:59 +04:00
|
|
|
return -ENOMEDIUM;
|
2006-08-06 01:31:00 +04:00
|
|
|
if (!drv->bdrv_get_info)
|
|
|
|
return -ENOTSUP;
|
|
|
|
memset(bdi, 0, sizeof(*bdi));
|
|
|
|
return drv->bdrv_get_info(bs, bdi);
|
|
|
|
}
|
|
|
|
|
2009-07-11 01:11:57 +04:00
|
|
|
int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
|
|
|
|
int64_t pos, int size)
|
2009-04-05 23:10:55 +04:00
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
|
if (!drv)
|
|
|
|
return -ENOMEDIUM;
|
2010-05-28 06:44:58 +04:00
|
|
|
if (drv->bdrv_save_vmstate)
|
|
|
|
return drv->bdrv_save_vmstate(bs, buf, pos, size);
|
|
|
|
if (bs->file)
|
|
|
|
return bdrv_save_vmstate(bs->file, buf, pos, size);
|
|
|
|
return -ENOTSUP;
|
2009-04-05 23:10:55 +04:00
|
|
|
}
|
|
|
|
|
2009-07-11 01:11:57 +04:00
|
|
|
int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
|
|
|
|
int64_t pos, int size)
|
2009-04-05 23:10:55 +04:00
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
|
if (!drv)
|
|
|
|
return -ENOMEDIUM;
|
2010-05-28 06:44:58 +04:00
|
|
|
if (drv->bdrv_load_vmstate)
|
|
|
|
return drv->bdrv_load_vmstate(bs, buf, pos, size);
|
|
|
|
if (bs->file)
|
|
|
|
return bdrv_load_vmstate(bs->file, buf, pos, size);
|
|
|
|
return -ENOTSUP;
|
2009-04-05 23:10:55 +04:00
|
|
|
}
|
|
|
|
|
2010-03-15 19:27:00 +03:00
|
|
|
void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event)
|
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
|
|
|
|
|
if (!drv || !drv->bdrv_debug_event) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return drv->bdrv_debug_event(bs, event);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2006-08-06 01:31:00 +04:00
|
|
|
/**************************************************************/
|
|
|
|
/* handling of snapshots */
|
|
|
|
|
savevm: Really verify if a drive supports snapshots
Both bdrv_can_snapshot() and bdrv_has_snapshot() does not work as advertized.
First issue: Their names implies different porpouses, but they do the same thing
and have exactly the same code. Maybe copied and pasted and forgotten?
bdrv_has_snapshot() is called in various places for actually checking if there
is snapshots or not.
Second issue: the way bdrv_can_snapshot() verifies if a block driver supports or
not snapshots does not catch all cases. E.g.: a raw image.
So when do_savevm() is called, first thing it does is to set a global
BlockDriverState to save the VM memory state calling get_bs_snapshots().
static BlockDriverState *get_bs_snapshots(void)
{
BlockDriverState *bs;
DriveInfo *dinfo;
if (bs_snapshots)
return bs_snapshots;
QTAILQ_FOREACH(dinfo, &drives, next) {
bs = dinfo->bdrv;
if (bdrv_can_snapshot(bs))
goto ok;
}
return NULL;
ok:
bs_snapshots = bs;
return bs;
}
bdrv_can_snapshot() may return a BlockDriverState that does not support
snapshots and do_savevm() goes on.
Later on in do_savevm(), we find:
QTAILQ_FOREACH(dinfo, &drives, next) {
bs1 = dinfo->bdrv;
if (bdrv_has_snapshot(bs1)) {
/* Write VM state size only to the image that contains the state */
sn->vm_state_size = (bs == bs1 ? vm_state_size : 0);
ret = bdrv_snapshot_create(bs1, sn);
if (ret < 0) {
monitor_printf(mon, "Error while creating snapshot on '%s'\n",
bdrv_get_device_name(bs1));
}
}
}
bdrv_has_snapshot(bs1) is not checking if the device does support or has
snapshots as explained above. Only in bdrv_snapshot_create() the device is
actually checked for snapshot support.
So, in cases where the first device supports snapshots, and the second does not,
the snapshot on the first will happen anyways. I believe this is not a good
behavior. It should be an all or nothing process.
This patch addresses these issues by making bdrv_can_snapshot() actually do
what it must do and enforces better tests to avoid errors in the middle of
do_savevm(). bdrv_has_snapshot() is removed and replaced by bdrv_can_snapshot()
where appropriate.
bdrv_can_snapshot() was moved from savevm.c to block.c. It makes more sense to me.
The loadvm_state() function was updated too to enforce that when loading a VM at
least all writable devices must support snapshots too.
Signed-off-by: Miguel Di Ciurcio Filho <miguel.filho@gmail.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2010-06-08 17:40:55 +04:00
|
|
|
int bdrv_can_snapshot(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
|
if (!drv || bdrv_is_removable(bs) || bdrv_is_read_only(bs)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!drv->bdrv_snapshot_create) {
|
|
|
|
if (bs->file != NULL) {
|
|
|
|
return bdrv_can_snapshot(bs->file);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-07-26 00:49:34 +04:00
|
|
|
int bdrv_is_snapshot(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
return !!(bs->open_flags & BDRV_O_SNAPSHOT);
|
|
|
|
}
|
|
|
|
|
2010-06-25 12:33:39 +04:00
|
|
|
BlockDriverState *bdrv_snapshots(void)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
2010-07-01 11:30:38 +04:00
|
|
|
if (bs_snapshots) {
|
2010-06-25 12:33:39 +04:00
|
|
|
return bs_snapshots;
|
2010-07-01 11:30:38 +04:00
|
|
|
}
|
2010-06-25 12:33:39 +04:00
|
|
|
|
|
|
|
bs = NULL;
|
|
|
|
while ((bs = bdrv_next(bs))) {
|
|
|
|
if (bdrv_can_snapshot(bs)) {
|
2010-07-01 11:30:38 +04:00
|
|
|
bs_snapshots = bs;
|
|
|
|
return bs;
|
2010-06-25 12:33:39 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2007-09-17 01:08:06 +04:00
|
|
|
int bdrv_snapshot_create(BlockDriverState *bs,
|
2006-08-06 01:31:00 +04:00
|
|
|
QEMUSnapshotInfo *sn_info)
|
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
|
if (!drv)
|
2006-08-19 15:45:59 +04:00
|
|
|
return -ENOMEDIUM;
|
2010-05-28 06:44:58 +04:00
|
|
|
if (drv->bdrv_snapshot_create)
|
|
|
|
return drv->bdrv_snapshot_create(bs, sn_info);
|
|
|
|
if (bs->file)
|
|
|
|
return bdrv_snapshot_create(bs->file, sn_info);
|
|
|
|
return -ENOTSUP;
|
2006-08-06 01:31:00 +04:00
|
|
|
}
|
|
|
|
|
2007-09-17 01:08:06 +04:00
|
|
|
int bdrv_snapshot_goto(BlockDriverState *bs,
|
2006-08-06 01:31:00 +04:00
|
|
|
const char *snapshot_id)
|
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
2010-05-28 06:44:58 +04:00
|
|
|
int ret, open_ret;
|
|
|
|
|
2006-08-06 01:31:00 +04:00
|
|
|
if (!drv)
|
2006-08-19 15:45:59 +04:00
|
|
|
return -ENOMEDIUM;
|
2010-05-28 06:44:58 +04:00
|
|
|
if (drv->bdrv_snapshot_goto)
|
|
|
|
return drv->bdrv_snapshot_goto(bs, snapshot_id);
|
|
|
|
|
|
|
|
if (bs->file) {
|
|
|
|
drv->bdrv_close(bs);
|
|
|
|
ret = bdrv_snapshot_goto(bs->file, snapshot_id);
|
|
|
|
open_ret = drv->bdrv_open(bs, bs->open_flags);
|
|
|
|
if (open_ret < 0) {
|
|
|
|
bdrv_delete(bs->file);
|
|
|
|
bs->drv = NULL;
|
|
|
|
return open_ret;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENOTSUP;
|
2006-08-06 01:31:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
|
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
|
if (!drv)
|
2006-08-19 15:45:59 +04:00
|
|
|
return -ENOMEDIUM;
|
2010-05-28 06:44:58 +04:00
|
|
|
if (drv->bdrv_snapshot_delete)
|
|
|
|
return drv->bdrv_snapshot_delete(bs, snapshot_id);
|
|
|
|
if (bs->file)
|
|
|
|
return bdrv_snapshot_delete(bs->file, snapshot_id);
|
|
|
|
return -ENOTSUP;
|
2006-08-06 01:31:00 +04:00
|
|
|
}
|
|
|
|
|
2007-09-17 01:08:06 +04:00
|
|
|
int bdrv_snapshot_list(BlockDriverState *bs,
|
2006-08-06 01:31:00 +04:00
|
|
|
QEMUSnapshotInfo **psn_info)
|
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
|
if (!drv)
|
2006-08-19 15:45:59 +04:00
|
|
|
return -ENOMEDIUM;
|
2010-05-28 06:44:58 +04:00
|
|
|
if (drv->bdrv_snapshot_list)
|
|
|
|
return drv->bdrv_snapshot_list(bs, psn_info);
|
|
|
|
if (bs->file)
|
|
|
|
return bdrv_snapshot_list(bs->file, psn_info);
|
|
|
|
return -ENOTSUP;
|
2006-08-06 01:31:00 +04:00
|
|
|
}
|
|
|
|
|
2010-09-22 06:58:41 +04:00
|
|
|
int bdrv_snapshot_load_tmp(BlockDriverState *bs,
|
|
|
|
const char *snapshot_name)
|
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
|
if (!drv) {
|
|
|
|
return -ENOMEDIUM;
|
|
|
|
}
|
|
|
|
if (!bs->read_only) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (drv->bdrv_snapshot_load_tmp) {
|
|
|
|
return drv->bdrv_snapshot_load_tmp(bs, snapshot_name);
|
|
|
|
}
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2006-08-06 01:31:00 +04:00
|
|
|
#define NB_SUFFIXES 4
|
|
|
|
|
|
|
|
char *get_human_readable_size(char *buf, int buf_size, int64_t size)
|
|
|
|
{
|
|
|
|
static const char suffixes[NB_SUFFIXES] = "KMGT";
|
|
|
|
int64_t base;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (size <= 999) {
|
|
|
|
snprintf(buf, buf_size, "%" PRId64, size);
|
|
|
|
} else {
|
|
|
|
base = 1024;
|
|
|
|
for(i = 0; i < NB_SUFFIXES; i++) {
|
|
|
|
if (size < (10 * base)) {
|
2007-09-17 01:08:06 +04:00
|
|
|
snprintf(buf, buf_size, "%0.1f%c",
|
2006-08-06 01:31:00 +04:00
|
|
|
(double)size / base,
|
|
|
|
suffixes[i]);
|
|
|
|
break;
|
|
|
|
} else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) {
|
2007-09-17 01:08:06 +04:00
|
|
|
snprintf(buf, buf_size, "%" PRId64 "%c",
|
2006-08-06 01:31:00 +04:00
|
|
|
((size + (base >> 1)) / base),
|
|
|
|
suffixes[i]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
base = base * 1024;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn)
|
|
|
|
{
|
|
|
|
char buf1[128], date_buf[128], clock_buf[128];
|
2007-01-07 20:27:07 +03:00
|
|
|
#ifdef _WIN32
|
|
|
|
struct tm *ptm;
|
|
|
|
#else
|
2006-08-06 01:31:00 +04:00
|
|
|
struct tm tm;
|
2007-01-07 20:27:07 +03:00
|
|
|
#endif
|
2006-08-06 01:31:00 +04:00
|
|
|
time_t ti;
|
|
|
|
int64_t secs;
|
|
|
|
|
|
|
|
if (!sn) {
|
2007-09-17 01:08:06 +04:00
|
|
|
snprintf(buf, buf_size,
|
|
|
|
"%-10s%-20s%7s%20s%15s",
|
2006-08-06 01:31:00 +04:00
|
|
|
"ID", "TAG", "VM SIZE", "DATE", "VM CLOCK");
|
|
|
|
} else {
|
|
|
|
ti = sn->date_sec;
|
2007-01-07 20:27:07 +03:00
|
|
|
#ifdef _WIN32
|
|
|
|
ptm = localtime(&ti);
|
|
|
|
strftime(date_buf, sizeof(date_buf),
|
|
|
|
"%Y-%m-%d %H:%M:%S", ptm);
|
|
|
|
#else
|
2006-08-06 01:31:00 +04:00
|
|
|
localtime_r(&ti, &tm);
|
|
|
|
strftime(date_buf, sizeof(date_buf),
|
|
|
|
"%Y-%m-%d %H:%M:%S", &tm);
|
2007-01-07 20:27:07 +03:00
|
|
|
#endif
|
2006-08-06 01:31:00 +04:00
|
|
|
secs = sn->vm_clock_nsec / 1000000000;
|
|
|
|
snprintf(clock_buf, sizeof(clock_buf),
|
|
|
|
"%02d:%02d:%02d.%03d",
|
|
|
|
(int)(secs / 3600),
|
|
|
|
(int)((secs / 60) % 60),
|
2007-09-17 01:08:06 +04:00
|
|
|
(int)(secs % 60),
|
2006-08-06 01:31:00 +04:00
|
|
|
(int)((sn->vm_clock_nsec / 1000000) % 1000));
|
|
|
|
snprintf(buf, buf_size,
|
2007-09-17 01:08:06 +04:00
|
|
|
"%-10s%-20s%7s%20s%15s",
|
2006-08-06 01:31:00 +04:00
|
|
|
sn->id_str, sn->name,
|
|
|
|
get_human_readable_size(buf1, sizeof(buf1), sn->vm_state_size),
|
|
|
|
date_buf,
|
|
|
|
clock_buf);
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2006-08-01 20:21:11 +04:00
|
|
|
|
2004-08-02 01:59:26 +04:00
|
|
|
/**************************************************************/
|
2006-08-01 20:21:11 +04:00
|
|
|
/* async I/Os */
|
2004-08-02 01:59:26 +04:00
|
|
|
|
2009-01-22 19:59:24 +03:00
|
|
|
BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
|
2009-04-07 22:43:24 +04:00
|
|
|
QEMUIOVector *qiov, int nb_sectors,
|
2009-01-22 19:59:24 +03:00
|
|
|
BlockDriverCompletionFunc *cb, void *opaque)
|
2006-08-01 20:21:11 +04:00
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
2007-12-02 08:18:19 +03:00
|
|
|
BlockDriverAIOCB *ret;
|
2006-08-01 20:21:11 +04:00
|
|
|
|
2010-10-05 17:28:53 +04:00
|
|
|
trace_bdrv_aio_readv(bs, sector_num, nb_sectors, opaque);
|
|
|
|
|
2006-08-19 15:45:59 +04:00
|
|
|
if (!drv)
|
2006-08-07 06:38:06 +04:00
|
|
|
return NULL;
|
2009-03-03 20:37:16 +03:00
|
|
|
if (bdrv_check_request(bs, sector_num, nb_sectors))
|
|
|
|
return NULL;
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2009-04-07 22:43:24 +04:00
|
|
|
ret = drv->bdrv_aio_readv(bs, sector_num, qiov, nb_sectors,
|
|
|
|
cb, opaque);
|
2007-12-02 08:18:19 +03:00
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
/* Update stats even though technically transfer has not happened. */
|
2009-11-30 20:21:19 +03:00
|
|
|
bs->rd_bytes += (unsigned) nb_sectors * BDRV_SECTOR_SIZE;
|
2007-12-02 08:18:19 +03:00
|
|
|
bs->rd_ops ++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2004-08-02 01:59:26 +04:00
|
|
|
}
|
|
|
|
|
2010-11-08 22:02:55 +03:00
|
|
|
typedef struct BlockCompleteData {
|
|
|
|
BlockDriverCompletionFunc *cb;
|
|
|
|
void *opaque;
|
|
|
|
BlockDriverState *bs;
|
|
|
|
int64_t sector_num;
|
|
|
|
int nb_sectors;
|
|
|
|
} BlockCompleteData;
|
|
|
|
|
|
|
|
static void block_complete_cb(void *opaque, int ret)
|
|
|
|
{
|
|
|
|
BlockCompleteData *b = opaque;
|
|
|
|
|
|
|
|
if (b->bs->dirty_bitmap) {
|
|
|
|
set_dirty_bitmap(b->bs, b->sector_num, b->nb_sectors, 1);
|
|
|
|
}
|
|
|
|
b->cb(b->opaque, ret);
|
|
|
|
qemu_free(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
static BlockCompleteData *blk_dirty_cb_alloc(BlockDriverState *bs,
|
|
|
|
int64_t sector_num,
|
|
|
|
int nb_sectors,
|
|
|
|
BlockDriverCompletionFunc *cb,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
BlockCompleteData *blkdata = qemu_mallocz(sizeof(BlockCompleteData));
|
|
|
|
|
|
|
|
blkdata->bs = bs;
|
|
|
|
blkdata->cb = cb;
|
|
|
|
blkdata->opaque = opaque;
|
|
|
|
blkdata->sector_num = sector_num;
|
|
|
|
blkdata->nb_sectors = nb_sectors;
|
|
|
|
|
|
|
|
return blkdata;
|
|
|
|
}
|
|
|
|
|
2009-04-07 22:43:24 +04:00
|
|
|
BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
|
|
|
|
QEMUIOVector *qiov, int nb_sectors,
|
|
|
|
BlockDriverCompletionFunc *cb, void *opaque)
|
2004-08-02 01:59:26 +04:00
|
|
|
{
|
2006-08-01 20:21:11 +04:00
|
|
|
BlockDriver *drv = bs->drv;
|
2007-12-02 08:18:19 +03:00
|
|
|
BlockDriverAIOCB *ret;
|
2010-11-08 22:02:55 +03:00
|
|
|
BlockCompleteData *blk_cb_data;
|
2004-08-02 01:59:26 +04:00
|
|
|
|
2010-10-05 17:28:53 +04:00
|
|
|
trace_bdrv_aio_writev(bs, sector_num, nb_sectors, opaque);
|
|
|
|
|
2006-08-19 15:45:59 +04:00
|
|
|
if (!drv)
|
2006-08-07 06:38:06 +04:00
|
|
|
return NULL;
|
2006-08-01 20:21:11 +04:00
|
|
|
if (bs->read_only)
|
2006-08-07 06:38:06 +04:00
|
|
|
return NULL;
|
2009-03-03 20:37:16 +03:00
|
|
|
if (bdrv_check_request(bs, sector_num, nb_sectors))
|
|
|
|
return NULL;
|
2006-08-01 20:21:11 +04:00
|
|
|
|
2009-11-30 20:21:20 +03:00
|
|
|
if (bs->dirty_bitmap) {
|
2010-11-08 22:02:55 +03:00
|
|
|
blk_cb_data = blk_dirty_cb_alloc(bs, sector_num, nb_sectors, cb,
|
|
|
|
opaque);
|
|
|
|
cb = &block_complete_cb;
|
|
|
|
opaque = blk_cb_data;
|
2009-11-02 16:40:41 +03:00
|
|
|
}
|
2009-11-30 20:21:19 +03:00
|
|
|
|
2009-04-07 22:43:24 +04:00
|
|
|
ret = drv->bdrv_aio_writev(bs, sector_num, qiov, nb_sectors,
|
|
|
|
cb, opaque);
|
2007-12-02 08:18:19 +03:00
|
|
|
|
|
|
|
if (ret) {
|
2010-04-28 16:34:01 +04:00
|
|
|
/* Update stats even though technically transfer has not happened. */
|
|
|
|
bs->wr_bytes += (unsigned) nb_sectors * BDRV_SECTOR_SIZE;
|
|
|
|
bs->wr_ops ++;
|
|
|
|
if (bs->wr_highest_sector < sector_num + nb_sectors - 1) {
|
|
|
|
bs->wr_highest_sector = sector_num + nb_sectors - 1;
|
|
|
|
}
|
2007-12-02 08:18:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2006-08-01 20:21:11 +04:00
|
|
|
}
|
|
|
|
|
2009-09-09 19:53:37 +04:00
|
|
|
|
|
|
|
typedef struct MultiwriteCB {
|
|
|
|
int error;
|
|
|
|
int num_requests;
|
|
|
|
int num_callbacks;
|
|
|
|
struct {
|
|
|
|
BlockDriverCompletionFunc *cb;
|
|
|
|
void *opaque;
|
|
|
|
QEMUIOVector *free_qiov;
|
|
|
|
void *free_buf;
|
|
|
|
} callbacks[];
|
|
|
|
} MultiwriteCB;
|
|
|
|
|
|
|
|
static void multiwrite_user_cb(MultiwriteCB *mcb)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < mcb->num_callbacks; i++) {
|
|
|
|
mcb->callbacks[i].cb(mcb->callbacks[i].opaque, mcb->error);
|
2010-04-21 23:35:45 +04:00
|
|
|
if (mcb->callbacks[i].free_qiov) {
|
|
|
|
qemu_iovec_destroy(mcb->callbacks[i].free_qiov);
|
|
|
|
}
|
2009-09-09 19:53:37 +04:00
|
|
|
qemu_free(mcb->callbacks[i].free_qiov);
|
2010-01-25 00:23:56 +03:00
|
|
|
qemu_vfree(mcb->callbacks[i].free_buf);
|
2009-09-09 19:53:37 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void multiwrite_cb(void *opaque, int ret)
|
|
|
|
{
|
|
|
|
MultiwriteCB *mcb = opaque;
|
|
|
|
|
2010-05-22 21:15:08 +04:00
|
|
|
trace_multiwrite_cb(mcb, ret);
|
|
|
|
|
2010-04-02 00:48:44 +04:00
|
|
|
if (ret < 0 && !mcb->error) {
|
2009-09-09 19:53:37 +04:00
|
|
|
mcb->error = ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
mcb->num_requests--;
|
|
|
|
if (mcb->num_requests == 0) {
|
2010-07-01 18:08:51 +04:00
|
|
|
multiwrite_user_cb(mcb);
|
2009-09-09 19:53:37 +04:00
|
|
|
qemu_free(mcb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int multiwrite_req_compare(const void *a, const void *b)
|
|
|
|
{
|
2010-05-19 22:53:10 +04:00
|
|
|
const BlockRequest *req1 = a, *req2 = b;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that we can't simply subtract req2->sector from req1->sector
|
|
|
|
* here as that could overflow the return value.
|
|
|
|
*/
|
|
|
|
if (req1->sector > req2->sector) {
|
|
|
|
return 1;
|
|
|
|
} else if (req1->sector < req2->sector) {
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
2009-09-09 19:53:37 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Takes a bunch of requests and tries to merge them. Returns the number of
|
|
|
|
* requests that remain after merging.
|
|
|
|
*/
|
|
|
|
static int multiwrite_merge(BlockDriverState *bs, BlockRequest *reqs,
|
|
|
|
int num_reqs, MultiwriteCB *mcb)
|
|
|
|
{
|
|
|
|
int i, outidx;
|
|
|
|
|
|
|
|
// Sort requests by start sector
|
|
|
|
qsort(reqs, num_reqs, sizeof(*reqs), &multiwrite_req_compare);
|
|
|
|
|
|
|
|
// Check if adjacent requests touch the same clusters. If so, combine them,
|
|
|
|
// filling up gaps with zero sectors.
|
|
|
|
outidx = 0;
|
|
|
|
for (i = 1; i < num_reqs; i++) {
|
|
|
|
int merge = 0;
|
|
|
|
int64_t oldreq_last = reqs[outidx].sector + reqs[outidx].nb_sectors;
|
|
|
|
|
|
|
|
// This handles the cases that are valid for all block drivers, namely
|
|
|
|
// exactly sequential writes and overlapping writes.
|
|
|
|
if (reqs[i].sector <= oldreq_last) {
|
|
|
|
merge = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The block driver may decide that it makes sense to combine requests
|
|
|
|
// even if there is a gap of some sectors between them. In this case,
|
|
|
|
// the gap is filled with zeros (therefore only applicable for yet
|
|
|
|
// unused space in format like qcow2).
|
|
|
|
if (!merge && bs->drv->bdrv_merge_requests) {
|
|
|
|
merge = bs->drv->bdrv_merge_requests(bs, &reqs[outidx], &reqs[i]);
|
|
|
|
}
|
|
|
|
|
2010-01-26 16:49:08 +03:00
|
|
|
if (reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1 > IOV_MAX) {
|
|
|
|
merge = 0;
|
|
|
|
}
|
|
|
|
|
2009-09-09 19:53:37 +04:00
|
|
|
if (merge) {
|
|
|
|
size_t size;
|
|
|
|
QEMUIOVector *qiov = qemu_mallocz(sizeof(*qiov));
|
|
|
|
qemu_iovec_init(qiov,
|
|
|
|
reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1);
|
|
|
|
|
|
|
|
// Add the first request to the merged one. If the requests are
|
|
|
|
// overlapping, drop the last sectors of the first request.
|
|
|
|
size = (reqs[i].sector - reqs[outidx].sector) << 9;
|
|
|
|
qemu_iovec_concat(qiov, reqs[outidx].qiov, size);
|
|
|
|
|
|
|
|
// We might need to add some zeros between the two requests
|
|
|
|
if (reqs[i].sector > oldreq_last) {
|
|
|
|
size_t zero_bytes = (reqs[i].sector - oldreq_last) << 9;
|
|
|
|
uint8_t *buf = qemu_blockalign(bs, zero_bytes);
|
|
|
|
memset(buf, 0, zero_bytes);
|
|
|
|
qemu_iovec_add(qiov, buf, zero_bytes);
|
|
|
|
mcb->callbacks[i].free_buf = buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the second request
|
|
|
|
qemu_iovec_concat(qiov, reqs[i].qiov, reqs[i].qiov->size);
|
|
|
|
|
2010-05-21 13:09:42 +04:00
|
|
|
reqs[outidx].nb_sectors = qiov->size >> 9;
|
2009-09-09 19:53:37 +04:00
|
|
|
reqs[outidx].qiov = qiov;
|
|
|
|
|
|
|
|
mcb->callbacks[i].free_qiov = reqs[outidx].qiov;
|
|
|
|
} else {
|
|
|
|
outidx++;
|
|
|
|
reqs[outidx].sector = reqs[i].sector;
|
|
|
|
reqs[outidx].nb_sectors = reqs[i].nb_sectors;
|
|
|
|
reqs[outidx].qiov = reqs[i].qiov;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return outidx + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Submit multiple AIO write requests at once.
|
|
|
|
*
|
|
|
|
* On success, the function returns 0 and all requests in the reqs array have
|
|
|
|
* been submitted. In error case this function returns -1, and any of the
|
|
|
|
* requests may or may not be submitted yet. In particular, this means that the
|
|
|
|
* callback will be called for some of the requests, for others it won't. The
|
|
|
|
* caller must check the error field of the BlockRequest to wait for the right
|
|
|
|
* callbacks (if error != 0, no callback will be called).
|
|
|
|
*
|
|
|
|
* The implementation may modify the contents of the reqs array, e.g. to merge
|
|
|
|
* requests. However, the fields opaque and error are left unmodified as they
|
|
|
|
* are used to signal failure for a single request to the caller.
|
|
|
|
*/
|
|
|
|
int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
|
|
|
|
{
|
|
|
|
BlockDriverAIOCB *acb;
|
|
|
|
MultiwriteCB *mcb;
|
|
|
|
int i;
|
|
|
|
|
2011-03-07 19:01:04 +03:00
|
|
|
/* don't submit writes if we don't have a medium */
|
|
|
|
if (bs->drv == NULL) {
|
|
|
|
for (i = 0; i < num_reqs; i++) {
|
|
|
|
reqs[i].error = -ENOMEDIUM;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-09-09 19:53:37 +04:00
|
|
|
if (num_reqs == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create MultiwriteCB structure
|
|
|
|
mcb = qemu_mallocz(sizeof(*mcb) + num_reqs * sizeof(*mcb->callbacks));
|
|
|
|
mcb->num_requests = 0;
|
|
|
|
mcb->num_callbacks = num_reqs;
|
|
|
|
|
|
|
|
for (i = 0; i < num_reqs; i++) {
|
|
|
|
mcb->callbacks[i].cb = reqs[i].cb;
|
|
|
|
mcb->callbacks[i].opaque = reqs[i].opaque;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for mergable requests
|
|
|
|
num_reqs = multiwrite_merge(bs, reqs, num_reqs, mcb);
|
|
|
|
|
2010-05-22 21:15:08 +04:00
|
|
|
trace_bdrv_aio_multiwrite(mcb, mcb->num_callbacks, num_reqs);
|
|
|
|
|
2010-07-02 16:01:21 +04:00
|
|
|
/*
|
|
|
|
* Run the aio requests. As soon as one request can't be submitted
|
|
|
|
* successfully, fail all requests that are not yet submitted (we must
|
|
|
|
* return failure for all requests anyway)
|
|
|
|
*
|
|
|
|
* num_requests cannot be set to the right value immediately: If
|
|
|
|
* bdrv_aio_writev fails for some request, num_requests would be too high
|
|
|
|
* and therefore multiwrite_cb() would never recognize the multiwrite
|
|
|
|
* request as completed. We also cannot use the loop variable i to set it
|
|
|
|
* when the first request fails because the callback may already have been
|
|
|
|
* called for previously submitted requests. Thus, num_requests must be
|
|
|
|
* incremented for each request that is submitted.
|
|
|
|
*
|
|
|
|
* The problem that callbacks may be called early also means that we need
|
|
|
|
* to take care that num_requests doesn't become 0 before all requests are
|
|
|
|
* submitted - multiwrite_cb() would consider the multiwrite request
|
|
|
|
* completed. A dummy request that is "completed" by a manual call to
|
|
|
|
* multiwrite_cb() takes care of this.
|
|
|
|
*/
|
|
|
|
mcb->num_requests = 1;
|
|
|
|
|
2010-05-22 21:15:08 +04:00
|
|
|
// Run the aio requests
|
2009-09-09 19:53:37 +04:00
|
|
|
for (i = 0; i < num_reqs; i++) {
|
2010-07-02 16:01:21 +04:00
|
|
|
mcb->num_requests++;
|
2009-09-09 19:53:37 +04:00
|
|
|
acb = bdrv_aio_writev(bs, reqs[i].sector, reqs[i].qiov,
|
|
|
|
reqs[i].nb_sectors, multiwrite_cb, mcb);
|
|
|
|
|
|
|
|
if (acb == NULL) {
|
|
|
|
// We can only fail the whole thing if no request has been
|
|
|
|
// submitted yet. Otherwise we'll wait for the submitted AIOs to
|
|
|
|
// complete and report the error in the callback.
|
2010-07-02 16:01:21 +04:00
|
|
|
if (i == 0) {
|
2010-05-22 21:15:08 +04:00
|
|
|
trace_bdrv_aio_multiwrite_earlyfail(mcb);
|
2009-09-09 19:53:37 +04:00
|
|
|
goto fail;
|
|
|
|
} else {
|
2010-05-22 21:15:08 +04:00
|
|
|
trace_bdrv_aio_multiwrite_latefail(mcb, i);
|
2010-04-06 20:24:07 +04:00
|
|
|
multiwrite_cb(mcb, -EIO);
|
2009-09-09 19:53:37 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-02 16:01:21 +04:00
|
|
|
/* Complete the dummy request */
|
|
|
|
multiwrite_cb(mcb, 0);
|
|
|
|
|
2009-09-09 19:53:37 +04:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
2010-07-02 16:01:21 +04:00
|
|
|
for (i = 0; i < mcb->num_callbacks; i++) {
|
|
|
|
reqs[i].error = -EIO;
|
|
|
|
}
|
2010-05-14 01:14:33 +04:00
|
|
|
qemu_free(mcb);
|
2009-09-09 19:53:37 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-09-04 21:01:49 +04:00
|
|
|
BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs,
|
|
|
|
BlockDriverCompletionFunc *cb, void *opaque)
|
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
|
|
2011-03-07 10:58:04 +03:00
|
|
|
trace_bdrv_aio_flush(bs, opaque);
|
|
|
|
|
2010-05-26 19:51:49 +04:00
|
|
|
if (bs->open_flags & BDRV_O_NO_FLUSH) {
|
|
|
|
return bdrv_aio_noop_em(bs, cb, opaque);
|
|
|
|
}
|
|
|
|
|
2009-09-04 21:01:49 +04:00
|
|
|
if (!drv)
|
|
|
|
return NULL;
|
|
|
|
return drv->bdrv_aio_flush(bs, cb, opaque);
|
|
|
|
}
|
|
|
|
|
2006-08-01 20:21:11 +04:00
|
|
|
void bdrv_aio_cancel(BlockDriverAIOCB *acb)
|
|
|
|
{
|
2009-03-20 21:25:59 +03:00
|
|
|
acb->pool->cancel(acb);
|
2006-08-01 20:21:11 +04:00
|
|
|
}
|
|
|
|
|
2006-08-07 06:38:06 +04:00
|
|
|
|
2006-08-01 20:21:11 +04:00
|
|
|
/**************************************************************/
|
|
|
|
/* async block device emulation */
|
|
|
|
|
2009-05-25 14:37:32 +04:00
|
|
|
typedef struct BlockDriverAIOCBSync {
|
|
|
|
BlockDriverAIOCB common;
|
|
|
|
QEMUBH *bh;
|
|
|
|
int ret;
|
|
|
|
/* vector translation state */
|
|
|
|
QEMUIOVector *qiov;
|
|
|
|
uint8_t *bounce;
|
|
|
|
int is_write;
|
|
|
|
} BlockDriverAIOCBSync;
|
|
|
|
|
|
|
|
static void bdrv_aio_cancel_em(BlockDriverAIOCB *blockacb)
|
|
|
|
{
|
2010-05-05 13:44:39 +04:00
|
|
|
BlockDriverAIOCBSync *acb =
|
|
|
|
container_of(blockacb, BlockDriverAIOCBSync, common);
|
2009-06-01 13:07:23 +04:00
|
|
|
qemu_bh_delete(acb->bh);
|
2009-06-23 17:20:36 +04:00
|
|
|
acb->bh = NULL;
|
2009-05-25 14:37:32 +04:00
|
|
|
qemu_aio_release(acb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static AIOPool bdrv_em_aio_pool = {
|
|
|
|
.aiocb_size = sizeof(BlockDriverAIOCBSync),
|
|
|
|
.cancel = bdrv_aio_cancel_em,
|
|
|
|
};
|
|
|
|
|
2006-08-07 06:38:06 +04:00
|
|
|
static void bdrv_aio_bh_cb(void *opaque)
|
2006-08-01 20:21:11 +04:00
|
|
|
{
|
2006-08-07 06:38:06 +04:00
|
|
|
BlockDriverAIOCBSync *acb = opaque;
|
2009-04-07 22:43:24 +04:00
|
|
|
|
|
|
|
if (!acb->is_write)
|
|
|
|
qemu_iovec_from_buffer(acb->qiov, acb->bounce, acb->qiov->size);
|
2009-04-07 22:43:28 +04:00
|
|
|
qemu_vfree(acb->bounce);
|
2006-08-07 06:38:06 +04:00
|
|
|
acb->common.cb(acb->common.opaque, acb->ret);
|
2009-06-01 13:07:23 +04:00
|
|
|
qemu_bh_delete(acb->bh);
|
2009-06-23 17:20:36 +04:00
|
|
|
acb->bh = NULL;
|
2006-08-07 06:38:06 +04:00
|
|
|
qemu_aio_release(acb);
|
2006-08-01 20:21:11 +04:00
|
|
|
}
|
2006-06-27 00:08:57 +04:00
|
|
|
|
2009-04-07 22:43:24 +04:00
|
|
|
static BlockDriverAIOCB *bdrv_aio_rw_vector(BlockDriverState *bs,
|
|
|
|
int64_t sector_num,
|
|
|
|
QEMUIOVector *qiov,
|
|
|
|
int nb_sectors,
|
|
|
|
BlockDriverCompletionFunc *cb,
|
|
|
|
void *opaque,
|
|
|
|
int is_write)
|
|
|
|
|
2006-08-01 20:21:11 +04:00
|
|
|
{
|
2006-08-07 06:38:06 +04:00
|
|
|
BlockDriverAIOCBSync *acb;
|
|
|
|
|
2009-05-25 14:37:32 +04:00
|
|
|
acb = qemu_aio_get(&bdrv_em_aio_pool, bs, cb, opaque);
|
2009-04-07 22:43:24 +04:00
|
|
|
acb->is_write = is_write;
|
|
|
|
acb->qiov = qiov;
|
2009-04-23 00:20:00 +04:00
|
|
|
acb->bounce = qemu_blockalign(bs, qiov->size);
|
2009-04-07 22:43:24 +04:00
|
|
|
|
2006-08-07 06:38:06 +04:00
|
|
|
if (!acb->bh)
|
|
|
|
acb->bh = qemu_bh_new(bdrv_aio_bh_cb, acb);
|
2009-04-07 22:43:24 +04:00
|
|
|
|
|
|
|
if (is_write) {
|
|
|
|
qemu_iovec_to_buffer(acb->qiov, acb->bounce);
|
|
|
|
acb->ret = bdrv_write(bs, sector_num, acb->bounce, nb_sectors);
|
|
|
|
} else {
|
|
|
|
acb->ret = bdrv_read(bs, sector_num, acb->bounce, nb_sectors);
|
|
|
|
}
|
|
|
|
|
2006-08-07 06:38:06 +04:00
|
|
|
qemu_bh_schedule(acb->bh);
|
2009-04-07 22:43:24 +04:00
|
|
|
|
2006-08-07 06:38:06 +04:00
|
|
|
return &acb->common;
|
2006-06-27 00:08:57 +04:00
|
|
|
}
|
|
|
|
|
2009-04-07 22:43:24 +04:00
|
|
|
static BlockDriverAIOCB *bdrv_aio_readv_em(BlockDriverState *bs,
|
|
|
|
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
2006-08-07 06:38:06 +04:00
|
|
|
BlockDriverCompletionFunc *cb, void *opaque)
|
2006-06-27 00:08:57 +04:00
|
|
|
{
|
2009-04-07 22:43:24 +04:00
|
|
|
return bdrv_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
|
|
|
|
}
|
2006-08-01 20:21:11 +04:00
|
|
|
|
2009-04-07 22:43:24 +04:00
|
|
|
static BlockDriverAIOCB *bdrv_aio_writev_em(BlockDriverState *bs,
|
|
|
|
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
|
|
|
BlockDriverCompletionFunc *cb, void *opaque)
|
|
|
|
{
|
|
|
|
return bdrv_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
|
2006-06-27 00:08:57 +04:00
|
|
|
}
|
|
|
|
|
2009-09-04 21:01:49 +04:00
|
|
|
static BlockDriverAIOCB *bdrv_aio_flush_em(BlockDriverState *bs,
|
|
|
|
BlockDriverCompletionFunc *cb, void *opaque)
|
|
|
|
{
|
|
|
|
BlockDriverAIOCBSync *acb;
|
|
|
|
|
|
|
|
acb = qemu_aio_get(&bdrv_em_aio_pool, bs, cb, opaque);
|
|
|
|
acb->is_write = 1; /* don't bounce in the completion hadler */
|
|
|
|
acb->qiov = NULL;
|
|
|
|
acb->bounce = NULL;
|
|
|
|
acb->ret = 0;
|
|
|
|
|
|
|
|
if (!acb->bh)
|
|
|
|
acb->bh = qemu_bh_new(bdrv_aio_bh_cb, acb);
|
|
|
|
|
|
|
|
bdrv_flush(bs);
|
|
|
|
qemu_bh_schedule(acb->bh);
|
|
|
|
return &acb->common;
|
|
|
|
}
|
|
|
|
|
2010-05-26 19:51:49 +04:00
|
|
|
static BlockDriverAIOCB *bdrv_aio_noop_em(BlockDriverState *bs,
|
|
|
|
BlockDriverCompletionFunc *cb, void *opaque)
|
|
|
|
{
|
|
|
|
BlockDriverAIOCBSync *acb;
|
|
|
|
|
|
|
|
acb = qemu_aio_get(&bdrv_em_aio_pool, bs, cb, opaque);
|
|
|
|
acb->is_write = 1; /* don't bounce in the completion handler */
|
|
|
|
acb->qiov = NULL;
|
|
|
|
acb->bounce = NULL;
|
|
|
|
acb->ret = 0;
|
|
|
|
|
|
|
|
if (!acb->bh) {
|
|
|
|
acb->bh = qemu_bh_new(bdrv_aio_bh_cb, acb);
|
|
|
|
}
|
|
|
|
|
|
|
|
qemu_bh_schedule(acb->bh);
|
|
|
|
return &acb->common;
|
|
|
|
}
|
|
|
|
|
2006-08-01 20:21:11 +04:00
|
|
|
/**************************************************************/
|
|
|
|
/* sync block device emulation */
|
2004-08-02 01:59:26 +04:00
|
|
|
|
2006-08-01 20:21:11 +04:00
|
|
|
static void bdrv_rw_em_cb(void *opaque, int ret)
|
|
|
|
{
|
|
|
|
*(int *)opaque = ret;
|
2004-08-02 01:59:26 +04:00
|
|
|
}
|
|
|
|
|
2006-08-01 20:21:11 +04:00
|
|
|
#define NOT_DONE 0x7fffffff
|
|
|
|
|
2007-09-17 01:08:06 +04:00
|
|
|
static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num,
|
2006-08-01 20:21:11 +04:00
|
|
|
uint8_t *buf, int nb_sectors)
|
2006-06-04 15:39:07 +04:00
|
|
|
{
|
2006-08-07 06:38:06 +04:00
|
|
|
int async_ret;
|
|
|
|
BlockDriverAIOCB *acb;
|
2009-04-07 22:43:24 +04:00
|
|
|
struct iovec iov;
|
|
|
|
QEMUIOVector qiov;
|
2006-08-01 20:21:11 +04:00
|
|
|
|
2009-10-22 19:54:39 +04:00
|
|
|
async_context_push();
|
|
|
|
|
2006-08-01 20:21:11 +04:00
|
|
|
async_ret = NOT_DONE;
|
2009-04-13 20:31:01 +04:00
|
|
|
iov.iov_base = (void *)buf;
|
2010-05-27 18:20:31 +04:00
|
|
|
iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE;
|
2009-04-07 22:43:24 +04:00
|
|
|
qemu_iovec_init_external(&qiov, &iov, 1);
|
|
|
|
acb = bdrv_aio_readv(bs, sector_num, &qiov, nb_sectors,
|
|
|
|
bdrv_rw_em_cb, &async_ret);
|
2009-10-22 19:54:39 +04:00
|
|
|
if (acb == NULL) {
|
|
|
|
async_ret = -1;
|
|
|
|
goto fail;
|
|
|
|
}
|
2008-09-10 19:45:19 +04:00
|
|
|
|
2006-08-01 20:21:11 +04:00
|
|
|
while (async_ret == NOT_DONE) {
|
|
|
|
qemu_aio_wait();
|
|
|
|
}
|
2008-09-10 19:45:19 +04:00
|
|
|
|
2009-10-22 19:54:39 +04:00
|
|
|
|
|
|
|
fail:
|
|
|
|
async_context_pop();
|
2006-08-01 20:21:11 +04:00
|
|
|
return async_ret;
|
2006-06-04 15:39:07 +04:00
|
|
|
}
|
|
|
|
|
2006-08-01 20:21:11 +04:00
|
|
|
static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num,
|
|
|
|
const uint8_t *buf, int nb_sectors)
|
|
|
|
{
|
2006-08-07 06:38:06 +04:00
|
|
|
int async_ret;
|
|
|
|
BlockDriverAIOCB *acb;
|
2009-04-07 22:43:24 +04:00
|
|
|
struct iovec iov;
|
|
|
|
QEMUIOVector qiov;
|
2006-08-01 20:21:11 +04:00
|
|
|
|
2009-10-22 19:54:39 +04:00
|
|
|
async_context_push();
|
|
|
|
|
2006-08-01 20:21:11 +04:00
|
|
|
async_ret = NOT_DONE;
|
2009-04-07 22:43:24 +04:00
|
|
|
iov.iov_base = (void *)buf;
|
2010-05-27 18:20:31 +04:00
|
|
|
iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE;
|
2009-04-07 22:43:24 +04:00
|
|
|
qemu_iovec_init_external(&qiov, &iov, 1);
|
|
|
|
acb = bdrv_aio_writev(bs, sector_num, &qiov, nb_sectors,
|
|
|
|
bdrv_rw_em_cb, &async_ret);
|
2009-10-22 19:54:39 +04:00
|
|
|
if (acb == NULL) {
|
|
|
|
async_ret = -1;
|
|
|
|
goto fail;
|
|
|
|
}
|
2006-08-01 20:21:11 +04:00
|
|
|
while (async_ret == NOT_DONE) {
|
|
|
|
qemu_aio_wait();
|
|
|
|
}
|
2009-10-22 19:54:39 +04:00
|
|
|
|
|
|
|
fail:
|
|
|
|
async_context_pop();
|
2006-08-01 20:21:11 +04:00
|
|
|
return async_ret;
|
|
|
|
}
|
2004-08-02 01:59:26 +04:00
|
|
|
|
|
|
|
void bdrv_init(void)
|
|
|
|
{
|
2009-05-10 02:03:42 +04:00
|
|
|
module_call_init(MODULE_INIT_BLOCK);
|
2004-08-02 01:59:26 +04:00
|
|
|
}
|
2006-08-07 06:38:06 +04:00
|
|
|
|
2009-10-27 20:41:44 +03:00
|
|
|
void bdrv_init_with_whitelist(void)
|
|
|
|
{
|
|
|
|
use_bdrv_whitelist = 1;
|
|
|
|
bdrv_init();
|
|
|
|
}
|
|
|
|
|
2009-05-25 14:37:32 +04:00
|
|
|
void *qemu_aio_get(AIOPool *pool, BlockDriverState *bs,
|
|
|
|
BlockDriverCompletionFunc *cb, void *opaque)
|
2006-08-07 06:38:06 +04:00
|
|
|
{
|
|
|
|
BlockDriverAIOCB *acb;
|
|
|
|
|
2009-03-20 21:25:59 +03:00
|
|
|
if (pool->free_aiocb) {
|
|
|
|
acb = pool->free_aiocb;
|
|
|
|
pool->free_aiocb = acb->next;
|
2006-08-07 06:38:06 +04:00
|
|
|
} else {
|
2009-03-20 21:25:59 +03:00
|
|
|
acb = qemu_mallocz(pool->aiocb_size);
|
|
|
|
acb->pool = pool;
|
2006-08-07 06:38:06 +04:00
|
|
|
}
|
|
|
|
acb->bs = bs;
|
|
|
|
acb->cb = cb;
|
|
|
|
acb->opaque = opaque;
|
|
|
|
return acb;
|
|
|
|
}
|
|
|
|
|
|
|
|
void qemu_aio_release(void *p)
|
|
|
|
{
|
2009-03-20 21:25:59 +03:00
|
|
|
BlockDriverAIOCB *acb = (BlockDriverAIOCB *)p;
|
|
|
|
AIOPool *pool = acb->pool;
|
|
|
|
acb->next = pool->free_aiocb;
|
|
|
|
pool->free_aiocb = acb;
|
2006-08-07 06:38:06 +04:00
|
|
|
}
|
2006-08-19 15:45:59 +04:00
|
|
|
|
|
|
|
/**************************************************************/
|
|
|
|
/* 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)
|
block: Change bdrv_eject() not to drop the image
bdrv_eject() gets called when a device model opens or closes the tray.
If the block driver implements method bdrv_eject(), that method gets
called. Drivers host_cdrom implements it, and it opens and closes the
physical tray, and nothing else. When a device model opens, then
closes the tray, media changes only if the user actively changes the
physical media while the tray is open. This is matches how physical
hardware behaves.
If the block driver doesn't implement method bdrv_eject(), we do
something quite different: opening the tray severs the connection to
the image by calling bdrv_close(), and closing the tray does nothing.
When the device model opens, then closes the tray, media is gone,
unless the user actively inserts another one while the tray is open,
with a suitable change command in the monitor. This isn't how
physical hardware behaves. Rather inconvenient when programs
"helpfully" eject media to give you a chance to change it. The way
bdrv_eject() behaves here turns that chance into a must, which is not
what these programs or their users expect.
Change the default action not to call bdrv_close(). Instead, note the
tray status in new BlockDriverState member tray_open. Use it in
bdrv_is_inserted().
Arguably, the device models should keep track of tray status
themselves. But this is less invasive.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2010-07-27 16:02:01 +04:00
|
|
|
return !bs->tray_open;
|
2006-08-19 15:45:59 +04:00
|
|
|
ret = drv->bdrv_is_inserted(bs);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return TRUE if the media changed since the last call to this
|
2007-09-17 01:08:06 +04:00
|
|
|
* function. It is currently only used for floppy disks
|
2006-08-19 15:45:59 +04:00
|
|
|
*/
|
|
|
|
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
|
|
|
|
*/
|
2009-05-27 13:06:11 +04:00
|
|
|
int bdrv_eject(BlockDriverState *bs, int eject_flag)
|
2006-08-19 15:45:59 +04:00
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
|
int ret;
|
|
|
|
|
2009-05-27 13:06:11 +04:00
|
|
|
if (bs->locked) {
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2006-08-19 15:45:59 +04:00
|
|
|
if (!drv || !drv->bdrv_eject) {
|
|
|
|
ret = -ENOTSUP;
|
|
|
|
} else {
|
|
|
|
ret = drv->bdrv_eject(bs, eject_flag);
|
|
|
|
}
|
|
|
|
if (ret == -ENOTSUP) {
|
2009-05-27 13:06:11 +04:00
|
|
|
ret = 0;
|
2006-08-19 15:45:59 +04:00
|
|
|
}
|
block: Change bdrv_eject() not to drop the image
bdrv_eject() gets called when a device model opens or closes the tray.
If the block driver implements method bdrv_eject(), that method gets
called. Drivers host_cdrom implements it, and it opens and closes the
physical tray, and nothing else. When a device model opens, then
closes the tray, media changes only if the user actively changes the
physical media while the tray is open. This is matches how physical
hardware behaves.
If the block driver doesn't implement method bdrv_eject(), we do
something quite different: opening the tray severs the connection to
the image by calling bdrv_close(), and closing the tray does nothing.
When the device model opens, then closes the tray, media is gone,
unless the user actively inserts another one while the tray is open,
with a suitable change command in the monitor. This isn't how
physical hardware behaves. Rather inconvenient when programs
"helpfully" eject media to give you a chance to change it. The way
bdrv_eject() behaves here turns that chance into a must, which is not
what these programs or their users expect.
Change the default action not to call bdrv_close(). Instead, note the
tray status in new BlockDriverState member tray_open. Use it in
bdrv_is_inserted().
Arguably, the device models should keep track of tray status
themselves. But this is less invasive.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2010-07-27 16:02:01 +04:00
|
|
|
if (ret >= 0) {
|
|
|
|
bs->tray_open = eject_flag;
|
|
|
|
}
|
2009-05-27 13:06:11 +04:00
|
|
|
|
|
|
|
return ret;
|
2006-08-19 15:45:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2011-03-29 23:04:40 +04:00
|
|
|
trace_bdrv_set_locked(bs, locked);
|
|
|
|
|
2006-08-19 15:45:59 +04:00
|
|
|
bs->locked = locked;
|
|
|
|
if (drv && drv->bdrv_set_locked) {
|
|
|
|
drv->bdrv_set_locked(bs, locked);
|
|
|
|
}
|
|
|
|
}
|
2007-12-24 19:10:43 +03:00
|
|
|
|
|
|
|
/* needed for generic scsi interface */
|
|
|
|
|
|
|
|
int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
|
|
|
{
|
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
|
|
|
|
|
if (drv && drv->bdrv_ioctl)
|
|
|
|
return drv->bdrv_ioctl(bs, req, buf);
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
2009-03-12 22:57:08 +03:00
|
|
|
|
2009-03-28 20:28:41 +03:00
|
|
|
BlockDriverAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
|
|
|
|
unsigned long int req, void *buf,
|
|
|
|
BlockDriverCompletionFunc *cb, void *opaque)
|
2009-03-12 22:57:08 +03:00
|
|
|
{
|
2009-03-28 20:28:41 +03:00
|
|
|
BlockDriver *drv = bs->drv;
|
2009-03-12 22:57:08 +03:00
|
|
|
|
2009-03-28 20:28:41 +03:00
|
|
|
if (drv && drv->bdrv_aio_ioctl)
|
|
|
|
return drv->bdrv_aio_ioctl(bs, req, buf, cb, opaque);
|
|
|
|
return NULL;
|
2009-03-12 22:57:08 +03:00
|
|
|
}
|
2009-04-23 00:20:00 +04:00
|
|
|
|
2009-11-02 16:40:41 +03:00
|
|
|
|
|
|
|
|
2009-04-23 00:20:00 +04:00
|
|
|
void *qemu_blockalign(BlockDriverState *bs, size_t size)
|
|
|
|
{
|
|
|
|
return qemu_memalign((bs && bs->buffer_alignment) ? bs->buffer_alignment : 512, size);
|
|
|
|
}
|
2009-11-02 16:40:41 +03:00
|
|
|
|
|
|
|
void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable)
|
|
|
|
{
|
|
|
|
int64_t bitmap_size;
|
2009-11-30 20:21:19 +03:00
|
|
|
|
2010-01-26 11:31:48 +03:00
|
|
|
bs->dirty_count = 0;
|
2009-11-30 20:21:19 +03:00
|
|
|
if (enable) {
|
2009-11-30 20:21:20 +03:00
|
|
|
if (!bs->dirty_bitmap) {
|
|
|
|
bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) +
|
|
|
|
BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1;
|
|
|
|
bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8;
|
2009-11-30 20:21:19 +03:00
|
|
|
|
2009-11-02 16:40:41 +03:00
|
|
|
bs->dirty_bitmap = qemu_mallocz(bitmap_size);
|
2009-11-30 20:21:19 +03:00
|
|
|
}
|
2009-11-02 16:40:41 +03:00
|
|
|
} else {
|
2009-11-30 20:21:20 +03:00
|
|
|
if (bs->dirty_bitmap) {
|
2009-11-02 16:40:41 +03:00
|
|
|
qemu_free(bs->dirty_bitmap);
|
2009-11-30 20:21:20 +03:00
|
|
|
bs->dirty_bitmap = NULL;
|
2009-11-30 20:21:19 +03:00
|
|
|
}
|
2009-11-02 16:40:41 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int bdrv_get_dirty(BlockDriverState *bs, int64_t sector)
|
|
|
|
{
|
2009-11-30 20:21:19 +03:00
|
|
|
int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK;
|
2009-11-30 20:21:19 +03:00
|
|
|
|
2009-11-30 20:21:20 +03:00
|
|
|
if (bs->dirty_bitmap &&
|
|
|
|
(sector << BDRV_SECTOR_BITS) < bdrv_getlength(bs)) {
|
2010-11-08 22:02:54 +03:00
|
|
|
return !!(bs->dirty_bitmap[chunk / (sizeof(unsigned long) * 8)] &
|
|
|
|
(1UL << (chunk % (sizeof(unsigned long) * 8))));
|
2009-11-02 16:40:41 +03:00
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-30 20:21:19 +03:00
|
|
|
void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
|
|
|
|
int nr_sectors)
|
2009-11-02 16:40:41 +03:00
|
|
|
{
|
|
|
|
set_dirty_bitmap(bs, cur_sector, nr_sectors, 0);
|
|
|
|
}
|
2010-01-26 11:31:48 +03:00
|
|
|
|
|
|
|
int64_t bdrv_get_dirty_count(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
return bs->dirty_count;
|
|
|
|
}
|
2010-12-16 15:52:15 +03:00
|
|
|
|
2011-01-26 17:12:34 +03:00
|
|
|
void bdrv_set_in_use(BlockDriverState *bs, int in_use)
|
|
|
|
{
|
|
|
|
assert(bs->in_use != in_use);
|
|
|
|
bs->in_use = in_use;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bdrv_in_use(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
return bs->in_use;
|
|
|
|
}
|
|
|
|
|
2010-12-16 15:52:15 +03:00
|
|
|
int bdrv_img_create(const char *filename, const char *fmt,
|
|
|
|
const char *base_filename, const char *base_fmt,
|
|
|
|
char *options, uint64_t img_size, int flags)
|
|
|
|
{
|
|
|
|
QEMUOptionParameter *param = NULL, *create_options = NULL;
|
2010-12-16 15:52:17 +03:00
|
|
|
QEMUOptionParameter *backing_fmt, *backing_file;
|
2010-12-16 15:52:15 +03:00
|
|
|
BlockDriverState *bs = NULL;
|
|
|
|
BlockDriver *drv, *proto_drv;
|
2011-01-24 12:32:20 +03:00
|
|
|
BlockDriver *backing_drv = NULL;
|
2010-12-16 15:52:15 +03:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
/* Find driver and parse its options */
|
|
|
|
drv = bdrv_find_format(fmt);
|
|
|
|
if (!drv) {
|
|
|
|
error_report("Unknown file format '%s'", fmt);
|
2010-12-16 15:52:18 +03:00
|
|
|
ret = -EINVAL;
|
2010-12-16 15:52:15 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
proto_drv = bdrv_find_protocol(filename);
|
|
|
|
if (!proto_drv) {
|
|
|
|
error_report("Unknown protocol '%s'", filename);
|
2010-12-16 15:52:18 +03:00
|
|
|
ret = -EINVAL;
|
2010-12-16 15:52:15 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
create_options = append_option_parameters(create_options,
|
|
|
|
drv->create_options);
|
|
|
|
create_options = append_option_parameters(create_options,
|
|
|
|
proto_drv->create_options);
|
|
|
|
|
|
|
|
/* Create parameter list with default values */
|
|
|
|
param = parse_option_parameters("", create_options, param);
|
|
|
|
|
|
|
|
set_option_parameter_int(param, BLOCK_OPT_SIZE, img_size);
|
|
|
|
|
|
|
|
/* Parse -o options */
|
|
|
|
if (options) {
|
|
|
|
param = parse_option_parameters(options, create_options, param);
|
|
|
|
if (param == NULL) {
|
|
|
|
error_report("Invalid options for file format '%s'.", fmt);
|
2010-12-16 15:52:18 +03:00
|
|
|
ret = -EINVAL;
|
2010-12-16 15:52:15 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (base_filename) {
|
|
|
|
if (set_option_parameter(param, BLOCK_OPT_BACKING_FILE,
|
|
|
|
base_filename)) {
|
|
|
|
error_report("Backing file not supported for file format '%s'",
|
|
|
|
fmt);
|
2010-12-16 15:52:18 +03:00
|
|
|
ret = -EINVAL;
|
2010-12-16 15:52:15 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (base_fmt) {
|
|
|
|
if (set_option_parameter(param, BLOCK_OPT_BACKING_FMT, base_fmt)) {
|
|
|
|
error_report("Backing file format not supported for file "
|
|
|
|
"format '%s'", fmt);
|
2010-12-16 15:52:18 +03:00
|
|
|
ret = -EINVAL;
|
2010-12-16 15:52:15 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-16 15:52:17 +03:00
|
|
|
backing_file = get_option_parameter(param, BLOCK_OPT_BACKING_FILE);
|
|
|
|
if (backing_file && backing_file->value.s) {
|
|
|
|
if (!strcmp(filename, backing_file->value.s)) {
|
|
|
|
error_report("Error: Trying to create an image with the "
|
|
|
|
"same filename as the backing file");
|
2010-12-16 15:52:18 +03:00
|
|
|
ret = -EINVAL;
|
2010-12-16 15:52:17 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-16 15:52:15 +03:00
|
|
|
backing_fmt = get_option_parameter(param, BLOCK_OPT_BACKING_FMT);
|
|
|
|
if (backing_fmt && backing_fmt->value.s) {
|
2011-01-24 12:32:20 +03:00
|
|
|
backing_drv = bdrv_find_format(backing_fmt->value.s);
|
|
|
|
if (!backing_drv) {
|
2010-12-16 15:52:15 +03:00
|
|
|
error_report("Unknown backing file format '%s'",
|
|
|
|
backing_fmt->value.s);
|
2010-12-16 15:52:18 +03:00
|
|
|
ret = -EINVAL;
|
2010-12-16 15:52:15 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The size for the image must always be specified, with one exception:
|
|
|
|
// If we are using a backing file, we can obtain the size from there
|
|
|
|
if (get_option_parameter(param, BLOCK_OPT_SIZE)->value.n == -1) {
|
|
|
|
if (backing_file && backing_file->value.s) {
|
|
|
|
uint64_t size;
|
|
|
|
char buf[32];
|
|
|
|
|
|
|
|
bs = bdrv_new("");
|
|
|
|
|
2011-01-24 12:32:20 +03:00
|
|
|
ret = bdrv_open(bs, backing_file->value.s, flags, backing_drv);
|
2010-12-16 15:52:15 +03:00
|
|
|
if (ret < 0) {
|
2011-01-24 12:32:20 +03:00
|
|
|
error_report("Could not open '%s'", backing_file->value.s);
|
2010-12-16 15:52:15 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
bdrv_get_geometry(bs, &size);
|
|
|
|
size *= 512;
|
|
|
|
|
|
|
|
snprintf(buf, sizeof(buf), "%" PRId64, size);
|
|
|
|
set_option_parameter(param, BLOCK_OPT_SIZE, buf);
|
|
|
|
} else {
|
|
|
|
error_report("Image creation needs a size parameter");
|
2010-12-16 15:52:18 +03:00
|
|
|
ret = -EINVAL;
|
2010-12-16 15:52:15 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Formatting '%s', fmt=%s ", filename, fmt);
|
|
|
|
print_option_parameters(param);
|
|
|
|
puts("");
|
|
|
|
|
|
|
|
ret = bdrv_create(drv, filename, param);
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
if (ret == -ENOTSUP) {
|
|
|
|
error_report("Formatting or formatting option not supported for "
|
|
|
|
"file format '%s'", fmt);
|
|
|
|
} else if (ret == -EFBIG) {
|
|
|
|
error_report("The image size is too large for file format '%s'",
|
|
|
|
fmt);
|
|
|
|
} else {
|
|
|
|
error_report("%s: error while creating %s: %s", filename, fmt,
|
|
|
|
strerror(-ret));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
free_option_parameters(create_options);
|
|
|
|
free_option_parameters(param);
|
|
|
|
|
|
|
|
if (bs) {
|
|
|
|
bdrv_delete(bs);
|
|
|
|
}
|
2010-12-16 15:52:18 +03:00
|
|
|
|
|
|
|
return ret;
|
2010-12-16 15:52:15 +03:00
|
|
|
}
|