* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+
#include "qemu/osdep.h"
+
+#define ZLIB_CONST
+#include <zlib.h>
+
#include "block/block_int.h"
+#include "block/qdict.h"
#include "sysemu/block-backend.h"
#include "qemu/module.h"
-#include <zlib.h>
-#include "block/qcow2.h"
+#include "qcow2.h"
#include "qemu/error-report.h"
-#include "qapi/qmp/qerror.h"
-#include "qapi/qmp/qbool.h"
-#include "qapi/qmp/types.h"
-#include "qapi-event.h"
+#include "qapi/error.h"
+#include "qapi/qapi-events-block-core.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qstring.h"
#include "trace.h"
#include "qemu/option_int.h"
#include "qemu/cutils.h"
#include "qemu/bswap.h"
-#include "qapi/opts-visitor.h"
-#include "qapi-visit.h"
-#include "block/crypto.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/qapi-visit-block-core.h"
+#include "crypto.h"
+#include "block/thread-pool.h"
/*
Differences with QCOW:
}
if (!(s->autoclear_features & QCOW2_AUTOCLEAR_BITMAPS)) {
- warn_report("a program lacking bitmap support "
- "modified this file, so all bitmaps are now "
- "considered inconsistent");
+ if (s->qcow_version < 3) {
+ /* Let's be a bit more specific */
+ warn_report("This qcow2 v2 image contains bitmaps, but "
+ "they may have been modified by a program "
+ "without persistent bitmap support; so now "
+ "they must all be considered inconsistent");
+ } else {
+ warn_report("a program lacking bitmap support "
+ "modified this file, so all bitmaps are now "
+ "considered inconsistent");
+ }
error_printf("Some clusters may be leaked, "
"run 'qemu-img check -r' on the image "
"file to fix.");
s->incompatible_features &= ~QCOW2_INCOMPAT_DIRTY;
- ret = bdrv_flush(bs);
+ ret = qcow2_flush_caches(bs);
if (ret < 0) {
return ret;
}
BDRVQcow2State *s = bs->opaque;
if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) {
- int ret = bdrv_flush(bs);
+ int ret = qcow2_flush_caches(bs);
if (ret < 0) {
return ret;
}
return 0;
}
-static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
- BdrvCheckMode fix)
+static int coroutine_fn qcow2_co_check_locked(BlockDriverState *bs,
+ BdrvCheckResult *result,
+ BdrvCheckMode fix)
{
int ret = qcow2_check_refcounts(bs, result, fix);
if (ret < 0) {
return ret;
}
-static int validate_table_offset(BlockDriverState *bs, uint64_t offset,
- uint64_t entries, size_t entry_len)
+static int coroutine_fn qcow2_co_check(BlockDriverState *bs,
+ BdrvCheckResult *result,
+ BdrvCheckMode fix)
{
BDRVQcow2State *s = bs->opaque;
- uint64_t size;
+ int ret;
- /* Use signed INT64_MAX as the maximum even for uint64_t header fields,
- * because values will be passed to qemu functions taking int64_t. */
- if (entries > INT64_MAX / entry_len) {
- return -EINVAL;
- }
+ qemu_co_mutex_lock(&s->lock);
+ ret = qcow2_co_check_locked(bs, result, fix);
+ qemu_co_mutex_unlock(&s->lock);
+ return ret;
+}
- size = entries * entry_len;
+int qcow2_validate_table(BlockDriverState *bs, uint64_t offset,
+ uint64_t entries, size_t entry_len,
+ int64_t max_size_bytes, const char *table_name,
+ Error **errp)
+{
+ BDRVQcow2State *s = bs->opaque;
- if (INT64_MAX - size < offset) {
- return -EINVAL;
+ if (entries > max_size_bytes / entry_len) {
+ error_setg(errp, "%s too large", table_name);
+ return -EFBIG;
}
- /* Tables must be cluster aligned */
- if (offset_into_cluster(s, offset) != 0) {
+ /* Use signed INT64_MAX as the maximum even for uint64_t header fields,
+ * because values will be passed to qemu functions taking int64_t. */
+ if ((INT64_MAX - entries * entry_len < offset) ||
+ (offset_into_cluster(s, offset) != 0)) {
+ error_setg(errp, "%s offset invalid", table_name);
return -EINVAL;
}
.type = QEMU_OPT_BOOL,
.help = "Check for unintended writes into an inactive L2 table",
},
+ {
+ .name = QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY,
+ .type = QEMU_OPT_BOOL,
+ .help = "Check for unintended writes into the bitmap directory",
+ },
{
.name = QCOW2_OPT_CACHE_SIZE,
.type = QEMU_OPT_SIZE,
.type = QEMU_OPT_SIZE,
.help = "Maximum L2 table cache size",
},
+ {
+ .name = QCOW2_OPT_L2_CACHE_ENTRY_SIZE,
+ .type = QEMU_OPT_SIZE,
+ .help = "Size of each entry in the L2 cache",
+ },
{
.name = QCOW2_OPT_REFCOUNT_CACHE_SIZE,
.type = QEMU_OPT_SIZE,
};
static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = {
- [QCOW2_OL_MAIN_HEADER_BITNR] = QCOW2_OPT_OVERLAP_MAIN_HEADER,
- [QCOW2_OL_ACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L1,
- [QCOW2_OL_ACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L2,
- [QCOW2_OL_REFCOUNT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE,
- [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK,
- [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE,
- [QCOW2_OL_INACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L1,
- [QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2,
+ [QCOW2_OL_MAIN_HEADER_BITNR] = QCOW2_OPT_OVERLAP_MAIN_HEADER,
+ [QCOW2_OL_ACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L1,
+ [QCOW2_OL_ACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L2,
+ [QCOW2_OL_REFCOUNT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE,
+ [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK,
+ [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE,
+ [QCOW2_OL_INACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L1,
+ [QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2,
+ [QCOW2_OL_BITMAP_DIRECTORY_BITNR] = QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY,
};
static void cache_clean_timer_cb(void *opaque)
{
BlockDriverState *bs = opaque;
BDRVQcow2State *s = bs->opaque;
- qcow2_cache_clean_unused(bs, s->l2_table_cache);
- qcow2_cache_clean_unused(bs, s->refcount_block_cache);
+ qcow2_cache_clean_unused(s->l2_table_cache);
+ qcow2_cache_clean_unused(s->refcount_block_cache);
timer_mod(s->cache_clean_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
(int64_t) s->cache_clean_interval * 1000);
}
static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
uint64_t *l2_cache_size,
+ uint64_t *l2_cache_entry_size,
uint64_t *refcount_cache_size, Error **errp)
{
BDRVQcow2State *s = bs->opaque;
uint64_t combined_cache_size;
bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set;
+ int min_refcount_cache = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size;
combined_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_CACHE_SIZE);
l2_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_L2_CACHE_SIZE);
*refcount_cache_size = qemu_opt_get_size(opts,
QCOW2_OPT_REFCOUNT_CACHE_SIZE, 0);
+ *l2_cache_entry_size = qemu_opt_get_size(
+ opts, QCOW2_OPT_L2_CACHE_ENTRY_SIZE, s->cluster_size);
+
if (combined_cache_size_set) {
if (l2_cache_size_set && refcount_cache_size_set) {
error_setg(errp, QCOW2_OPT_CACHE_SIZE ", " QCOW2_OPT_L2_CACHE_SIZE
} else if (refcount_cache_size_set) {
*l2_cache_size = combined_cache_size - *refcount_cache_size;
} else {
- *refcount_cache_size = combined_cache_size
- / (DEFAULT_L2_REFCOUNT_SIZE_RATIO + 1);
- *l2_cache_size = combined_cache_size - *refcount_cache_size;
+ uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE;
+ uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8);
+
+ /* Assign as much memory as possible to the L2 cache, and
+ * use the remainder for the refcount cache */
+ if (combined_cache_size >= max_l2_cache + min_refcount_cache) {
+ *l2_cache_size = max_l2_cache;
+ *refcount_cache_size = combined_cache_size - *l2_cache_size;
+ } else {
+ *refcount_cache_size =
+ MIN(combined_cache_size, min_refcount_cache);
+ *l2_cache_size = combined_cache_size - *refcount_cache_size;
+ }
}
} else {
- if (!l2_cache_size_set && !refcount_cache_size_set) {
+ if (!l2_cache_size_set) {
*l2_cache_size = MAX(DEFAULT_L2_CACHE_BYTE_SIZE,
(uint64_t)DEFAULT_L2_CACHE_CLUSTERS
* s->cluster_size);
- *refcount_cache_size = *l2_cache_size
- / DEFAULT_L2_REFCOUNT_SIZE_RATIO;
- } else if (!l2_cache_size_set) {
- *l2_cache_size = *refcount_cache_size
- * DEFAULT_L2_REFCOUNT_SIZE_RATIO;
- } else if (!refcount_cache_size_set) {
- *refcount_cache_size = *l2_cache_size
- / DEFAULT_L2_REFCOUNT_SIZE_RATIO;
+ }
+ if (!refcount_cache_size_set) {
+ *refcount_cache_size = min_refcount_cache;
}
}
+
+ if (*l2_cache_entry_size < (1 << MIN_CLUSTER_BITS) ||
+ *l2_cache_entry_size > s->cluster_size ||
+ !is_power_of_2(*l2_cache_entry_size)) {
+ error_setg(errp, "L2 cache entry size must be a power of two "
+ "between %d and the cluster size (%d)",
+ 1 << MIN_CLUSTER_BITS, s->cluster_size);
+ return;
+ }
}
typedef struct Qcow2ReopenState {
Qcow2Cache *l2_table_cache;
Qcow2Cache *refcount_block_cache;
+ int l2_slice_size; /* Number of entries in a slice of the L2 table */
bool use_lazy_refcounts;
int overlap_check;
bool discard_passthrough[QCOW2_DISCARD_MAX];
QemuOpts *opts = NULL;
const char *opt_overlap_check, *opt_overlap_check_template;
int overlap_check_template = 0;
- uint64_t l2_cache_size, refcount_cache_size;
+ uint64_t l2_cache_size, l2_cache_entry_size, refcount_cache_size;
int i;
const char *encryptfmt;
QDict *encryptopts = NULL;
}
/* get L2 table/refcount block cache size from command line options */
- read_cache_sizes(bs, opts, &l2_cache_size, &refcount_cache_size,
- &local_err);
+ read_cache_sizes(bs, opts, &l2_cache_size, &l2_cache_entry_size,
+ &refcount_cache_size, &local_err);
if (local_err) {
error_propagate(errp, local_err);
ret = -EINVAL;
goto fail;
}
- l2_cache_size /= s->cluster_size;
+ l2_cache_size /= l2_cache_entry_size;
if (l2_cache_size < MIN_L2_CACHE_SIZE) {
l2_cache_size = MIN_L2_CACHE_SIZE;
}
}
}
- r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size);
- r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size);
+ r->l2_slice_size = l2_cache_entry_size / sizeof(uint64_t);
+ r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size,
+ l2_cache_entry_size);
+ r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size,
+ s->cluster_size);
if (r->l2_table_cache == NULL || r->refcount_block_cache == NULL) {
error_setg(errp, "Could not allocate metadata caches");
ret = -ENOMEM;
ret = -EINVAL;
goto fail;
}
- qdict_del(encryptopts, "format");
- r->crypto_opts = block_crypto_open_opts_init(
- Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp);
+ qdict_put_str(encryptopts, "format", "qcow");
+ r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp);
break;
case QCOW_CRYPT_LUKS:
ret = -EINVAL;
goto fail;
}
- qdict_del(encryptopts, "format");
- r->crypto_opts = block_crypto_open_opts_init(
- Q_CRYPTO_BLOCK_FORMAT_LUKS, encryptopts, errp);
+ qdict_put_str(encryptopts, "format", "luks");
+ r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp);
break;
default:
ret = 0;
fail:
- QDECREF(encryptopts);
+ qobject_unref(encryptopts);
qemu_opts_del(opts);
opts = NULL;
return ret;
int i;
if (s->l2_table_cache) {
- qcow2_cache_destroy(bs, s->l2_table_cache);
+ qcow2_cache_destroy(s->l2_table_cache);
}
if (s->refcount_block_cache) {
- qcow2_cache_destroy(bs, s->refcount_block_cache);
+ qcow2_cache_destroy(s->refcount_block_cache);
}
s->l2_table_cache = r->l2_table_cache;
s->refcount_block_cache = r->refcount_block_cache;
+ s->l2_slice_size = r->l2_slice_size;
s->overlap_check = r->overlap_check;
s->use_lazy_refcounts = r->use_lazy_refcounts;
Qcow2ReopenState *r)
{
if (r->l2_table_cache) {
- qcow2_cache_destroy(bs, r->l2_table_cache);
+ qcow2_cache_destroy(r->l2_table_cache);
}
if (r->refcount_block_cache) {
- qcow2_cache_destroy(bs, r->refcount_block_cache);
+ qcow2_cache_destroy(r->refcount_block_cache);
}
qapi_free_QCryptoBlockOpenOptions(r->crypto_opts);
}
return ret;
}
-static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
- Error **errp)
+/* Called with s->lock held. */
+static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
+ int flags, Error **errp)
{
BDRVQcow2State *s = bs->opaque;
unsigned int len, i;
uint64_t ext_end;
uint64_t l1_vm_state_index;
bool update_header = false;
+ bool header_updated = false;
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
if (ret < 0) {
s->refcount_table_size =
header.refcount_table_clusters << (s->cluster_bits - 3);
- if (header.refcount_table_clusters > qcow2_max_refcount_clusters(s)) {
- error_setg(errp, "Reference count table too large");
- ret = -EINVAL;
- goto fail;
- }
-
if (header.refcount_table_clusters == 0 && !(flags & BDRV_O_CHECK)) {
error_setg(errp, "Image does not contain a reference count table");
ret = -EINVAL;
goto fail;
}
- ret = validate_table_offset(bs, s->refcount_table_offset,
- s->refcount_table_size, sizeof(uint64_t));
+ ret = qcow2_validate_table(bs, s->refcount_table_offset,
+ header.refcount_table_clusters,
+ s->cluster_size, QCOW_MAX_REFTABLE_SIZE,
+ "Reference count table", errp);
if (ret < 0) {
- error_setg(errp, "Invalid reference count table offset");
goto fail;
}
- /* Snapshot table offset/length */
- if (header.nb_snapshots > QCOW_MAX_SNAPSHOTS) {
- error_setg(errp, "Too many snapshots");
- ret = -EINVAL;
- goto fail;
- }
-
- ret = validate_table_offset(bs, header.snapshots_offset,
- header.nb_snapshots,
- sizeof(QCowSnapshotHeader));
+ /* The total size in bytes of the snapshot table is checked in
+ * qcow2_read_snapshots() because the size of each snapshot is
+ * variable and we don't know it yet.
+ * Here we only check the offset and number of snapshots. */
+ ret = qcow2_validate_table(bs, header.snapshots_offset,
+ header.nb_snapshots,
+ sizeof(QCowSnapshotHeader),
+ sizeof(QCowSnapshotHeader) * QCOW_MAX_SNAPSHOTS,
+ "Snapshot table", errp);
if (ret < 0) {
- error_setg(errp, "Invalid snapshot table offset");
goto fail;
}
/* read the level 1 table */
- if (header.l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) {
- error_setg(errp, "Active L1 table too large");
- ret = -EFBIG;
+ ret = qcow2_validate_table(bs, header.l1_table_offset,
+ header.l1_size, sizeof(uint64_t),
+ QCOW_MAX_L1_SIZE, "Active L1 table", errp);
+ if (ret < 0) {
goto fail;
}
s->l1_size = header.l1_size;
+ s->l1_table_offset = header.l1_table_offset;
l1_vm_state_index = size_to_l1(s, header.size);
if (l1_vm_state_index > INT_MAX) {
goto fail;
}
- ret = validate_table_offset(bs, header.l1_table_offset,
- header.l1_size, sizeof(uint64_t));
- if (ret < 0) {
- error_setg(errp, "Invalid L1 table offset");
- goto fail;
- }
- s->l1_table_offset = header.l1_table_offset;
-
-
if (s->l1_size > 0) {
s->l1_table = qemu_try_blockalign(bs->file->bs,
- align_offset(s->l1_size * sizeof(uint64_t), 512));
+ ROUND_UP(s->l1_size * sizeof(uint64_t), 512));
if (s->l1_table == NULL) {
error_setg(errp, "Could not allocate L1 table");
ret = -ENOMEM;
s->autoclear_features &= QCOW2_AUTOCLEAR_MASK;
}
- if (qcow2_load_autoloading_dirty_bitmaps(bs, &local_err)) {
- update_header = false;
+ if (s->dirty_bitmaps_loaded) {
+ /* It's some kind of reopen. There are no known cases where we need to
+ * reload bitmaps in such a situation, so it's safer to skip them.
+ *
+ * Moreover, if we have some readonly bitmaps and we are reopening for
+ * rw we should reopen bitmaps correspondingly.
+ */
+ if (bdrv_has_readonly_bitmaps(bs) &&
+ !bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE))
+ {
+ qcow2_reopen_bitmaps_rw_hint(bs, &header_updated, &local_err);
+ }
+ } else {
+ header_updated = qcow2_load_dirty_bitmaps(bs, &local_err);
+ s->dirty_bitmaps_loaded = true;
}
+ update_header = update_header && !header_updated;
if (local_err != NULL) {
error_propagate(errp, local_err);
ret = -EINVAL;
}
}
- /* Initialise locks */
- qemu_co_mutex_init(&s->lock);
- bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP;
+ bs->supported_zero_flags = header.version >= 3 ? BDRV_REQ_MAY_UNMAP : 0;
/* Repair image if dirty */
if (!(flags & (BDRV_O_CHECK | BDRV_O_INACTIVE)) && !bs->read_only &&
(s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) {
BdrvCheckResult result = {0};
- ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS | BDRV_FIX_LEAKS);
+ ret = qcow2_co_check_locked(bs, &result,
+ BDRV_FIX_ERRORS | BDRV_FIX_LEAKS);
if (ret < 0 || result.check_errors) {
if (ret >= 0) {
ret = -EIO;
qcow2_check_refcounts(bs, &result, 0);
}
#endif
+
+ qemu_co_queue_init(&s->compress_wait_queue);
+
return ret;
fail:
s->l1_table = NULL;
cache_clean_timer_del(bs);
if (s->l2_table_cache) {
- qcow2_cache_destroy(bs, s->l2_table_cache);
+ qcow2_cache_destroy(s->l2_table_cache);
}
if (s->refcount_block_cache) {
- qcow2_cache_destroy(bs, s->refcount_block_cache);
+ qcow2_cache_destroy(s->refcount_block_cache);
}
qcrypto_block_free(s->crypto);
qapi_free_QCryptoBlockOpenOptions(s->crypto_opts);
return ret;
}
+typedef struct QCow2OpenCo {
+ BlockDriverState *bs;
+ QDict *options;
+ int flags;
+ Error **errp;
+ int ret;
+} QCow2OpenCo;
+
+static void coroutine_fn qcow2_open_entry(void *opaque)
+{
+ QCow2OpenCo *qoc = opaque;
+ BDRVQcow2State *s = qoc->bs->opaque;
+
+ qemu_co_mutex_lock(&s->lock);
+ qoc->ret = qcow2_do_open(qoc->bs, qoc->options, qoc->flags, qoc->errp);
+ qemu_co_mutex_unlock(&s->lock);
+}
+
static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
+ BDRVQcow2State *s = bs->opaque;
+ QCow2OpenCo qoc = {
+ .bs = bs,
+ .options = options,
+ .flags = flags,
+ .errp = errp,
+ .ret = -EINPROGRESS
+ };
+
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
false, errp);
if (!bs->file) {
return -EINVAL;
}
- return qcow2_do_open(bs, options, flags, errp);
+ /* Initialise locks */
+ qemu_co_mutex_init(&s->lock);
+
+ if (qemu_in_coroutine()) {
+ /* From bdrv_co_create. */
+ qcow2_open_entry(&qoc);
+ } else {
+ qemu_coroutine_enter(qemu_coroutine_create(qcow2_open_entry, &qoc));
+ BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
+ }
+ return qoc.ret;
}
static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp)
}
}
-static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file)
+static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs,
+ bool want_zero,
+ int64_t offset, int64_t count,
+ int64_t *pnum, int64_t *map,
+ BlockDriverState **file)
{
BDRVQcow2State *s = bs->opaque;
uint64_t cluster_offset;
int index_in_cluster, ret;
unsigned int bytes;
- int64_t status = 0;
+ int status = 0;
- bytes = MIN(INT_MAX, nb_sectors * BDRV_SECTOR_SIZE);
+ bytes = MIN(INT_MAX, count);
qemu_co_mutex_lock(&s->lock);
- ret = qcow2_get_cluster_offset(bs, sector_num << BDRV_SECTOR_BITS, &bytes,
- &cluster_offset);
+ ret = qcow2_get_cluster_offset(bs, offset, &bytes, &cluster_offset);
qemu_co_mutex_unlock(&s->lock);
if (ret < 0) {
return ret;
}
- *pnum = bytes >> BDRV_SECTOR_BITS;
+ *pnum = bytes;
if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED &&
!s->crypto) {
- index_in_cluster = sector_num & (s->cluster_sectors - 1);
- cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
+ index_in_cluster = offset & (s->cluster_size - 1);
+ *map = cluster_offset | index_in_cluster;
*file = bs->file->bs;
- status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset;
+ status |= BDRV_BLOCK_OFFSET_VALID;
}
if (ret == QCOW2_CLUSTER_ZERO_PLAIN || ret == QCOW2_CLUSTER_ZERO_ALLOC) {
status |= BDRV_BLOCK_ZERO;
return status;
}
+static coroutine_fn int qcow2_handle_l2meta(BlockDriverState *bs,
+ QCowL2Meta **pl2meta,
+ bool link_l2)
+{
+ int ret = 0;
+ QCowL2Meta *l2meta = *pl2meta;
+
+ while (l2meta != NULL) {
+ QCowL2Meta *next;
+
+ if (link_l2) {
+ ret = qcow2_alloc_cluster_link_l2(bs, l2meta);
+ if (ret) {
+ goto out;
+ }
+ } else {
+ qcow2_alloc_cluster_abort(bs, l2meta);
+ }
+
+ /* Take the request off the list of running requests */
+ if (l2meta->nb_clusters != 0) {
+ QLIST_REMOVE(l2meta, next_in_flight);
+ }
+
+ qemu_co_queue_restart_all(&l2meta->dependent_requests);
+
+ next = l2meta->next;
+ g_free(l2meta);
+ l2meta = next;
+ }
+out:
+ *pl2meta = l2meta;
+ return ret;
+}
+
static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov,
int flags)
}
}
- while (l2meta != NULL) {
- QCowL2Meta *next;
-
- ret = qcow2_alloc_cluster_link_l2(bs, l2meta);
- if (ret < 0) {
- goto fail;
- }
-
- /* Take the request off the list of running requests */
- if (l2meta->nb_clusters != 0) {
- QLIST_REMOVE(l2meta, next_in_flight);
- }
-
- qemu_co_queue_restart_all(&l2meta->dependent_requests);
-
- next = l2meta->next;
- g_free(l2meta);
- l2meta = next;
+ ret = qcow2_handle_l2meta(bs, &l2meta, true);
+ if (ret) {
+ goto fail;
}
bytes -= cur_bytes;
ret = 0;
fail:
- while (l2meta != NULL) {
- QCowL2Meta *next;
-
- if (l2meta->nb_clusters != 0) {
- QLIST_REMOVE(l2meta, next_in_flight);
- }
- qemu_co_queue_restart_all(&l2meta->dependent_requests);
-
- next = l2meta->next;
- g_free(l2meta);
- l2meta = next;
- }
+ qcow2_handle_l2meta(bs, &l2meta, false);
qemu_co_mutex_unlock(&s->lock);
}
cache_clean_timer_del(bs);
- qcow2_cache_destroy(bs, s->l2_table_cache);
- qcow2_cache_destroy(bs, s->refcount_block_cache);
+ qcow2_cache_destroy(s->l2_table_cache);
+ qcow2_cache_destroy(s->refcount_block_cache);
qcrypto_block_free(s->crypto);
s->crypto = NULL;
qcow2_free_snapshots(bs);
}
-static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
+static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs,
+ Error **errp)
{
BDRVQcow2State *s = bs->opaque;
int flags = s->flags;
options = qdict_clone_shallow(bs->options);
flags &= ~BDRV_O_INACTIVE;
+ qemu_co_mutex_lock(&s->lock);
ret = qcow2_do_open(bs, options, flags, &local_err);
- QDECREF(options);
+ qemu_co_mutex_unlock(&s->lock);
+ qobject_unref(options);
if (local_err) {
error_propagate(errp, local_err);
error_prepend(errp, "Could not reopen qcow2 layer: ");
}
}
-static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encryptfmt,
- QemuOpts *opts, Error **errp)
+static int qcow2_set_up_encryption(BlockDriverState *bs,
+ QCryptoBlockCreateOptions *cryptoopts,
+ Error **errp)
{
BDRVQcow2State *s = bs->opaque;
- QCryptoBlockCreateOptions *cryptoopts = NULL;
QCryptoBlock *crypto = NULL;
- int ret = -EINVAL;
- QDict *options, *encryptopts;
- int fmt;
-
- options = qemu_opts_to_qdict(opts, NULL);
- qdict_extract_subqdict(options, &encryptopts, "encrypt.");
- QDECREF(options);
+ int fmt, ret;
- fmt = qcow2_crypt_method_from_format(encryptfmt);
-
- switch (fmt) {
- case QCOW_CRYPT_LUKS:
- cryptoopts = block_crypto_create_opts_init(
- Q_CRYPTO_BLOCK_FORMAT_LUKS, encryptopts, errp);
+ switch (cryptoopts->format) {
+ case Q_CRYPTO_BLOCK_FORMAT_LUKS:
+ fmt = QCOW_CRYPT_LUKS;
break;
- case QCOW_CRYPT_AES:
- cryptoopts = block_crypto_create_opts_init(
- Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp);
+ case Q_CRYPTO_BLOCK_FORMAT_QCOW:
+ fmt = QCOW_CRYPT_AES;
break;
default:
- error_setg(errp, "Unknown encryption format '%s'", encryptfmt);
- break;
- }
- if (!cryptoopts) {
- ret = -EINVAL;
- goto out;
+ error_setg(errp, "Crypto format not supported in qcow2");
+ return -EINVAL;
}
+
s->crypt_method_header = fmt;
crypto = qcrypto_block_create(cryptoopts, "encrypt.",
qcow2_crypto_hdr_write_func,
bs, errp);
if (!crypto) {
- ret = -EINVAL;
- goto out;
+ return -EINVAL;
}
ret = qcow2_update_header(bs);
goto out;
}
+ ret = 0;
out:
- QDECREF(encryptopts);
qcrypto_block_free(crypto);
- qapi_free_QCryptoBlockCreateOptions(cryptoopts);
return ret;
}
-
-typedef struct PreallocCo {
- BlockDriverState *bs;
- uint64_t offset;
- uint64_t new_length;
-
- int ret;
-} PreallocCo;
-
/**
* Preallocates metadata structures for data clusters between @offset (in the
* guest disk) and @new_length (which is thus generally the new guest disk
*
* Returns: 0 on success, -errno on failure.
*/
-static void coroutine_fn preallocate_co(void *opaque)
+static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset,
+ uint64_t new_length)
{
- PreallocCo *params = opaque;
- BlockDriverState *bs = params->bs;
- uint64_t offset = params->offset;
- uint64_t new_length = params->new_length;
- BDRVQcow2State *s = bs->opaque;
uint64_t bytes;
uint64_t host_offset = 0;
unsigned int cur_bytes;
int ret;
QCowL2Meta *meta;
- qemu_co_mutex_lock(&s->lock);
-
assert(offset <= new_length);
bytes = new_length - offset;
ret = qcow2_alloc_cluster_offset(bs, offset, &cur_bytes,
&host_offset, &meta);
if (ret < 0) {
- goto done;
+ return ret;
}
while (meta) {
if (ret < 0) {
qcow2_free_any_clusters(bs, meta->alloc_offset,
meta->nb_clusters, QCOW2_DISCARD_NEVER);
- goto done;
+ return ret;
}
/* There are no dependent requests, but we need to remove our
ret = bdrv_pwrite(bs->file, (host_offset + cur_bytes) - 1,
&data, 1);
if (ret < 0) {
- goto done;
+ return ret;
}
}
- ret = 0;
-
-done:
- qemu_co_mutex_unlock(&s->lock);
- params->ret = ret;
-}
-
-static int preallocate(BlockDriverState *bs,
- uint64_t offset, uint64_t new_length)
-{
- PreallocCo params = {
- .bs = bs,
- .offset = offset,
- .new_length = new_length,
- .ret = -EINPROGRESS,
- };
-
- if (qemu_in_coroutine()) {
- preallocate_co(¶ms);
- } else {
- Coroutine *co = qemu_coroutine_create(preallocate_co, ¶ms);
- bdrv_coroutine_enter(bs, co);
- BDRV_POLL_WHILE(bs, params.ret == -EINPROGRESS);
- }
- return params.ret;
+ return 0;
}
/* qcow2_refcount_metadata_size:
{
int64_t meta_size = 0;
uint64_t nl1e, nl2e;
- int64_t aligned_total_size = align_offset(total_size, cluster_size);
+ int64_t aligned_total_size = ROUND_UP(total_size, cluster_size);
/* header: 1 cluster */
meta_size += cluster_size;
/* total size of L2 tables */
nl2e = aligned_total_size / cluster_size;
- nl2e = align_offset(nl2e, cluster_size / sizeof(uint64_t));
+ nl2e = ROUND_UP(nl2e, cluster_size / sizeof(uint64_t));
meta_size += nl2e * sizeof(uint64_t);
/* total size of L1 tables */
nl1e = nl2e * sizeof(uint64_t) / cluster_size;
- nl1e = align_offset(nl1e, cluster_size / sizeof(uint64_t));
+ nl1e = ROUND_UP(nl1e, cluster_size / sizeof(uint64_t));
meta_size += nl1e * sizeof(uint64_t);
/* total size of refcount table and blocks */
return meta_size + aligned_total_size;
}
-static size_t qcow2_opt_get_cluster_size_del(QemuOpts *opts, Error **errp)
+static bool validate_cluster_size(size_t cluster_size, Error **errp)
{
- size_t cluster_size;
- int cluster_bits;
-
- cluster_size = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
- DEFAULT_CLUSTER_SIZE);
- cluster_bits = ctz32(cluster_size);
+ int cluster_bits = ctz32(cluster_size);
if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS ||
(1 << cluster_bits) != cluster_size)
{
error_setg(errp, "Cluster size must be a power of two between %d and "
"%dk", 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10));
+ return false;
+ }
+ return true;
+}
+
+static size_t qcow2_opt_get_cluster_size_del(QemuOpts *opts, Error **errp)
+{
+ size_t cluster_size;
+
+ cluster_size = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
+ DEFAULT_CLUSTER_SIZE);
+ if (!validate_cluster_size(cluster_size, errp)) {
return 0;
}
return cluster_size;
return refcount_bits;
}
-static int qcow2_create2(const char *filename, int64_t total_size,
- const char *backing_file, const char *backing_format,
- int flags, size_t cluster_size, PreallocMode prealloc,
- QemuOpts *opts, int version, int refcount_order,
- const char *encryptfmt, Error **errp)
+static int coroutine_fn
+qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
{
+ BlockdevCreateOptionsQcow2 *qcow2_opts;
QDict *options;
/*
* 2 GB for 64k clusters, and we don't want to have a 2 GB initial file
* size for any qcow2 image.
*/
- BlockBackend *blk;
+ BlockBackend *blk = NULL;
+ BlockDriverState *bs = NULL;
QCowHeader *header;
+ size_t cluster_size;
+ int version;
+ int refcount_order;
uint64_t* refcount_table;
Error *local_err = NULL;
int ret;
- if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) {
- int64_t prealloc_size =
- qcow2_calc_prealloc_size(total_size, cluster_size, refcount_order);
- qemu_opt_set_number(opts, BLOCK_OPT_SIZE, prealloc_size, &error_abort);
- qemu_opt_set(opts, BLOCK_OPT_PREALLOC, PreallocMode_str(prealloc),
- &error_abort);
- }
-
- ret = bdrv_create_file(filename, opts, &local_err);
- if (ret < 0) {
- error_propagate(errp, local_err);
- return ret;
- }
+ assert(create_options->driver == BLOCKDEV_DRIVER_QCOW2);
+ qcow2_opts = &create_options->u.qcow2;
- blk = blk_new_open(filename, NULL, NULL,
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
- &local_err);
- if (blk == NULL) {
- error_propagate(errp, local_err);
+ bs = bdrv_open_blockdev_ref(qcow2_opts->file, errp);
+ if (bs == NULL) {
return -EIO;
}
- blk_set_allow_write_beyond_eof(blk, true);
-
- /* Write the header */
- QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header));
- header = g_malloc0(cluster_size);
- *header = (QCowHeader) {
- .magic = cpu_to_be32(QCOW_MAGIC),
- .version = cpu_to_be32(version),
- .cluster_bits = cpu_to_be32(ctz32(cluster_size)),
- .size = cpu_to_be64(0),
- .l1_table_offset = cpu_to_be64(0),
- .l1_size = cpu_to_be32(0),
- .refcount_table_offset = cpu_to_be64(cluster_size),
- .refcount_table_clusters = cpu_to_be32(1),
- .refcount_order = cpu_to_be32(refcount_order),
- .header_length = cpu_to_be32(sizeof(*header)),
- };
+ /* Validate options and set default values */
+ if (!QEMU_IS_ALIGNED(qcow2_opts->size, BDRV_SECTOR_SIZE)) {
+ error_setg(errp, "Image size must be a multiple of 512 bytes");
+ ret = -EINVAL;
+ goto out;
+ }
- /* We'll update this to correct value later */
- header->crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
+ if (qcow2_opts->has_version) {
+ switch (qcow2_opts->version) {
+ case BLOCKDEV_QCOW2_VERSION_V2:
+ version = 2;
+ break;
+ case BLOCKDEV_QCOW2_VERSION_V3:
+ version = 3;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ } else {
+ version = 3;
+ }
- if (flags & BLOCK_FLAG_LAZY_REFCOUNTS) {
- header->compatible_features |=
- cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
+ if (qcow2_opts->has_cluster_size) {
+ cluster_size = qcow2_opts->cluster_size;
+ } else {
+ cluster_size = DEFAULT_CLUSTER_SIZE;
}
- ret = blk_pwrite(blk, 0, header, cluster_size, 0);
- g_free(header);
- if (ret < 0) {
- error_setg_errno(errp, -ret, "Could not write qcow2 header");
+ if (!validate_cluster_size(cluster_size, errp)) {
+ ret = -EINVAL;
goto out;
}
- /* Write a refcount table with one refcount block */
- refcount_table = g_malloc0(2 * cluster_size);
- refcount_table[0] = cpu_to_be64(2 * cluster_size);
- ret = blk_pwrite(blk, cluster_size, refcount_table, 2 * cluster_size, 0);
- g_free(refcount_table);
+ if (!qcow2_opts->has_preallocation) {
+ qcow2_opts->preallocation = PREALLOC_MODE_OFF;
+ }
+ if (qcow2_opts->has_backing_file &&
+ qcow2_opts->preallocation != PREALLOC_MODE_OFF)
+ {
+ error_setg(errp, "Backing file and preallocation cannot be used at "
+ "the same time");
+ ret = -EINVAL;
+ goto out;
+ }
+ if (qcow2_opts->has_backing_fmt && !qcow2_opts->has_backing_file) {
+ error_setg(errp, "Backing format cannot be used without backing file");
+ ret = -EINVAL;
+ goto out;
+ }
- if (ret < 0) {
- error_setg_errno(errp, -ret, "Could not write refcount table");
+ if (!qcow2_opts->has_lazy_refcounts) {
+ qcow2_opts->lazy_refcounts = false;
+ }
+ if (version < 3 && qcow2_opts->lazy_refcounts) {
+ error_setg(errp, "Lazy refcounts only supported with compatibility "
+ "level 1.1 and above (use version=v3 or greater)");
+ ret = -EINVAL;
goto out;
}
- blk_unref(blk);
- blk = NULL;
+ if (!qcow2_opts->has_refcount_bits) {
+ qcow2_opts->refcount_bits = 16;
+ }
+ if (qcow2_opts->refcount_bits > 64 ||
+ !is_power_of_2(qcow2_opts->refcount_bits))
+ {
+ error_setg(errp, "Refcount width must be a power of two and may not "
+ "exceed 64 bits");
+ ret = -EINVAL;
+ goto out;
+ }
+ if (version < 3 && qcow2_opts->refcount_bits != 16) {
+ error_setg(errp, "Different refcount widths than 16 bits require "
+ "compatibility level 1.1 or above (use version=v3 or "
+ "greater)");
+ ret = -EINVAL;
+ goto out;
+ }
+ refcount_order = ctz32(qcow2_opts->refcount_bits);
+
+
+ /* Create BlockBackend to write to the image */
+ blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
+ ret = blk_insert_bs(blk, bs, errp);
+ if (ret < 0) {
+ goto out;
+ }
+ blk_set_allow_write_beyond_eof(blk, true);
+
+ /* Clear the protocol layer and preallocate it if necessary */
+ ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
+ if (ret < 0) {
+ goto out;
+ }
+
+ if (qcow2_opts->preallocation == PREALLOC_MODE_FULL ||
+ qcow2_opts->preallocation == PREALLOC_MODE_FALLOC)
+ {
+ int64_t prealloc_size =
+ qcow2_calc_prealloc_size(qcow2_opts->size, cluster_size,
+ refcount_order);
+
+ ret = blk_truncate(blk, prealloc_size, qcow2_opts->preallocation, errp);
+ if (ret < 0) {
+ goto out;
+ }
+ }
+
+ /* Write the header */
+ QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header));
+ header = g_malloc0(cluster_size);
+ *header = (QCowHeader) {
+ .magic = cpu_to_be32(QCOW_MAGIC),
+ .version = cpu_to_be32(version),
+ .cluster_bits = cpu_to_be32(ctz32(cluster_size)),
+ .size = cpu_to_be64(0),
+ .l1_table_offset = cpu_to_be64(0),
+ .l1_size = cpu_to_be32(0),
+ .refcount_table_offset = cpu_to_be64(cluster_size),
+ .refcount_table_clusters = cpu_to_be32(1),
+ .refcount_order = cpu_to_be32(refcount_order),
+ .header_length = cpu_to_be32(sizeof(*header)),
+ };
+
+ /* We'll update this to correct value later */
+ header->crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
+
+ if (qcow2_opts->lazy_refcounts) {
+ header->compatible_features |=
+ cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
+ }
+
+ ret = blk_pwrite(blk, 0, header, cluster_size, 0);
+ g_free(header);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not write qcow2 header");
+ goto out;
+ }
+
+ /* Write a refcount table with one refcount block */
+ refcount_table = g_malloc0(2 * cluster_size);
+ refcount_table[0] = cpu_to_be64(2 * cluster_size);
+ ret = blk_pwrite(blk, cluster_size, refcount_table, 2 * cluster_size, 0);
+ g_free(refcount_table);
+
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not write refcount table");
+ goto out;
+ }
+
+ blk_unref(blk);
+ blk = NULL;
/*
* And now open the image and make it consistent first (i.e. increase the
*/
options = qdict_new();
qdict_put_str(options, "driver", "qcow2");
- blk = blk_new_open(filename, NULL, options,
+ qdict_put_str(options, "file", bs->node_name);
+ blk = blk_new_open(NULL, NULL, options,
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH,
&local_err);
if (blk == NULL) {
}
/* Okay, now that we have a valid image, let's give it the right size */
- ret = blk_truncate(blk, total_size, PREALLOC_MODE_OFF, errp);
+ ret = blk_truncate(blk, qcow2_opts->size, PREALLOC_MODE_OFF, errp);
if (ret < 0) {
error_prepend(errp, "Could not resize image: ");
goto out;
}
/* Want a backing file? There you go.*/
- if (backing_file) {
- ret = bdrv_change_backing_file(blk_bs(blk), backing_file, backing_format);
+ if (qcow2_opts->has_backing_file) {
+ const char *backing_format = NULL;
+
+ if (qcow2_opts->has_backing_fmt) {
+ backing_format = BlockdevDriver_str(qcow2_opts->backing_fmt);
+ }
+
+ ret = bdrv_change_backing_file(blk_bs(blk), qcow2_opts->backing_file,
+ backing_format);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not assign backing file '%s' "
- "with format '%s'", backing_file, backing_format);
+ "with format '%s'", qcow2_opts->backing_file,
+ backing_format);
goto out;
}
}
/* Want encryption? There you go. */
- if (encryptfmt) {
- ret = qcow2_set_up_encryption(blk_bs(blk), encryptfmt, opts, errp);
+ if (qcow2_opts->has_encrypt) {
+ ret = qcow2_set_up_encryption(blk_bs(blk), qcow2_opts->encrypt, errp);
if (ret < 0) {
goto out;
}
}
/* And if we're supposed to preallocate metadata, do that now */
- if (prealloc != PREALLOC_MODE_OFF) {
- ret = preallocate(blk_bs(blk), 0, total_size);
+ if (qcow2_opts->preallocation != PREALLOC_MODE_OFF) {
+ BDRVQcow2State *s = blk_bs(blk)->opaque;
+ qemu_co_mutex_lock(&s->lock);
+ ret = preallocate_co(blk_bs(blk), 0, qcow2_opts->size);
+ qemu_co_mutex_unlock(&s->lock);
+
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not preallocate metadata");
goto out;
*/
options = qdict_new();
qdict_put_str(options, "driver", "qcow2");
- blk = blk_new_open(filename, NULL, options,
+ qdict_put_str(options, "file", bs->node_name);
+ blk = blk_new_open(NULL, NULL, options,
BDRV_O_RDWR | BDRV_O_NO_BACKING | BDRV_O_NO_IO,
&local_err);
if (blk == NULL) {
ret = 0;
out:
- if (blk) {
- blk_unref(blk);
- }
+ blk_unref(blk);
+ bdrv_unref(bs);
return ret;
}
-static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp)
+static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opts,
+ Error **errp)
{
- char *backing_file = NULL;
- char *backing_fmt = NULL;
- char *buf = NULL;
- uint64_t size = 0;
- int flags = 0;
- size_t cluster_size = DEFAULT_CLUSTER_SIZE;
- PreallocMode prealloc;
- int version;
- uint64_t refcount_bits;
- int refcount_order;
- char *encryptfmt = NULL;
+ BlockdevCreateOptions *create_options = NULL;
+ QDict *qdict;
+ Visitor *v;
+ BlockDriverState *bs = NULL;
Error *local_err = NULL;
+ const char *val;
int ret;
- /* Read out options */
- size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
- backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
- backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
- encryptfmt = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT);
- if (encryptfmt) {
- if (qemu_opt_get(opts, BLOCK_OPT_ENCRYPT)) {
- error_setg(errp, "Options " BLOCK_OPT_ENCRYPT " and "
- BLOCK_OPT_ENCRYPT_FORMAT " are mutually exclusive");
- ret = -EINVAL;
- goto finish;
- }
- } else if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) {
- encryptfmt = g_strdup("aes");
- }
- cluster_size = qcow2_opt_get_cluster_size_del(opts, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ /* Only the keyval visitor supports the dotted syntax needed for
+ * encryption, so go through a QDict before getting a QAPI type. Ignore
+ * options meant for the protocol layer so that the visitor doesn't
+ * complain. */
+ qdict = qemu_opts_to_qdict_filtered(opts, NULL, bdrv_qcow2.create_opts,
+ true);
+
+ /* Handle encryption options */
+ val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT);
+ if (val && !strcmp(val, "on")) {
+ qdict_put_str(qdict, BLOCK_OPT_ENCRYPT, "qcow");
+ } else if (val && !strcmp(val, "off")) {
+ qdict_del(qdict, BLOCK_OPT_ENCRYPT);
+ }
+
+ val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT);
+ if (val && !strcmp(val, "aes")) {
+ qdict_put_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT, "qcow");
+ }
+
+ /* Convert compat=0.10/1.1 into compat=v2/v3, to be renamed into
+ * version=v2/v3 below. */
+ val = qdict_get_try_str(qdict, BLOCK_OPT_COMPAT_LEVEL);
+ if (val && !strcmp(val, "0.10")) {
+ qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v2");
+ } else if (val && !strcmp(val, "1.1")) {
+ qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v3");
+ }
+
+ /* Change legacy command line options into QMP ones */
+ static const QDictRenames opt_renames[] = {
+ { BLOCK_OPT_BACKING_FILE, "backing-file" },
+ { BLOCK_OPT_BACKING_FMT, "backing-fmt" },
+ { BLOCK_OPT_CLUSTER_SIZE, "cluster-size" },
+ { BLOCK_OPT_LAZY_REFCOUNTS, "lazy-refcounts" },
+ { BLOCK_OPT_REFCOUNT_BITS, "refcount-bits" },
+ { BLOCK_OPT_ENCRYPT, BLOCK_OPT_ENCRYPT_FORMAT },
+ { BLOCK_OPT_COMPAT_LEVEL, "version" },
+ { NULL, NULL },
+ };
+
+ if (!qdict_rename_keys(qdict, opt_renames, errp)) {
ret = -EINVAL;
goto finish;
}
- buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
- prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
- PREALLOC_MODE_OFF, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- ret = -EINVAL;
+
+ /* Create and open the file (protocol layer) */
+ ret = bdrv_create_file(filename, opts, errp);
+ if (ret < 0) {
goto finish;
}
- version = qcow2_opt_get_version_del(opts, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- ret = -EINVAL;
+ bs = bdrv_open(filename, NULL, NULL,
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
+ if (bs == NULL) {
+ ret = -EIO;
goto finish;
}
- if (qemu_opt_get_bool_del(opts, BLOCK_OPT_LAZY_REFCOUNTS, false)) {
- flags |= BLOCK_FLAG_LAZY_REFCOUNTS;
- }
+ /* Set 'driver' and 'node' options */
+ qdict_put_str(qdict, "driver", "qcow2");
+ qdict_put_str(qdict, "file", bs->node_name);
- if (backing_file && prealloc != PREALLOC_MODE_OFF) {
- error_setg(errp, "Backing file and preallocation cannot be used at "
- "the same time");
+ /* Now get the QAPI type BlockdevCreateOptions */
+ v = qobject_input_visitor_new_flat_confused(qdict, errp);
+ if (!v) {
ret = -EINVAL;
goto finish;
}
- if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) {
- error_setg(errp, "Lazy refcounts only supported with compatibility "
- "level 1.1 and above (use compat=1.1 or greater)");
- ret = -EINVAL;
- goto finish;
- }
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
+ visit_free(v);
- refcount_bits = qcow2_opt_get_refcount_bits_del(opts, version, &local_err);
if (local_err) {
error_propagate(errp, local_err);
ret = -EINVAL;
goto finish;
}
- refcount_order = ctz32(refcount_bits);
+ /* Silently round up size */
+ create_options->u.qcow2.size = ROUND_UP(create_options->u.qcow2.size,
+ BDRV_SECTOR_SIZE);
- ret = qcow2_create2(filename, size, backing_file, backing_fmt, flags,
- cluster_size, prealloc, opts, version, refcount_order,
- encryptfmt, &local_err);
- error_propagate(errp, local_err);
+ /* Create the qcow2 image (format layer) */
+ ret = qcow2_co_create(create_options, errp);
+ if (ret < 0) {
+ goto finish;
+ }
+ ret = 0;
finish:
- g_free(backing_file);
- g_free(backing_fmt);
- g_free(encryptfmt);
- g_free(buf);
+ qobject_unref(qdict);
+ bdrv_unref(bs);
+ qapi_free_BlockdevCreateOptions(create_options);
return ret;
}
return ret;
}
-static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
- PreallocMode prealloc, Error **errp)
+static int coroutine_fn
+qcow2_co_copy_range_from(BlockDriverState *bs,
+ BdrvChild *src, uint64_t src_offset,
+ BdrvChild *dst, uint64_t dst_offset,
+ uint64_t bytes, BdrvRequestFlags flags)
+{
+ BDRVQcow2State *s = bs->opaque;
+ int ret;
+ unsigned int cur_bytes; /* number of bytes in current iteration */
+ BdrvChild *child = NULL;
+ BdrvRequestFlags cur_flags;
+
+ assert(!bs->encrypted);
+ qemu_co_mutex_lock(&s->lock);
+
+ while (bytes != 0) {
+ uint64_t copy_offset = 0;
+ /* prepare next request */
+ cur_bytes = MIN(bytes, INT_MAX);
+ cur_flags = flags;
+
+ ret = qcow2_get_cluster_offset(bs, src_offset, &cur_bytes, ©_offset);
+ if (ret < 0) {
+ goto out;
+ }
+
+ switch (ret) {
+ case QCOW2_CLUSTER_UNALLOCATED:
+ if (bs->backing && bs->backing->bs) {
+ int64_t backing_length = bdrv_getlength(bs->backing->bs);
+ if (src_offset >= backing_length) {
+ cur_flags |= BDRV_REQ_ZERO_WRITE;
+ } else {
+ child = bs->backing;
+ cur_bytes = MIN(cur_bytes, backing_length - src_offset);
+ copy_offset = src_offset;
+ }
+ } else {
+ cur_flags |= BDRV_REQ_ZERO_WRITE;
+ }
+ break;
+
+ case QCOW2_CLUSTER_ZERO_PLAIN:
+ case QCOW2_CLUSTER_ZERO_ALLOC:
+ cur_flags |= BDRV_REQ_ZERO_WRITE;
+ break;
+
+ case QCOW2_CLUSTER_COMPRESSED:
+ ret = -ENOTSUP;
+ goto out;
+
+ case QCOW2_CLUSTER_NORMAL:
+ child = bs->file;
+ copy_offset += offset_into_cluster(s, src_offset);
+ if ((copy_offset & 511) != 0) {
+ ret = -EIO;
+ goto out;
+ }
+ break;
+
+ default:
+ abort();
+ }
+ qemu_co_mutex_unlock(&s->lock);
+ ret = bdrv_co_copy_range_from(child,
+ copy_offset,
+ dst, dst_offset,
+ cur_bytes, cur_flags);
+ qemu_co_mutex_lock(&s->lock);
+ if (ret < 0) {
+ goto out;
+ }
+
+ bytes -= cur_bytes;
+ src_offset += cur_bytes;
+ dst_offset += cur_bytes;
+ }
+ ret = 0;
+
+out:
+ qemu_co_mutex_unlock(&s->lock);
+ return ret;
+}
+
+static int coroutine_fn
+qcow2_co_copy_range_to(BlockDriverState *bs,
+ BdrvChild *src, uint64_t src_offset,
+ BdrvChild *dst, uint64_t dst_offset,
+ uint64_t bytes, BdrvRequestFlags flags)
+{
+ BDRVQcow2State *s = bs->opaque;
+ int offset_in_cluster;
+ int ret;
+ unsigned int cur_bytes; /* number of sectors in current iteration */
+ uint64_t cluster_offset;
+ QCowL2Meta *l2meta = NULL;
+
+ assert(!bs->encrypted);
+ s->cluster_cache_offset = -1; /* disable compressed cache */
+
+ qemu_co_mutex_lock(&s->lock);
+
+ while (bytes != 0) {
+
+ l2meta = NULL;
+
+ offset_in_cluster = offset_into_cluster(s, dst_offset);
+ cur_bytes = MIN(bytes, INT_MAX);
+
+ /* TODO:
+ * If src->bs == dst->bs, we could simply copy by incrementing
+ * the refcnt, without copying user data.
+ * Or if src->bs == dst->bs->backing->bs, we could copy by discarding. */
+ ret = qcow2_alloc_cluster_offset(bs, dst_offset, &cur_bytes,
+ &cluster_offset, &l2meta);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ assert((cluster_offset & 511) == 0);
+
+ ret = qcow2_pre_write_overlap_check(bs, 0,
+ cluster_offset + offset_in_cluster, cur_bytes);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ qemu_co_mutex_unlock(&s->lock);
+ ret = bdrv_co_copy_range_to(src, src_offset,
+ bs->file,
+ cluster_offset + offset_in_cluster,
+ cur_bytes, flags);
+ qemu_co_mutex_lock(&s->lock);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ ret = qcow2_handle_l2meta(bs, &l2meta, true);
+ if (ret) {
+ goto fail;
+ }
+
+ bytes -= cur_bytes;
+ src_offset += cur_bytes;
+ dst_offset += cur_bytes;
+ }
+ ret = 0;
+
+fail:
+ qcow2_handle_l2meta(bs, &l2meta, false);
+
+ qemu_co_mutex_unlock(&s->lock);
+
+ trace_qcow2_writev_done_req(qemu_coroutine_self(), ret);
+
+ return ret;
+}
+
+static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
+ PreallocMode prealloc, Error **errp)
{
BDRVQcow2State *s = bs->opaque;
uint64_t old_length;
return -EINVAL;
}
+ qemu_co_mutex_lock(&s->lock);
+
/* cannot proceed if image has snapshots */
if (s->nb_snapshots) {
error_setg(errp, "Can't resize an image which has snapshots");
- return -ENOTSUP;
+ ret = -ENOTSUP;
+ goto fail;
}
/* cannot proceed if image has bitmaps */
if (s->nb_bitmaps) {
/* TODO: resize bitmaps in the image */
error_setg(errp, "Can't resize an image which has bitmaps");
- return -ENOTSUP;
+ ret = -ENOTSUP;
+ goto fail;
}
old_length = bs->total_sectors * 512;
if (prealloc != PREALLOC_MODE_OFF) {
error_setg(errp,
"Preallocation can't be used for shrinking an image");
- return -EINVAL;
+ ret = -EINVAL;
+ goto fail;
}
ret = qcow2_cluster_discard(bs, ROUND_UP(offset, s->cluster_size),
QCOW2_DISCARD_ALWAYS, true);
if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to discard cropped clusters");
- return ret;
+ goto fail;
}
ret = qcow2_shrink_l1_table(bs, new_l1_size);
if (ret < 0) {
error_setg_errno(errp, -ret,
"Failed to reduce the number of L2 tables");
- return ret;
+ goto fail;
}
ret = qcow2_shrink_reftable(bs);
if (ret < 0) {
error_setg_errno(errp, -ret,
"Failed to discard unused refblocks");
- return ret;
+ goto fail;
}
old_file_size = bdrv_getlength(bs->file->bs);
if (old_file_size < 0) {
error_setg_errno(errp, -old_file_size,
"Failed to inquire current file length");
- return old_file_size;
+ ret = old_file_size;
+ goto fail;
}
last_cluster = qcow2_get_last_cluster(bs, old_file_size);
if (last_cluster < 0) {
error_setg_errno(errp, -last_cluster,
"Failed to find the last cluster");
- return last_cluster;
+ ret = last_cluster;
+ goto fail;
}
if ((last_cluster + 1) * s->cluster_size < old_file_size) {
Error *local_err = NULL;
- bdrv_truncate(bs->file, (last_cluster + 1) * s->cluster_size,
- PREALLOC_MODE_OFF, &local_err);
+ bdrv_co_truncate(bs->file, (last_cluster + 1) * s->cluster_size,
+ PREALLOC_MODE_OFF, &local_err);
if (local_err) {
warn_reportf_err(local_err,
"Failed to truncate the tail of the image: ");
ret = qcow2_grow_l1_table(bs, new_l1_size, true);
if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to grow the L1 table");
- return ret;
+ goto fail;
}
}
break;
case PREALLOC_MODE_METADATA:
- ret = preallocate(bs, old_length, offset);
+ ret = preallocate_co(bs, old_length, offset);
if (ret < 0) {
error_setg_errno(errp, -ret, "Preallocation failed");
- return ret;
+ goto fail;
}
break;
if (old_file_size < 0) {
error_setg_errno(errp, -old_file_size,
"Failed to inquire current file length");
- return old_file_size;
+ ret = old_file_size;
+ goto fail;
}
old_file_size = ROUND_UP(old_file_size, s->cluster_size);
if (allocation_start < 0) {
error_setg_errno(errp, -allocation_start,
"Failed to resize refcount structures");
- return allocation_start;
+ ret = allocation_start;
+ goto fail;
}
clusters_allocated = qcow2_alloc_clusters_at(bs, allocation_start,
if (clusters_allocated < 0) {
error_setg_errno(errp, -clusters_allocated,
"Failed to allocate data clusters");
- return -clusters_allocated;
+ ret = clusters_allocated;
+ goto fail;
}
assert(clusters_allocated == nb_new_data_clusters);
/* Allocate the data area */
new_file_size = allocation_start +
nb_new_data_clusters * s->cluster_size;
- ret = bdrv_truncate(bs->file, new_file_size, prealloc, errp);
+ ret = bdrv_co_truncate(bs->file, new_file_size, prealloc, errp);
if (ret < 0) {
error_prepend(errp, "Failed to resize underlying file: ");
qcow2_free_clusters(bs, allocation_start,
nb_new_data_clusters * s->cluster_size,
QCOW2_DISCARD_OTHER);
- return ret;
+ goto fail;
}
/* Create the necessary L2 entries */
host_offset = allocation_start;
guest_offset = old_length;
while (nb_new_data_clusters) {
- int64_t guest_cluster = guest_offset >> s->cluster_bits;
- int64_t nb_clusters = MIN(nb_new_data_clusters,
- s->l2_size - guest_cluster % s->l2_size);
+ int64_t nb_clusters = MIN(
+ nb_new_data_clusters,
+ s->l2_slice_size - offset_to_l2_slice_index(s, guest_offset));
QCowL2Meta allocation = {
.offset = guest_offset,
.alloc_offset = host_offset,
qcow2_free_clusters(bs, host_offset,
nb_new_data_clusters * s->cluster_size,
QCOW2_DISCARD_OTHER);
- return ret;
+ goto fail;
}
guest_offset += nb_clusters * s->cluster_size;
if (prealloc != PREALLOC_MODE_OFF) {
/* Flush metadata before actually changing the image size */
- ret = bdrv_flush(bs);
+ ret = qcow2_write_caches(bs);
if (ret < 0) {
error_setg_errno(errp, -ret,
"Failed to flush the preallocated area to disk");
- return ret;
+ goto fail;
}
}
&offset, sizeof(uint64_t));
if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to update the image size");
- return ret;
+ goto fail;
}
s->l1_vm_state_index = new_l1_size;
+ ret = 0;
+fail:
+ qemu_co_mutex_unlock(&s->lock);
+ return ret;
+}
+
+/*
+ * qcow2_compress()
+ *
+ * @dest - destination buffer, at least of @size-1 bytes
+ * @src - source buffer, @size bytes
+ *
+ * Returns: compressed size on success
+ * -1 if compression is inefficient
+ * -2 on any other error
+ */
+static ssize_t qcow2_compress(void *dest, const void *src, size_t size)
+{
+ ssize_t ret;
+ z_stream strm;
+
+ /* best compression, small window, no zlib header */
+ memset(&strm, 0, sizeof(strm));
+ ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
+ -12, 9, Z_DEFAULT_STRATEGY);
+ if (ret != 0) {
+ return -2;
+ }
+
+ /* strm.next_in is not const in old zlib versions, such as those used on
+ * OpenBSD/NetBSD, so cast the const away */
+ strm.avail_in = size;
+ strm.next_in = (void *) src;
+ strm.avail_out = size - 1;
+ strm.next_out = dest;
+
+ ret = deflate(&strm, Z_FINISH);
+ if (ret == Z_STREAM_END) {
+ ret = size - 1 - strm.avail_out;
+ } else {
+ ret = (ret == Z_OK ? -1 : -2);
+ }
+
+ deflateEnd(&strm);
+
+ return ret;
+}
+
+#define MAX_COMPRESS_THREADS 4
+
+typedef struct Qcow2CompressData {
+ void *dest;
+ const void *src;
+ size_t size;
+ ssize_t ret;
+} Qcow2CompressData;
+
+static int qcow2_compress_pool_func(void *opaque)
+{
+ Qcow2CompressData *data = opaque;
+
+ data->ret = qcow2_compress(data->dest, data->src, data->size);
+
return 0;
}
+static void qcow2_compress_complete(void *opaque, int ret)
+{
+ qemu_coroutine_enter(opaque);
+}
+
+/* See qcow2_compress definition for parameters description */
+static ssize_t qcow2_co_compress(BlockDriverState *bs,
+ void *dest, const void *src, size_t size)
+{
+ BDRVQcow2State *s = bs->opaque;
+ BlockAIOCB *acb;
+ ThreadPool *pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
+ Qcow2CompressData arg = {
+ .dest = dest,
+ .src = src,
+ .size = size,
+ };
+
+ while (s->nb_compress_threads >= MAX_COMPRESS_THREADS) {
+ qemu_co_queue_wait(&s->compress_wait_queue, NULL);
+ }
+
+ s->nb_compress_threads++;
+ acb = thread_pool_submit_aio(pool, qcow2_compress_pool_func, &arg,
+ qcow2_compress_complete,
+ qemu_coroutine_self());
+
+ if (!acb) {
+ s->nb_compress_threads--;
+ return -EINVAL;
+ }
+ qemu_coroutine_yield();
+ s->nb_compress_threads--;
+ qemu_co_queue_next(&s->compress_wait_queue);
+
+ return arg.ret;
+}
+
/* XXX: put compressed sectors first, then all the cluster aligned
tables to avoid losing bytes in alignment */
static coroutine_fn int
BDRVQcow2State *s = bs->opaque;
QEMUIOVector hd_qiov;
struct iovec iov;
- z_stream strm;
- int ret, out_len;
+ int ret;
+ size_t out_len;
uint8_t *buf, *out_buf;
int64_t cluster_offset;
if (cluster_offset < 0) {
return cluster_offset;
}
- return bdrv_truncate(bs->file, cluster_offset, PREALLOC_MODE_OFF, NULL);
+ return bdrv_co_truncate(bs->file, cluster_offset, PREALLOC_MODE_OFF,
+ NULL);
}
if (offset_into_cluster(s, offset)) {
out_buf = g_malloc(s->cluster_size);
- /* best compression, small window, no zlib header */
- memset(&strm, 0, sizeof(strm));
- ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION,
- Z_DEFLATED, -12,
- 9, Z_DEFAULT_STRATEGY);
- if (ret != 0) {
- ret = -EINVAL;
- goto fail;
- }
-
- strm.avail_in = s->cluster_size;
- strm.next_in = (uint8_t *)buf;
- strm.avail_out = s->cluster_size;
- strm.next_out = out_buf;
-
- ret = deflate(&strm, Z_FINISH);
- if (ret != Z_STREAM_END && ret != Z_OK) {
- deflateEnd(&strm);
+ out_len = qcow2_co_compress(bs, out_buf, buf, s->cluster_size);
+ if (out_len == -2) {
ret = -EINVAL;
goto fail;
- }
- out_len = strm.next_out - out_buf;
-
- deflateEnd(&strm);
-
- if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
+ } else if (out_len == -1) {
/* could not compress: write normal cluster */
ret = qcow2_co_pwritev(bs, offset, bytes, qiov, 0);
if (ret < 0) {
int ret;
qemu_co_mutex_lock(&s->lock);
- ret = qcow2_cache_write(bs, s->l2_table_cache);
- if (ret < 0) {
- qemu_co_mutex_unlock(&s->lock);
- return ret;
- }
-
- if (qcow2_need_accurate_refcounts(s)) {
- ret = qcow2_cache_write(bs, s->refcount_block_cache);
- if (ret < 0) {
- qemu_co_mutex_unlock(&s->lock);
- return ret;
- }
- }
+ ret = qcow2_write_caches(bs);
qemu_co_mutex_unlock(&s->lock);
- return 0;
+ return ret;
}
static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
has_backing_file = !!optstr;
g_free(optstr);
- virtual_size = align_offset(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- cluster_size);
+ virtual_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
+ virtual_size = ROUND_UP(virtual_size, cluster_size);
/* Check that virtual disk size is valid */
l2_tables = DIV_ROUND_UP(virtual_size / cluster_size,
goto err;
}
- virtual_size = align_offset(ssize, cluster_size);
+ virtual_size = ROUND_UP(ssize, cluster_size);
if (has_backing_file) {
/* We don't how much of the backing chain is shared by the input
{
BDRVQcow2State *s = bs->opaque;
bdi->unallocated_blocks_are_zero = true;
- bdi->can_write_zeroes_with_unmap = (s->qcow_version >= 3);
bdi->cluster_size = s->cluster_size;
bdi->vm_state_offset = qcow2_vm_state_offset(s);
return 0;
switch (encrypt_info->format) {
case Q_CRYPTO_BLOCK_FORMAT_QCOW:
qencrypt->format = BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_AES;
- qencrypt->u.aes = encrypt_info->u.qcow;
break;
case Q_CRYPTO_BLOCK_FORMAT_LUKS:
qencrypt->format = BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_LUKS;
* have to be removed.
*/
static int qcow2_downgrade(BlockDriverState *bs, int target_version,
- BlockDriverAmendStatusCB *status_cb, void *cb_opaque)
+ BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
+ Error **errp)
{
BDRVQcow2State *s = bs->opaque;
int current_version = s->qcow_version;
int ret;
- if (target_version == current_version) {
- return 0;
- } else if (target_version > current_version) {
- return -EINVAL;
- } else if (target_version != 2) {
- return -EINVAL;
- }
+ /* This is qcow2_downgrade(), not qcow2_upgrade() */
+ assert(target_version < current_version);
+
+ /* There are no other versions (now) that you can downgrade to */
+ assert(target_version == 2);
if (s->refcount_order != 4) {
- error_report("compat=0.10 requires refcount_bits=16");
+ error_setg(errp, "compat=0.10 requires refcount_bits=16");
return -ENOTSUP;
}
if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
ret = qcow2_mark_clean(bs);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Failed to make the image clean");
return ret;
}
}
* best thing to do anyway */
if (s->incompatible_features) {
+ error_setg(errp, "Cannot downgrade an image with incompatible features "
+ "%#" PRIx64 " set", s->incompatible_features);
return -ENOTSUP;
}
ret = qcow2_expand_zero_clusters(bs, status_cb, cb_opaque);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Failed to turn zero into data clusters");
return ret;
}
ret = qcow2_update_header(bs);
if (ret < 0) {
s->qcow_version = current_version;
+ error_setg_errno(errp, -ret, "Failed to update the image header");
return ret;
}
return 0;
static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb,
- void *cb_opaque)
+ void *cb_opaque,
+ Error **errp)
{
BDRVQcow2State *s = bs->opaque;
int old_version = s->qcow_version, new_version = old_version;
bool encrypt;
int encformat;
int refcount_bits = s->refcount_bits;
- Error *local_err = NULL;
int ret;
QemuOptDesc *desc = opts->list->desc;
Qcow2AmendHelperCBInfo helper_cb_info;
} else if (!strcmp(compat, "1.1")) {
new_version = 3;
} else {
- error_report("Unknown compatibility level %s", compat);
+ error_setg(errp, "Unknown compatibility level %s", compat);
return -EINVAL;
}
} else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) {
- error_report("Cannot change preallocation mode");
+ error_setg(errp, "Cannot change preallocation mode");
return -ENOTSUP;
} else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) {
new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0);
!!s->crypto);
if (encrypt != !!s->crypto) {
- error_report("Changing the encryption flag is not supported");
+ error_setg(errp,
+ "Changing the encryption flag is not supported");
return -ENOTSUP;
}
} else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT_FORMAT)) {
qemu_opt_get(opts, BLOCK_OPT_ENCRYPT_FORMAT));
if (encformat != s->crypt_method_header) {
- error_report("Changing the encryption format is not supported");
+ error_setg(errp,
+ "Changing the encryption format is not supported");
return -ENOTSUP;
}
} else if (g_str_has_prefix(desc->name, "encrypt.")) {
- error_report("Changing the encryption parameters is not supported");
+ error_setg(errp,
+ "Changing the encryption parameters is not supported");
return -ENOTSUP;
} else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) {
cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE,
cluster_size);
if (cluster_size != s->cluster_size) {
- error_report("Changing the cluster size is not supported");
+ error_setg(errp, "Changing the cluster size is not supported");
return -ENOTSUP;
}
} else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
if (refcount_bits <= 0 || refcount_bits > 64 ||
!is_power_of_2(refcount_bits))
{
- error_report("Refcount width must be a power of two and may "
- "not exceed 64 bits");
+ error_setg(errp, "Refcount width must be a power of two and "
+ "may not exceed 64 bits");
return -EINVAL;
}
} else {
ret = qcow2_update_header(bs);
if (ret < 0) {
s->qcow_version = old_version;
+ error_setg_errno(errp, -ret, "Failed to update the image header");
return ret;
}
}
int refcount_order = ctz32(refcount_bits);
if (new_version < 3 && refcount_bits != 16) {
- error_report("Different refcount widths than 16 bits require "
- "compatibility level 1.1 or above (use compat=1.1 or "
- "greater)");
+ error_setg(errp, "Refcount widths other than 16 bits require "
+ "compatibility level 1.1 or above (use compat=1.1 or "
+ "greater)");
return -EINVAL;
}
helper_cb_info.current_operation = QCOW2_CHANGING_REFCOUNT_ORDER;
ret = qcow2_change_refcount_order(bs, refcount_order,
&qcow2_amend_helper_cb,
- &helper_cb_info, &local_err);
+ &helper_cb_info, errp);
if (ret < 0) {
- error_report_err(local_err);
return ret;
}
}
backing_file ?: s->image_backing_file,
backing_format ?: s->image_backing_format);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Failed to change the backing file");
return ret;
}
}
if (s->use_lazy_refcounts != lazy_refcounts) {
if (lazy_refcounts) {
if (new_version < 3) {
- error_report("Lazy refcounts only supported with compatibility "
- "level 1.1 and above (use compat=1.1 or greater)");
+ error_setg(errp, "Lazy refcounts only supported with "
+ "compatibility level 1.1 and above (use compat=1.1 "
+ "or greater)");
return -EINVAL;
}
s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS;
ret = qcow2_update_header(bs);
if (ret < 0) {
s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS;
+ error_setg_errno(errp, -ret, "Failed to update the image header");
return ret;
}
s->use_lazy_refcounts = true;
/* make image clean first */
ret = qcow2_mark_clean(bs);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Failed to make the image clean");
return ret;
}
/* now disallow lazy refcounts */
ret = qcow2_update_header(bs);
if (ret < 0) {
s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS;
+ error_setg_errno(errp, -ret, "Failed to update the image header");
return ret;
}
s->use_lazy_refcounts = false;
if (new_size) {
BlockBackend *blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL);
- ret = blk_insert_bs(blk, bs, &local_err);
+ ret = blk_insert_bs(blk, bs, errp);
if (ret < 0) {
- error_report_err(local_err);
blk_unref(blk);
return ret;
}
- ret = blk_truncate(blk, new_size, PREALLOC_MODE_OFF, &local_err);
+ ret = blk_truncate(blk, new_size, PREALLOC_MODE_OFF, errp);
blk_unref(blk);
if (ret < 0) {
- error_report_err(local_err);
return ret;
}
}
if (new_version < old_version) {
helper_cb_info.current_operation = QCOW2_DOWNGRADING;
ret = qcow2_downgrade(bs, new_version, &qcow2_amend_helper_cb,
- &helper_cb_info);
+ &helper_cb_info, errp);
if (ret < 0) {
return ret;
}
char *message;
va_list ap;
- fatal = fatal && !bs->read_only;
+ fatal = fatal && bdrv_is_writable(bs);
if (s->signaled_corruption &&
(!fatal || (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT)))
.bdrv_reopen_abort = qcow2_reopen_abort,
.bdrv_join_options = qcow2_join_options,
.bdrv_child_perm = bdrv_format_default_perms,
- .bdrv_create = qcow2_create,
+ .bdrv_co_create_opts = qcow2_co_create_opts,
+ .bdrv_co_create = qcow2_co_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
- .bdrv_co_get_block_status = qcow2_co_get_block_status,
+ .bdrv_co_block_status = qcow2_co_block_status,
.bdrv_co_preadv = qcow2_co_preadv,
.bdrv_co_pwritev = qcow2_co_pwritev,
.bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes,
.bdrv_co_pdiscard = qcow2_co_pdiscard,
- .bdrv_truncate = qcow2_truncate,
+ .bdrv_co_copy_range_from = qcow2_co_copy_range_from,
+ .bdrv_co_copy_range_to = qcow2_co_copy_range_to,
+ .bdrv_co_truncate = qcow2_co_truncate,
.bdrv_co_pwritev_compressed = qcow2_co_pwritev_compressed,
.bdrv_make_empty = qcow2_make_empty,
.bdrv_change_backing_file = qcow2_change_backing_file,
.bdrv_refresh_limits = qcow2_refresh_limits,
- .bdrv_invalidate_cache = qcow2_invalidate_cache,
+ .bdrv_co_invalidate_cache = qcow2_co_invalidate_cache,
.bdrv_inactivate = qcow2_inactivate,
.create_opts = &qcow2_create_opts,
- .bdrv_check = qcow2_check,
+ .bdrv_co_check = qcow2_co_check,
.bdrv_amend_options = qcow2_amend_options,
.bdrv_detach_aio_context = qcow2_detach_aio_context,