summaryrefslogtreecommitdiffstats
path: root/block.c
diff options
context:
space:
mode:
Diffstat (limited to 'block.c')
-rw-r--r--block.c343
1 files changed, 210 insertions, 133 deletions
diff --git a/block.c b/block.c
index f1ef4b0109..4745712d22 100644
--- a/block.c
+++ b/block.c
@@ -332,10 +332,21 @@ void bdrv_register(BlockDriver *bdrv)
}
/* create a new block device (by default it is empty) */
-BlockDriverState *bdrv_new(const char *device_name)
+BlockDriverState *bdrv_new(const char *device_name, Error **errp)
{
BlockDriverState *bs;
+ if (bdrv_find(device_name)) {
+ error_setg(errp, "Device with id '%s' already exists",
+ device_name);
+ return NULL;
+ }
+ if (bdrv_find_node(device_name)) {
+ error_setg(errp, "Device with node-name '%s' already exists",
+ device_name);
+ return NULL;
+ }
+
bs = g_malloc0(sizeof(BlockDriverState));
QLIST_INIT(&bs->dirty_bitmaps);
pstrcpy(bs->device_name, sizeof(bs->device_name), device_name);
@@ -767,6 +778,11 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
{
int open_flags = flags | BDRV_O_CACHE_WB;
+ /* The backing file of a temporary snapshot is read-only */
+ if (flags & BDRV_O_SNAPSHOT) {
+ open_flags &= ~BDRV_O_RDWR;
+ }
+
/*
* Clear flags that are internal to the block layer before opening the
* image.
@@ -783,38 +799,36 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
return open_flags;
}
-static int bdrv_assign_node_name(BlockDriverState *bs,
- const char *node_name,
- Error **errp)
+static void bdrv_assign_node_name(BlockDriverState *bs,
+ const char *node_name,
+ Error **errp)
{
if (!node_name) {
- return 0;
+ return;
}
/* empty string node name is invalid */
if (node_name[0] == '\0') {
error_setg(errp, "Empty node name");
- return -EINVAL;
+ return;
}
/* takes care of avoiding namespaces collisions */
if (bdrv_find(node_name)) {
error_setg(errp, "node-name=%s is conflicting with a device id",
node_name);
- return -EINVAL;
+ return;
}
/* takes care of avoiding duplicates node names */
if (bdrv_find_node(node_name)) {
error_setg(errp, "Duplicate node name");
- return -EINVAL;
+ return;
}
/* copy node name into the bs and insert it into the graph list */
pstrcpy(bs->node_name, sizeof(bs->node_name), node_name);
QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list);
-
- return 0;
}
/*
@@ -849,9 +863,10 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name);
node_name = qdict_get_try_str(options, "node-name");
- ret = bdrv_assign_node_name(bs, node_name, errp);
- if (ret < 0) {
- return ret;
+ bdrv_assign_node_name(bs, node_name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return -EINVAL;
}
qdict_del(options, "node-name");
@@ -968,7 +983,7 @@ static int bdrv_file_open(BlockDriverState *bs, const char *filename,
{
BlockDriver *drv;
const char *drvname;
- bool allow_protocol_prefix = false;
+ bool parse_filename = false;
Error *local_err = NULL;
int ret;
@@ -977,7 +992,7 @@ static int bdrv_file_open(BlockDriverState *bs, const char *filename,
filename = qdict_get_try_str(*options, "filename");
} else if (filename && !qdict_haskey(*options, "filename")) {
qdict_put(*options, "filename", qstring_from_str(filename));
- allow_protocol_prefix = true;
+ parse_filename = true;
} else {
error_setg(errp, "Can't specify 'file' and 'filename' options at the "
"same time");
@@ -994,7 +1009,7 @@ static int bdrv_file_open(BlockDriverState *bs, const char *filename,
}
qdict_del(*options, "driver");
} else if (filename) {
- drv = bdrv_find_protocol(filename, allow_protocol_prefix);
+ drv = bdrv_find_protocol(filename, parse_filename);
if (!drv) {
error_setg(errp, "Unknown protocol");
}
@@ -1010,7 +1025,7 @@ static int bdrv_file_open(BlockDriverState *bs, const char *filename,
}
/* Parse the filename and open it */
- if (drv->bdrv_parse_filename && filename) {
+ if (drv->bdrv_parse_filename && parse_filename) {
drv->bdrv_parse_filename(filename, *options, &local_err);
if (local_err) {
error_propagate(errp, local_err);
@@ -1053,14 +1068,14 @@ fail:
*/
int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
{
- char backing_filename[PATH_MAX];
- int back_flags, ret;
+ char *backing_filename = g_malloc0(PATH_MAX);
+ int back_flags, ret = 0;
BlockDriver *back_drv = NULL;
Error *local_err = NULL;
if (bs->backing_hd != NULL) {
QDECREF(options);
- return 0;
+ goto free_exit;
}
/* NULL means an empty set of options */
@@ -1073,10 +1088,9 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
backing_filename[0] = '\0';
} else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) {
QDECREF(options);
- return 0;
+ goto free_exit;
} else {
- bdrv_get_full_backing_filename(bs, backing_filename,
- sizeof(backing_filename));
+ bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX);
}
if (bs->backing_format[0] != '\0') {
@@ -1097,7 +1111,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
error_setg(errp, "Could not open backing file: %s",
error_get_pretty(local_err));
error_free(local_err);
- return ret;
+ goto free_exit;
}
if (bs->backing_hd->file) {
@@ -1108,7 +1122,9 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
/* Recalculate the BlockLimits with the backing file */
bdrv_refresh_limits(bs);
- return 0;
+free_exit:
+ g_free(backing_filename);
+ return ret;
}
/*
@@ -1162,6 +1178,75 @@ done:
return ret;
}
+void bdrv_append_temp_snapshot(BlockDriverState *bs, Error **errp)
+{
+ /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */
+ char *tmp_filename = g_malloc0(PATH_MAX + 1);
+ int64_t total_size;
+ BlockDriver *bdrv_qcow2;
+ QEMUOptionParameter *create_options;
+ QDict *snapshot_options;
+ BlockDriverState *bs_snapshot;
+ Error *local_err;
+ int ret;
+
+ /* if snapshot, we create a temporary backing file and open it
+ instead of opening 'filename' directly */
+
+ /* Get the required size from the image */
+ total_size = bdrv_getlength(bs);
+ if (total_size < 0) {
+ error_setg_errno(errp, -total_size, "Could not get image size");
+ goto out;
+ }
+ total_size &= BDRV_SECTOR_MASK;
+
+ /* Create the temporary image */
+ ret = get_tmp_filename(tmp_filename, PATH_MAX + 1);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not get temporary filename");
+ goto out;
+ }
+
+ bdrv_qcow2 = bdrv_find_format("qcow2");
+ create_options = parse_option_parameters("", bdrv_qcow2->create_options,
+ NULL);
+
+ set_option_parameter_int(create_options, BLOCK_OPT_SIZE, total_size);
+
+ ret = bdrv_create(bdrv_qcow2, tmp_filename, create_options, &local_err);
+ free_option_parameters(create_options);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not create temporary overlay "
+ "'%s': %s", tmp_filename,
+ error_get_pretty(local_err));
+ error_free(local_err);
+ goto out;
+ }
+
+ /* Prepare a new options QDict for the temporary file */
+ snapshot_options = qdict_new();
+ qdict_put(snapshot_options, "file.driver",
+ qstring_from_str("file"));
+ qdict_put(snapshot_options, "file.filename",
+ qstring_from_str(tmp_filename));
+
+ bs_snapshot = bdrv_new("", &error_abort);
+ bs_snapshot->is_temporary = 1;
+
+ ret = bdrv_open(&bs_snapshot, NULL, NULL, snapshot_options,
+ bs->open_flags & ~BDRV_O_SNAPSHOT, bdrv_qcow2, &local_err);
+ if (ret < 0) {
+ error_propagate(errp, local_err);
+ goto out;
+ }
+
+ bdrv_append(bs_snapshot, bs);
+
+out:
+ g_free(tmp_filename);
+}
+
/*
* Opens a disk image (raw, qcow2, vmdk, ...)
*
@@ -1182,8 +1267,6 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
BlockDriver *drv, Error **errp)
{
int ret;
- /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */
- char tmp_filename[PATH_MAX + 1];
BlockDriverState *file = NULL, *bs;
const char *drvname;
Error *local_err = NULL;
@@ -1218,7 +1301,7 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
if (*pbs) {
bs = *pbs;
} else {
- bs = bdrv_new("");
+ bs = bdrv_new("", &error_abort);
}
/* NULL means an empty set of options */
@@ -1243,74 +1326,6 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
}
}
- /* For snapshot=on, create a temporary qcow2 overlay */
- if (flags & BDRV_O_SNAPSHOT) {
- BlockDriverState *bs1;
- int64_t total_size;
- BlockDriver *bdrv_qcow2;
- QEMUOptionParameter *create_options;
- QDict *snapshot_options;
-
- /* if snapshot, we create a temporary backing file and open it
- instead of opening 'filename' directly */
-
- /* Get the required size from the image */
- QINCREF(options);
- bs1 = NULL;
- ret = bdrv_open(&bs1, filename, NULL, options, BDRV_O_NO_BACKING,
- drv, &local_err);
- if (ret < 0) {
- goto fail;
- }
- total_size = bdrv_getlength(bs1) & BDRV_SECTOR_MASK;
-
- bdrv_unref(bs1);
-
- /* Create the temporary image */
- ret = get_tmp_filename(tmp_filename, sizeof(tmp_filename));
- if (ret < 0) {
- error_setg_errno(errp, -ret, "Could not get temporary filename");
- goto fail;
- }
-
- bdrv_qcow2 = bdrv_find_format("qcow2");
- create_options = parse_option_parameters("", bdrv_qcow2->create_options,
- NULL);
-
- set_option_parameter_int(create_options, BLOCK_OPT_SIZE, total_size);
-
- ret = bdrv_create(bdrv_qcow2, tmp_filename, create_options, &local_err);
- free_option_parameters(create_options);
- if (ret < 0) {
- error_setg_errno(errp, -ret, "Could not create temporary overlay "
- "'%s': %s", tmp_filename,
- error_get_pretty(local_err));
- error_free(local_err);
- local_err = NULL;
- goto fail;
- }
-
- /* Prepare a new options QDict for the temporary file, where user
- * options refer to the backing file */
- if (filename) {
- qdict_put(options, "file.filename", qstring_from_str(filename));
- }
- if (drv) {
- qdict_put(options, "driver", qstring_from_str(drv->format_name));
- }
-
- snapshot_options = qdict_new();
- qdict_put(snapshot_options, "backing", options);
- qdict_flatten(snapshot_options);
-
- bs->options = snapshot_options;
- options = qdict_clone_shallow(bs->options);
-
- filename = tmp_filename;
- drv = bdrv_qcow2;
- bs->is_temporary = 1;
- }
-
/* Open image file without format layer */
if (flags & BDRV_O_RDWR) {
flags |= BDRV_O_ALLOW_RDWR;
@@ -1321,7 +1336,7 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
bdrv_open_flags(bs, flags | BDRV_O_UNMAP) |
BDRV_O_PROTOCOL, true, &local_err);
if (ret < 0) {
- goto fail;
+ goto unlink_and_fail;
}
/* Find the right image format driver */
@@ -1372,6 +1387,17 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
}
}
+ /* For snapshot=on, create a temporary qcow2 overlay. bs points to the
+ * temporary snapshot afterwards. */
+ if (flags & BDRV_O_SNAPSHOT) {
+ bdrv_append_temp_snapshot(bs, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto close_and_fail;
+ }
+ }
+
+
done:
/* Check if any unknown options were used */
if (options && (qdict_size(options) != 0)) {
@@ -1388,12 +1414,19 @@ done:
ret = -EINVAL;
goto close_and_fail;
}
- QDECREF(options);
if (!bdrv_key_required(bs)) {
bdrv_dev_change_media_cb(bs, true);
+ } else if (!runstate_check(RUN_STATE_PRELAUNCH)
+ && !runstate_check(RUN_STATE_INMIGRATE)
+ && !runstate_check(RUN_STATE_PAUSED)) { /* HACK */
+ error_setg(errp,
+ "Guest must be stopped for opening of encrypted image");
+ ret = -EBUSY;
+ goto close_and_fail;
}
+ QDECREF(options);
*pbs = bs;
return 0;
@@ -2561,6 +2594,10 @@ static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset,
{
int64_t len;
+ if (size > INT_MAX) {
+ return -EIO;
+ }
+
if (!bdrv_is_inserted(bs))
return -ENOMEDIUM;
@@ -2581,6 +2618,10 @@ static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset,
static int bdrv_check_request(BlockDriverState *bs, int64_t sector_num,
int nb_sectors)
{
+ if (nb_sectors < 0 || nb_sectors > INT_MAX / BDRV_SECTOR_SIZE) {
+ return -EIO;
+ }
+
return bdrv_check_byte_request(bs, sector_num * BDRV_SECTOR_SIZE,
nb_sectors * BDRV_SECTOR_SIZE);
}
@@ -2662,6 +2703,10 @@ static int bdrv_rw_co(BlockDriverState *bs, int64_t sector_num, uint8_t *buf,
.iov_len = nb_sectors * BDRV_SECTOR_SIZE,
};
+ if (nb_sectors < 0 || nb_sectors > INT_MAX / BDRV_SECTOR_SIZE) {
+ return -EINVAL;
+ }
+
qemu_iovec_init_external(&qiov, &iov, 1);
return bdrv_prwv_co(bs, sector_num << BDRV_SECTOR_BITS,
&qiov, is_write, flags);
@@ -2717,10 +2762,16 @@ int bdrv_write_zeroes(BlockDriverState *bs, int64_t sector_num,
*/
int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags)
{
- int64_t target_size = bdrv_getlength(bs) / BDRV_SECTOR_SIZE;
+ int64_t target_size;
int64_t ret, nb_sectors, sector_num = 0;
int n;
+ target_size = bdrv_getlength(bs);
+ if (target_size < 0) {
+ return target_size;
+ }
+ target_size /= BDRV_SECTOR_SIZE;
+
for (;;) {
nb_sectors = target_size - sector_num;
if (nb_sectors <= 0) {
@@ -4055,7 +4106,7 @@ int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag)
int bdrv_debug_resume(BlockDriverState *bs, const char *tag)
{
- while (bs && bs->drv && !bs->drv->bdrv_debug_resume) {
+ while (bs && (!bs->drv || !bs->drv->bdrv_debug_resume)) {
bs = bs->file;
}
@@ -4774,19 +4825,43 @@ flush_parent:
return bdrv_co_flush(bs->file);
}
-void bdrv_invalidate_cache(BlockDriverState *bs)
+void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
{
- if (bs->drv && bs->drv->bdrv_invalidate_cache) {
- bs->drv->bdrv_invalidate_cache(bs);
+ Error *local_err = NULL;
+ int ret;
+
+ if (!bs->drv) {
+ return;
+ }
+
+ if (bs->drv->bdrv_invalidate_cache) {
+ bs->drv->bdrv_invalidate_cache(bs, &local_err);
+ } else if (bs->file) {
+ bdrv_invalidate_cache(bs->file, &local_err);
+ }
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ ret = refresh_total_sectors(bs, bs->total_sectors);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not refresh total sector count");
+ return;
}
}
-void bdrv_invalidate_cache_all(void)
+void bdrv_invalidate_cache_all(Error **errp)
{
BlockDriverState *bs;
+ Error *local_err = NULL;
QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
- bdrv_invalidate_cache(bs);
+ bdrv_invalidate_cache(bs, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
}
}
@@ -5048,7 +5123,8 @@ bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov)
return true;
}
-BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, int granularity)
+BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, int granularity,
+ Error **errp)
{
int64_t bitmap_size;
BdrvDirtyBitmap *bitmap;
@@ -5057,7 +5133,13 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, int granularity)
granularity >>= BDRV_SECTOR_BITS;
assert(granularity);
- bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS);
+ bitmap_size = bdrv_getlength(bs);
+ if (bitmap_size < 0) {
+ error_setg_errno(errp, -bitmap_size, "could not get length of device");
+ errno = -bitmap_size;
+ return NULL;
+ }
+ bitmap_size >>= BDRV_SECTOR_BITS;
bitmap = g_malloc0(sizeof(BdrvDirtyBitmap));
bitmap->bitmap = hbitmap_alloc(bitmap_size, ffs(granularity) - 1);
QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list);
@@ -5390,43 +5472,37 @@ int bdrv_amend_options(BlockDriverState *bs, QEMUOptionParameter *options)
return bs->drv->bdrv_amend_options(bs, options);
}
-/* Used to recurse on single child block filters.
- * Single child block filter will store their child in bs->file.
+/* This function will be called by the bdrv_recurse_is_first_non_filter method
+ * of block filter and by bdrv_is_first_non_filter.
+ * It is used to test if the given bs is the candidate or recurse more in the
+ * node graph.
*/
-bool bdrv_generic_is_first_non_filter(BlockDriverState *bs,
+bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
BlockDriverState *candidate)
{
- if (!bs->drv) {
- return false;
- }
-
- if (!bs->drv->authorizations[BS_IS_A_FILTER]) {
- if (bs == candidate) {
- return true;
- } else {
- return false;
- }
- }
-
- if (!bs->drv->authorizations[BS_FILTER_PASS_DOWN]) {
+ /* return false if basic checks fails */
+ if (!bs || !bs->drv) {
return false;
}
- if (!bs->file) {
- return false;
+ /* the code reached a non block filter driver -> check if the bs is
+ * the same as the candidate. It's the recursion termination condition.
+ */
+ if (!bs->drv->is_filter) {
+ return bs == candidate;
}
+ /* Down this path the driver is a block filter driver */
- return bdrv_recurse_is_first_non_filter(bs->file, candidate);
-}
-
-bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
- BlockDriverState *candidate)
-{
- if (bs->drv && bs->drv->bdrv_recurse_is_first_non_filter) {
+ /* If the block filter recursion method is defined use it to recurse down
+ * the node graph.
+ */
+ if (bs->drv->bdrv_recurse_is_first_non_filter) {
return bs->drv->bdrv_recurse_is_first_non_filter(bs, candidate);
}
- return bdrv_generic_is_first_non_filter(bs, candidate);
+ /* the driver is a block filter but don't allow to recurse -> return false
+ */
+ return false;
}
/* This function checks if the candidate is the first non filter bs down it's
@@ -5441,6 +5517,7 @@ bool bdrv_is_first_non_filter(BlockDriverState *candidate)
QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
bool perm;
+ /* try to recurse in this top level bs */
perm = bdrv_recurse_is_first_non_filter(bs, candidate);
/* candidate is the first non filter */