summaryrefslogtreecommitdiff
path: root/block/qcow2.c
diff options
context:
space:
mode:
authorChanho Park <chanho61.park@samsung.com>2014-06-26 20:28:10 +0900
committerChanho Park <chanho61.park@samsung.com>2014-07-07 16:25:44 +0900
commita15119db2ff5c2fdfdeb913b297bf8aa3399132e (patch)
tree7d6f779408bb772b11c029ab88000fc01856b599 /block/qcow2.c
parent340f06c9eaee097e626c251bf7a013350649c091 (diff)
downloadqemu-a15119db2ff5c2fdfdeb913b297bf8aa3399132e.tar.gz
qemu-a15119db2ff5c2fdfdeb913b297bf8aa3399132e.tar.bz2
qemu-a15119db2ff5c2fdfdeb913b297bf8aa3399132e.zip
Imported Upstream version 2.0.0upstream/2.0.0
Change-Id: I081766c4314e7893f54fec80b920b1638d15021f
Diffstat (limited to 'block/qcow2.c')
-rw-r--r--block/qcow2.c913
1 files changed, 753 insertions, 160 deletions
diff --git a/block/qcow2.c b/block/qcow2.c
index 3376901bd..e903d971c 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -52,7 +52,7 @@
typedef struct {
uint32_t magic;
uint32_t len;
-} QCowExtension;
+} QEMU_PACKED QCowExtension;
#define QCOW2_EXT_MAGIC_END 0
#define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA
@@ -79,7 +79,8 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename)
* return 0 upon success, non-0 otherwise
*/
static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
- uint64_t end_offset, void **p_feature_table)
+ uint64_t end_offset, void **p_feature_table,
+ Error **errp)
{
BDRVQcowState *s = bs->opaque;
QCowExtension ext;
@@ -100,10 +101,10 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
printf("attempting to read extended header in offset %lu\n", offset);
#endif
- if (bdrv_pread(bs->file, offset, &ext, sizeof(ext)) != sizeof(ext)) {
- fprintf(stderr, "qcow2_read_extension: ERROR: "
- "pread fail from offset %" PRIu64 "\n",
- offset);
+ ret = bdrv_pread(bs->file, offset, &ext, sizeof(ext));
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "qcow2_read_extension: ERROR: "
+ "pread fail from offset %" PRIu64, offset);
return 1;
}
be32_to_cpus(&ext.magic);
@@ -113,7 +114,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
printf("ext.magic = 0x%x\n", ext.magic);
#endif
if (ext.len > end_offset - offset) {
- error_report("Header extension too large");
+ error_setg(errp, "Header extension too large");
return -EINVAL;
}
@@ -123,14 +124,16 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
case QCOW2_EXT_MAGIC_BACKING_FORMAT:
if (ext.len >= sizeof(bs->backing_format)) {
- fprintf(stderr, "ERROR: ext_backing_format: len=%u too large"
- " (>=%zu)\n",
- ext.len, sizeof(bs->backing_format));
+ error_setg(errp, "ERROR: ext_backing_format: len=%u too large"
+ " (>=%zu)", ext.len, sizeof(bs->backing_format));
return 2;
}
- if (bdrv_pread(bs->file, offset , bs->backing_format,
- ext.len) != ext.len)
+ ret = bdrv_pread(bs->file, offset, bs->backing_format, ext.len);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "ERROR: ext_backing_format: "
+ "Could not read format name");
return 3;
+ }
bs->backing_format[ext.len] = '\0';
#ifdef DEBUG_EXT
printf("Qcow2: Got format extension %s\n", bs->backing_format);
@@ -142,6 +145,8 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
void* feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature));
ret = bdrv_pread(bs->file, offset , feature_table, ext.len);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "ERROR: ext_feature_table: "
+ "Could not read table");
return ret;
}
@@ -161,6 +166,8 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
ret = bdrv_pread(bs->file, offset , uext->data, uext->len);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "ERROR: unknown extension: "
+ "Could not read data");
return ret;
}
}
@@ -184,8 +191,8 @@ static void cleanup_unknown_header_ext(BlockDriverState *bs)
}
}
-static void GCC_FMT_ATTR(2, 3) report_unsupported(BlockDriverState *bs,
- const char *fmt, ...)
+static void GCC_FMT_ATTR(3, 4) report_unsupported(BlockDriverState *bs,
+ Error **errp, const char *fmt, ...)
{
char msg[64];
va_list ap;
@@ -194,17 +201,17 @@ static void GCC_FMT_ATTR(2, 3) report_unsupported(BlockDriverState *bs,
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
- qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
- bs->device_name, "qcow2", msg);
+ error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, bs->device_name, "qcow2",
+ msg);
}
static void report_unsupported_feature(BlockDriverState *bs,
- Qcow2Feature *table, uint64_t mask)
+ Error **errp, Qcow2Feature *table, uint64_t mask)
{
while (table && table->name[0] != '\0') {
if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) {
if (mask & (1 << table->bit)) {
- report_unsupported(bs, "%.46s",table->name);
+ report_unsupported(bs, errp, "%.46s", table->name);
mask &= ~(1 << table->bit);
}
}
@@ -212,7 +219,8 @@ static void report_unsupported_feature(BlockDriverState *bs,
}
if (mask) {
- report_unsupported(bs, "Unknown incompatible feature: %" PRIx64, mask);
+ report_unsupported(bs, errp, "Unknown incompatible feature: %" PRIx64,
+ mask);
}
}
@@ -261,12 +269,46 @@ static int qcow2_mark_clean(BlockDriverState *bs)
BDRVQcowState *s = bs->opaque;
if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
+ int ret;
+
+ s->incompatible_features &= ~QCOW2_INCOMPAT_DIRTY;
+
+ ret = bdrv_flush(bs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return qcow2_update_header(bs);
+ }
+ return 0;
+}
+
+/*
+ * Marks the image as corrupt.
+ */
+int qcow2_mark_corrupt(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+
+ s->incompatible_features |= QCOW2_INCOMPAT_CORRUPT;
+ return qcow2_update_header(bs);
+}
+
+/*
+ * Marks the image as consistent, i.e., unsets the corrupt bit, and flushes
+ * before if necessary.
+ */
+int qcow2_mark_consistent(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+
+ if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) {
int ret = bdrv_flush(bs);
if (ret < 0) {
return ret;
}
- s->incompatible_features &= ~QCOW2_INCOMPAT_DIRTY;
+ s->incompatible_features &= ~QCOW2_INCOMPAT_CORRUPT;
return qcow2_update_header(bs);
}
return 0;
@@ -281,11 +323,41 @@ static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
}
if (fix && result->check_errors == 0 && result->corruptions == 0) {
- return qcow2_mark_clean(bs);
+ ret = qcow2_mark_clean(bs);
+ if (ret < 0) {
+ return ret;
+ }
+ return qcow2_mark_consistent(bs);
}
return ret;
}
+static int validate_table_offset(BlockDriverState *bs, uint64_t offset,
+ uint64_t entries, size_t entry_len)
+{
+ BDRVQcowState *s = bs->opaque;
+ uint64_t size;
+
+ /* 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;
+ }
+
+ size = entries * entry_len;
+
+ if (INT64_MAX - size < offset) {
+ return -EINVAL;
+ }
+
+ /* Tables must be cluster aligned */
+ if (offset & (s->cluster_size - 1)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static QemuOptsList qcow2_runtime_opts = {
.name = "qcow2",
.head = QTAILQ_HEAD_INITIALIZER(qcow2_runtime_opts.head),
@@ -311,22 +383,84 @@ static QemuOptsList qcow2_runtime_opts = {
.type = QEMU_OPT_BOOL,
.help = "Generate discard requests when other clusters are freed",
},
+ {
+ .name = QCOW2_OPT_OVERLAP,
+ .type = QEMU_OPT_STRING,
+ .help = "Selects which overlap checks to perform from a range of "
+ "templates (none, constant, cached, all)",
+ },
+ {
+ .name = QCOW2_OPT_OVERLAP_MAIN_HEADER,
+ .type = QEMU_OPT_BOOL,
+ .help = "Check for unintended writes into the main qcow2 header",
+ },
+ {
+ .name = QCOW2_OPT_OVERLAP_ACTIVE_L1,
+ .type = QEMU_OPT_BOOL,
+ .help = "Check for unintended writes into the active L1 table",
+ },
+ {
+ .name = QCOW2_OPT_OVERLAP_ACTIVE_L2,
+ .type = QEMU_OPT_BOOL,
+ .help = "Check for unintended writes into an active L2 table",
+ },
+ {
+ .name = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE,
+ .type = QEMU_OPT_BOOL,
+ .help = "Check for unintended writes into the refcount table",
+ },
+ {
+ .name = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK,
+ .type = QEMU_OPT_BOOL,
+ .help = "Check for unintended writes into a refcount block",
+ },
+ {
+ .name = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE,
+ .type = QEMU_OPT_BOOL,
+ .help = "Check for unintended writes into the snapshot table",
+ },
+ {
+ .name = QCOW2_OPT_OVERLAP_INACTIVE_L1,
+ .type = QEMU_OPT_BOOL,
+ .help = "Check for unintended writes into an inactive L1 table",
+ },
+ {
+ .name = QCOW2_OPT_OVERLAP_INACTIVE_L2,
+ .type = QEMU_OPT_BOOL,
+ .help = "Check for unintended writes into an inactive L2 table",
+ },
{ /* end of list */ }
},
};
-static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
+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,
+};
+
+static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
+ Error **errp)
{
BDRVQcowState *s = bs->opaque;
- int len, i, ret = 0;
+ unsigned int len, i;
+ int ret = 0;
QCowHeader header;
QemuOpts *opts;
Error *local_err = NULL;
uint64_t ext_end;
uint64_t l1_vm_state_index;
+ const char *opt_overlap_check;
+ int overlap_check_template = 0;
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not read qcow2 header");
goto fail;
}
be32_to_cpus(&header.magic);
@@ -344,17 +478,30 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
be32_to_cpus(&header.nb_snapshots);
if (header.magic != QCOW_MAGIC) {
- ret = -EMEDIUMTYPE;
+ error_setg(errp, "Image is not in qcow2 format");
+ ret = -EINVAL;
goto fail;
}
if (header.version < 2 || header.version > 3) {
- report_unsupported(bs, "QCOW version %d", header.version);
+ report_unsupported(bs, errp, "QCOW version %d", header.version);
ret = -ENOTSUP;
goto fail;
}
s->qcow_version = header.version;
+ /* Initialise cluster size */
+ if (header.cluster_bits < MIN_CLUSTER_BITS ||
+ header.cluster_bits > MAX_CLUSTER_BITS) {
+ error_setg(errp, "Unsupported cluster size: 2^%i", header.cluster_bits);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ s->cluster_bits = header.cluster_bits;
+ s->cluster_size = 1 << s->cluster_bits;
+ s->cluster_sectors = 1 << (s->cluster_bits - 9);
+
/* Initialise version 3 header fields */
if (header.version == 2) {
header.incompatible_features = 0;
@@ -368,6 +515,18 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
be64_to_cpus(&header.autoclear_features);
be32_to_cpus(&header.refcount_order);
be32_to_cpus(&header.header_length);
+
+ if (header.header_length < 104) {
+ error_setg(errp, "qcow2 header too short");
+ ret = -EINVAL;
+ goto fail;
+ }
+ }
+
+ if (header.header_length > s->cluster_size) {
+ error_setg(errp, "qcow2 header exceeds cluster size");
+ ret = -EINVAL;
+ goto fail;
}
if (header.header_length > sizeof(header)) {
@@ -376,10 +535,18 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
ret = bdrv_pread(bs->file, sizeof(header), s->unknown_header_fields,
s->unknown_header_fields_size);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not read unknown qcow2 header "
+ "fields");
goto fail;
}
}
+ if (header.backing_file_offset > s->cluster_size) {
+ error_setg(errp, "Invalid backing file offset");
+ ret = -EINVAL;
+ goto fail;
+ }
+
if (header.backing_file_offset) {
ext_end = header.backing_file_offset;
} else {
@@ -394,28 +561,38 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) {
void *feature_table = NULL;
qcow2_read_extensions(bs, header.header_length, ext_end,
- &feature_table);
- report_unsupported_feature(bs, feature_table,
+ &feature_table, NULL);
+ report_unsupported_feature(bs, errp, feature_table,
s->incompatible_features &
~QCOW2_INCOMPAT_MASK);
ret = -ENOTSUP;
+ g_free(feature_table);
goto fail;
}
+ if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) {
+ /* Corrupt images may not be written to unless they are being repaired
+ */
+ if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_CHECK)) {
+ error_setg(errp, "qcow2: Image is corrupt; cannot be opened "
+ "read/write");
+ ret = -EACCES;
+ goto fail;
+ }
+ }
+
/* Check support for various header values */
if (header.refcount_order != 4) {
- report_unsupported(bs, "%d bit reference counts",
+ report_unsupported(bs, errp, "%d bit reference counts",
1 << header.refcount_order);
ret = -ENOTSUP;
goto fail;
}
+ s->refcount_order = header.refcount_order;
- if (header.cluster_bits < MIN_CLUSTER_BITS ||
- header.cluster_bits > MAX_CLUSTER_BITS) {
- ret = -EINVAL;
- goto fail;
- }
if (header.crypt_method > QCOW_CRYPT_AES) {
+ error_setg(errp, "Unsupported encryption method: %i",
+ header.crypt_method);
ret = -EINVAL;
goto fail;
}
@@ -423,27 +600,57 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
if (s->crypt_method_header) {
bs->encrypted = 1;
}
- s->cluster_bits = header.cluster_bits;
- s->cluster_size = 1 << s->cluster_bits;
- s->cluster_sectors = 1 << (s->cluster_bits - 9);
+
s->l2_bits = s->cluster_bits - 3; /* L2 is always one cluster */
s->l2_size = 1 << s->l2_bits;
bs->total_sectors = header.size / 512;
s->csize_shift = (62 - (s->cluster_bits - 8));
s->csize_mask = (1 << (s->cluster_bits - 8)) - 1;
s->cluster_offset_mask = (1LL << s->csize_shift) - 1;
+
s->refcount_table_offset = header.refcount_table_offset;
s->refcount_table_size =
header.refcount_table_clusters << (s->cluster_bits - 3);
- s->snapshots_offset = header.snapshots_offset;
- s->nb_snapshots = header.nb_snapshots;
+ if (header.refcount_table_clusters > qcow2_max_refcount_clusters(s)) {
+ error_setg(errp, "Reference count table too large");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = validate_table_offset(bs, s->refcount_table_offset,
+ s->refcount_table_size, sizeof(uint64_t));
+ 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));
+ 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) {
+ error_setg(errp, "Active L1 table too large");
+ ret = -EFBIG;
+ goto fail;
+ }
s->l1_size = header.l1_size;
l1_vm_state_index = size_to_l1(s, header.size);
if (l1_vm_state_index > INT_MAX) {
+ error_setg(errp, "Image is too big");
ret = -EFBIG;
goto fail;
}
@@ -452,16 +659,27 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
/* the L1 table must contain at least enough entries to put
header.size bytes */
if (s->l1_size < s->l1_vm_state_index) {
+ error_setg(errp, "L1 table is too small");
ret = -EINVAL;
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 = g_malloc0(
align_offset(s->l1_size * sizeof(uint64_t), 512));
ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table,
s->l1_size * sizeof(uint64_t));
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not read L1 table");
goto fail;
}
for(i = 0;i < s->l1_size; i++) {
@@ -482,6 +700,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
ret = qcow2_refcount_init(bs);
if (ret != 0) {
+ error_setg_errno(errp, -ret, "Could not initialize refcount handling");
goto fail;
}
@@ -489,7 +708,9 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
QTAILQ_INIT(&s->discards);
/* read qcow2 extensions */
- if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL)) {
+ if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL,
+ &local_err)) {
+ error_propagate(errp, local_err);
ret = -EINVAL;
goto fail;
}
@@ -497,27 +718,36 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
/* read the backing file name */
if (header.backing_file_offset != 0) {
len = header.backing_file_size;
- if (len > 1023) {
- len = 1023;
+ if (len > MIN(1023, s->cluster_size - header.backing_file_offset)) {
+ error_setg(errp, "Backing file name too long");
+ ret = -EINVAL;
+ goto fail;
}
ret = bdrv_pread(bs->file, header.backing_file_offset,
bs->backing_file, len);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not read backing file name");
goto fail;
}
bs->backing_file[len] = '\0';
}
+ /* Internal snapshots */
+ s->snapshots_offset = header.snapshots_offset;
+ s->nb_snapshots = header.nb_snapshots;
+
ret = qcow2_read_snapshots(bs);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not read snapshots");
goto fail;
}
/* Clear unknown autoclear feature bits */
- if (!bs->read_only && s->autoclear_features != 0) {
+ if (!bs->read_only && !(flags & BDRV_O_INCOMING) && s->autoclear_features) {
s->autoclear_features = 0;
ret = qcow2_update_header(bs);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not update qcow2 header");
goto fail;
}
}
@@ -526,22 +756,22 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
qemu_co_mutex_init(&s->lock);
/* Repair image if dirty */
- if (!(flags & BDRV_O_CHECK) && !bs->read_only &&
+ if (!(flags & (BDRV_O_CHECK | BDRV_O_INCOMING)) && !bs->read_only &&
(s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) {
BdrvCheckResult result = {0};
ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not repair dirty image");
goto fail;
}
}
/* Enable lazy_refcounts according to image and command line options */
- opts = qemu_opts_create_nofail(&qcow2_runtime_opts);
+ opts = qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
- if (error_is_set(&local_err)) {
- qerror_report_err(local_err);
- error_free(local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
ret = -EINVAL;
goto fail;
}
@@ -559,11 +789,38 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
s->discard_passthrough[QCOW2_DISCARD_OTHER] =
qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false);
+ opt_overlap_check = qemu_opt_get(opts, "overlap-check") ?: "cached";
+ if (!strcmp(opt_overlap_check, "none")) {
+ overlap_check_template = 0;
+ } else if (!strcmp(opt_overlap_check, "constant")) {
+ overlap_check_template = QCOW2_OL_CONSTANT;
+ } else if (!strcmp(opt_overlap_check, "cached")) {
+ overlap_check_template = QCOW2_OL_CACHED;
+ } else if (!strcmp(opt_overlap_check, "all")) {
+ overlap_check_template = QCOW2_OL_ALL;
+ } else {
+ error_setg(errp, "Unsupported value '%s' for qcow2 option "
+ "'overlap-check'. Allowed are either of the following: "
+ "none, constant, cached, all", opt_overlap_check);
+ qemu_opts_del(opts);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ s->overlap_check = 0;
+ for (i = 0; i < QCOW2_OL_MAX_BITNR; i++) {
+ /* overlap-check defines a template bitmask, but every flag may be
+ * overwritten through the associated boolean option */
+ s->overlap_check |=
+ qemu_opt_get_bool(opts, overlap_bool_option_names[i],
+ overlap_check_template & (1 << i)) << i;
+ }
+
qemu_opts_del(opts);
if (s->use_lazy_refcounts && s->qcow_version < 3) {
- qerror_report(ERROR_CLASS_GENERIC_ERROR, "Lazy refcounts require "
- "a qcow2 image with at least qemu 1.1 compatibility level");
+ error_setg(errp, "Lazy refcounts require a qcow2 image with at least "
+ "qemu 1.1 compatibility level");
ret = -EINVAL;
goto fail;
}
@@ -582,14 +839,28 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
qcow2_free_snapshots(bs);
qcow2_refcount_close(bs);
g_free(s->l1_table);
+ /* else pre-write overlap checks in cache_destroy may crash */
+ s->l1_table = NULL;
if (s->l2_table_cache) {
qcow2_cache_destroy(bs, s->l2_table_cache);
}
+ if (s->refcount_block_cache) {
+ qcow2_cache_destroy(bs, s->refcount_block_cache);
+ }
g_free(s->cluster_cache);
qemu_vfree(s->cluster_data);
return ret;
}
+static int qcow2_refresh_limits(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+
+ bs->bl.write_zeroes_alignment = s->cluster_sectors;
+
+ return 0;
+}
+
static int qcow2_set_key(BlockDriverState *bs, const char *key)
{
BDRVQcowState *s = bs->opaque;
@@ -632,32 +903,56 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key)
return 0;
}
-/* We have nothing to do for QCOW2 reopen, stubs just return
- * success */
+/* We have no actual commit/abort logic for qcow2, but we need to write out any
+ * unwritten data if we reopen read-only. */
static int qcow2_reopen_prepare(BDRVReopenState *state,
BlockReopenQueue *queue, Error **errp)
{
+ int ret;
+
+ if ((state->flags & BDRV_O_RDWR) == 0) {
+ ret = bdrv_flush(state->bs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = qcow2_mark_clean(state->bs);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
return 0;
}
-static int coroutine_fn qcow2_co_is_allocated(BlockDriverState *bs,
+static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int *pnum)
{
BDRVQcowState *s = bs->opaque;
uint64_t cluster_offset;
- int ret;
+ int index_in_cluster, ret;
+ int64_t status = 0;
*pnum = nb_sectors;
- /* FIXME We can get errors here, but the bdrv_co_is_allocated interface
- * can't pass them on today */
qemu_co_mutex_lock(&s->lock);
ret = qcow2_get_cluster_offset(bs, sector_num << 9, pnum, &cluster_offset);
qemu_co_mutex_unlock(&s->lock);
if (ret < 0) {
- *pnum = 0;
+ return ret;
}
- return (cluster_offset != 0) || (ret == QCOW2_CLUSTER_ZERO);
+ if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED &&
+ !s->crypt_method) {
+ index_in_cluster = sector_num & (s->cluster_sectors - 1);
+ cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
+ status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset;
+ }
+ if (ret == QCOW2_CLUSTER_ZERO) {
+ status |= BDRV_BLOCK_ZERO;
+ } else if (ret != QCOW2_CLUSTER_UNALLOCATED) {
+ status |= BDRV_BLOCK_DATA;
+ }
+ return status;
}
/* handle reading after the end of the backing file */
@@ -821,7 +1116,6 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
{
BDRVQcowState *s = bs->opaque;
int index_in_cluster;
- int n_end;
int ret;
int cur_nr_sectors; /* number of sectors in current iteration */
uint64_t cluster_offset;
@@ -845,14 +1139,16 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
trace_qcow2_writev_start_part(qemu_coroutine_self());
index_in_cluster = sector_num & (s->cluster_sectors - 1);
- n_end = index_in_cluster + remaining_sectors;
+ cur_nr_sectors = remaining_sectors;
if (s->crypt_method &&
- n_end > QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors) {
- n_end = QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors;
+ cur_nr_sectors >
+ QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors - index_in_cluster) {
+ cur_nr_sectors =
+ QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors - index_in_cluster;
}
ret = qcow2_alloc_cluster_offset(bs, sector_num << 9,
- index_in_cluster, n_end, &cur_nr_sectors, &cluster_offset, &l2meta);
+ &cur_nr_sectors, &cluster_offset, &l2meta);
if (ret < 0) {
goto fail;
}
@@ -881,6 +1177,13 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
cur_nr_sectors * 512);
}
+ ret = qcow2_pre_write_overlap_check(bs, 0,
+ cluster_offset + index_in_cluster * BDRV_SECTOR_SIZE,
+ cur_nr_sectors * BDRV_SECTOR_SIZE);
+ if (ret < 0) {
+ goto fail;
+ }
+
qemu_co_mutex_unlock(&s->lock);
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
trace_qcow2_writev_data(qemu_coroutine_self(),
@@ -947,11 +1250,15 @@ static void qcow2_close(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
g_free(s->l1_table);
+ /* else pre-write overlap checks in cache_destroy may crash */
+ s->l1_table = NULL;
- qcow2_cache_flush(bs, s->l2_table_cache);
- qcow2_cache_flush(bs, s->refcount_block_cache);
+ if (!(bs->open_flags & BDRV_O_INCOMING)) {
+ qcow2_cache_flush(bs, s->l2_table_cache);
+ qcow2_cache_flush(bs, s->refcount_block_cache);
- qcow2_mark_clean(bs);
+ qcow2_mark_clean(bs);
+ }
qcow2_cache_destroy(bs, s->l2_table_cache);
qcow2_cache_destroy(bs, s->refcount_block_cache);
@@ -965,7 +1272,7 @@ static void qcow2_close(BlockDriverState *bs)
qcow2_free_snapshots(bs);
}
-static void qcow2_invalidate_cache(BlockDriverState *bs)
+static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
{
BDRVQcowState *s = bs->opaque;
int flags = s->flags;
@@ -973,6 +1280,8 @@ static void qcow2_invalidate_cache(BlockDriverState *bs)
AES_KEY aes_decrypt_key;
uint32_t crypt_method = 0;
QDict *options;
+ Error *local_err = NULL;
+ int ret;
/*
* Backing files are read-only which makes all of their metadata immutable,
@@ -987,12 +1296,25 @@ static void qcow2_invalidate_cache(BlockDriverState *bs)
qcow2_close(bs);
- options = qdict_new();
- qdict_put(options, QCOW2_OPT_LAZY_REFCOUNTS,
- qbool_from_int(s->use_lazy_refcounts));
+ bdrv_invalidate_cache(bs->file, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
memset(s, 0, sizeof(BDRVQcowState));
- qcow2_open(bs, options, flags);
+ options = qdict_clone_shallow(bs->options);
+
+ ret = qcow2_open(bs, options, flags, &local_err);
+ if (local_err) {
+ error_setg(errp, "Could not reopen qcow2 layer: %s",
+ error_get_pretty(local_err));
+ error_free(local_err);
+ return;
+ } else if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not reopen qcow2 layer");
+ return;
+ }
QDECREF(options);
@@ -1076,7 +1398,7 @@ int qcow2_update_header(BlockDriverState *bs)
.incompatible_features = cpu_to_be64(s->incompatible_features),
.compatible_features = cpu_to_be64(s->compatible_features),
.autoclear_features = cpu_to_be64(s->autoclear_features),
- .refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT),
+ .refcount_order = cpu_to_be32(s->refcount_order),
.header_length = cpu_to_be32(header_length),
};
@@ -1130,6 +1452,11 @@ int qcow2_update_header(BlockDriverState *bs)
.name = "dirty bit",
},
{
+ .type = QCOW2_FEAT_TYPE_INCOMPATIBLE,
+ .bit = QCOW2_INCOMPAT_CORRUPT_BITNR,
+ .name = "corrupt bit",
+ },
+ {
.type = QCOW2_FEAT_TYPE_COMPATIBLE,
.bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR,
.name = "lazy refcounts",
@@ -1210,34 +1537,39 @@ static int preallocate(BlockDriverState *bs)
int ret;
QCowL2Meta *meta;
- nb_sectors = bdrv_getlength(bs) >> 9;
+ nb_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
offset = 0;
while (nb_sectors) {
- num = MIN(nb_sectors, INT_MAX >> 9);
- ret = qcow2_alloc_cluster_offset(bs, offset, 0, num, &num,
+ num = MIN(nb_sectors, INT_MAX >> BDRV_SECTOR_BITS);
+ ret = qcow2_alloc_cluster_offset(bs, offset, &num,
&host_offset, &meta);
if (ret < 0) {
return ret;
}
- ret = qcow2_alloc_cluster_link_l2(bs, meta);
- if (ret < 0) {
- qcow2_free_any_clusters(bs, meta->alloc_offset, meta->nb_clusters,
- QCOW2_DISCARD_NEVER);
- return ret;
- }
+ while (meta) {
+ QCowL2Meta *next = meta->next;
+
+ ret = qcow2_alloc_cluster_link_l2(bs, meta);
+ if (ret < 0) {
+ qcow2_free_any_clusters(bs, meta->alloc_offset,
+ meta->nb_clusters, QCOW2_DISCARD_NEVER);
+ return ret;
+ }
- /* There are no dependent requests, but we need to remove our request
- * from the list of in-flight requests */
- if (meta != NULL) {
+ /* There are no dependent requests, but we need to remove our
+ * request from the list of in-flight requests */
QLIST_REMOVE(meta, next_in_flight);
+
+ g_free(meta);
+ meta = next;
}
/* TODO Preallocate data if requested */
nb_sectors -= num;
- offset += num << 9;
+ offset += num << BDRV_SECTOR_BITS;
}
/*
@@ -1246,9 +1578,10 @@ static int preallocate(BlockDriverState *bs)
* EOF). Extend the image to the last allocated sector.
*/
if (host_offset != 0) {
- uint8_t buf[512];
- memset(buf, 0, 512);
- ret = bdrv_write(bs->file, (host_offset >> 9) + num - 1, buf, 1);
+ uint8_t buf[BDRV_SECTOR_SIZE];
+ memset(buf, 0, BDRV_SECTOR_SIZE);
+ ret = bdrv_write(bs->file, (host_offset >> BDRV_SECTOR_BITS) + num - 1,
+ buf, 1);
if (ret < 0) {
return ret;
}
@@ -1260,7 +1593,8 @@ static int preallocate(BlockDriverState *bs)
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, int prealloc,
- QEMUOptionParameter *options, int version)
+ QEMUOptionParameter *options, int version,
+ Error **errp)
{
/* Calculate cluster_bits */
int cluster_bits;
@@ -1268,9 +1602,8 @@ static int qcow2_create2(const char *filename, int64_t total_size,
if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS ||
(1 << cluster_bits) != cluster_size)
{
- error_report(
- "Cluster size must be a power of two between %d and %dk",
- 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10));
+ 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 -EINVAL;
}
@@ -1287,59 +1620,72 @@ static int qcow2_create2(const char *filename, int64_t total_size,
* size for any qcow2 image.
*/
BlockDriverState* bs;
- QCowHeader header;
- uint8_t* refcount_table;
+ QCowHeader *header;
+ uint64_t* refcount_table;
+ Error *local_err = NULL;
int ret;
- ret = bdrv_create_file(filename, options);
+ ret = bdrv_create_file(filename, options, &local_err);
if (ret < 0) {
+ error_propagate(errp, local_err);
return ret;
}
- ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR);
+ bs = NULL;
+ ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
+ NULL, &local_err);
if (ret < 0) {
+ error_propagate(errp, local_err);
return ret;
}
/* Write the header */
- memset(&header, 0, sizeof(header));
- header.magic = cpu_to_be32(QCOW_MAGIC);
- header.version = cpu_to_be32(version);
- header.cluster_bits = cpu_to_be32(cluster_bits);
- header.size = cpu_to_be64(0);
- header.l1_table_offset = cpu_to_be64(0);
- header.l1_size = cpu_to_be32(0);
- header.refcount_table_offset = cpu_to_be64(cluster_size);
- header.refcount_table_clusters = cpu_to_be32(1);
- header.refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT);
- header.header_length = cpu_to_be32(sizeof(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(cluster_bits),
+ .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(3 + REFCOUNT_SHIFT),
+ .header_length = cpu_to_be32(sizeof(*header)),
+ };
if (flags & BLOCK_FLAG_ENCRYPT) {
- header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
+ header->crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
} else {
- header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
+ header->crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
}
if (flags & BLOCK_FLAG_LAZY_REFCOUNTS) {
- header.compatible_features |=
+ header->compatible_features |=
cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
}
- ret = bdrv_pwrite(bs, 0, &header, sizeof(header));
+ ret = bdrv_pwrite(bs, 0, header, cluster_size);
+ g_free(header);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not write qcow2 header");
goto out;
}
- /* Write an empty refcount table */
- refcount_table = g_malloc0(cluster_size);
- ret = bdrv_pwrite(bs, cluster_size, refcount_table, cluster_size);
+ /* 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 = bdrv_pwrite(bs, cluster_size, refcount_table, 2 * cluster_size);
g_free(refcount_table);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not write refcount table");
goto out;
}
- bdrv_close(bs);
+ bdrv_unref(bs);
+ bs = NULL;
/*
* And now open the image and make it consistent first (i.e. increase the
@@ -1348,14 +1694,17 @@ static int qcow2_create2(const char *filename, int64_t total_size,
*/
BlockDriver* drv = bdrv_find_format("qcow2");
assert(drv != NULL);
- ret = bdrv_open(bs, filename, NULL,
- BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv);
+ ret = bdrv_open(&bs, filename, NULL, NULL,
+ BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv, &local_err);
if (ret < 0) {
+ error_propagate(errp, local_err);
goto out;
}
- ret = qcow2_alloc_clusters(bs, 2 * cluster_size);
+ ret = qcow2_alloc_clusters(bs, 3 * cluster_size);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not allocate clusters for qcow2 "
+ "header and refcount table");
goto out;
} else if (ret != 0) {
@@ -1366,6 +1715,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
/* Okay, now that we have a valid image, let's give it the right size */
ret = bdrv_truncate(bs, total_size * BDRV_SECTOR_SIZE);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not resize image");
goto out;
}
@@ -1373,6 +1723,8 @@ static int qcow2_create2(const char *filename, int64_t total_size,
if (backing_file) {
ret = bdrv_change_backing_file(bs, 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);
goto out;
}
}
@@ -1384,17 +1736,33 @@ static int qcow2_create2(const char *filename, int64_t total_size,
ret = preallocate(bs);
qemu_co_mutex_unlock(&s->lock);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not preallocate metadata");
goto out;
}
}
+ bdrv_unref(bs);
+ bs = NULL;
+
+ /* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */
+ ret = bdrv_open(&bs, filename, NULL, NULL,
+ BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING,
+ drv, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto out;
+ }
+
ret = 0;
out:
- bdrv_delete(bs);
+ if (bs) {
+ bdrv_unref(bs);
+ }
return ret;
}
-static int qcow2_create(const char *filename, QEMUOptionParameter *options)
+static int qcow2_create(const char *filename, QEMUOptionParameter *options,
+ Error **errp)
{
const char *backing_file = NULL;
const char *backing_fmt = NULL;
@@ -1402,7 +1770,9 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
int flags = 0;
size_t cluster_size = DEFAULT_CLUSTER_SIZE;
int prealloc = 0;
- int version = 2;
+ int version = 3;
+ Error *local_err = NULL;
+ int ret;
/* Read out options */
while (options && options->name) {
@@ -1424,18 +1794,20 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
} else if (!strcmp(options->value.s, "metadata")) {
prealloc = 1;
} else {
- fprintf(stderr, "Invalid preallocation mode: '%s'\n",
- options->value.s);
+ error_setg(errp, "Invalid preallocation mode: '%s'",
+ options->value.s);
return -EINVAL;
}
} else if (!strcmp(options->name, BLOCK_OPT_COMPAT_LEVEL)) {
- if (!options->value.s || !strcmp(options->value.s, "0.10")) {
+ if (!options->value.s) {
+ /* keep the default */
+ } else if (!strcmp(options->value.s, "0.10")) {
version = 2;
} else if (!strcmp(options->value.s, "1.1")) {
version = 3;
} else {
- fprintf(stderr, "Invalid compatibility level: '%s'\n",
- options->value.s);
+ error_setg(errp, "Invalid compatibility level: '%s'",
+ options->value.s);
return -EINVAL;
}
} else if (!strcmp(options->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
@@ -1445,43 +1817,27 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
}
if (backing_file && prealloc) {
- fprintf(stderr, "Backing file and preallocation cannot be used at "
- "the same time\n");
+ error_setg(errp, "Backing file and preallocation cannot be used at "
+ "the same time");
return -EINVAL;
}
if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) {
- fprintf(stderr, "Lazy refcounts only supported with compatibility "
- "level 1.1 and above (use compat=1.1 or greater)\n");
+ error_setg(errp, "Lazy refcounts only supported with compatibility "
+ "level 1.1 and above (use compat=1.1 or greater)");
return -EINVAL;
}
- return qcow2_create2(filename, sectors, backing_file, backing_fmt, flags,
- cluster_size, prealloc, options, version);
-}
-
-static int qcow2_make_empty(BlockDriverState *bs)
-{
-#if 0
- /* XXX: not correct */
- BDRVQcowState *s = bs->opaque;
- uint32_t l1_length = s->l1_size * sizeof(uint64_t);
- int ret;
-
- memset(s->l1_table, 0, l1_length);
- if (bdrv_pwrite(bs->file, s->l1_table_offset, s->l1_table, l1_length) < 0)
- return -1;
- ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length);
- if (ret < 0)
- return ret;
-
- l2_cache_reset(bs);
-#endif
- return 0;
+ ret = qcow2_create2(filename, sectors, backing_file, backing_fmt, flags,
+ cluster_size, prealloc, options, version, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ }
+ return ret;
}
static coroutine_fn int qcow2_co_write_zeroes(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors)
+ int64_t sector_num, int nb_sectors, BdrvRequestFlags flags)
{
int ret;
BDRVQcowState *s = bs->opaque;
@@ -1508,7 +1864,7 @@ static coroutine_fn int qcow2_co_discard(BlockDriverState *bs,
qemu_co_mutex_lock(&s->lock);
ret = qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS,
- nb_sectors);
+ nb_sectors, QCOW2_DISCARD_REQUEST);
qemu_co_mutex_unlock(&s->lock);
return ret;
}
@@ -1631,6 +1987,12 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num,
goto fail;
}
cluster_offset &= s->cluster_offset_mask;
+
+ ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset, out_len);
+ if (ret < 0) {
+ goto fail;
+ }
+
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED);
ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len);
if (ret < 0) {
@@ -1668,19 +2030,43 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
return 0;
}
-static int64_t qcow2_vm_state_offset(BDRVQcowState *s)
-{
- return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
-}
-
static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
{
BDRVQcowState *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;
}
+static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ ImageInfoSpecific *spec_info = g_new(ImageInfoSpecific, 1);
+
+ *spec_info = (ImageInfoSpecific){
+ .kind = IMAGE_INFO_SPECIFIC_KIND_QCOW2,
+ {
+ .qcow2 = g_new(ImageInfoSpecificQCow2, 1),
+ },
+ };
+ if (s->qcow_version == 2) {
+ *spec_info->qcow2 = (ImageInfoSpecificQCow2){
+ .compat = g_strdup("0.10"),
+ };
+ } else if (s->qcow_version == 3) {
+ *spec_info->qcow2 = (ImageInfoSpecificQCow2){
+ .compat = g_strdup("1.1"),
+ .lazy_refcounts = s->compatible_features &
+ QCOW2_COMPAT_LAZY_REFCOUNTS,
+ .has_lazy_refcounts = true,
+ };
+ }
+
+ return spec_info;
+}
+
#if 0
static void dump_refcounts(BlockDriverState *bs)
{
@@ -1706,13 +2092,22 @@ static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
int64_t pos)
{
BDRVQcowState *s = bs->opaque;
+ int64_t total_sectors = bs->total_sectors;
int growable = bs->growable;
+ bool zero_beyond_eof = bs->zero_beyond_eof;
int ret;
BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE);
bs->growable = 1;
+ bs->zero_beyond_eof = false;
ret = bdrv_pwritev(bs, qcow2_vm_state_offset(s) + pos, qiov);
bs->growable = growable;
+ bs->zero_beyond_eof = zero_beyond_eof;
+
+ /* bdrv_co_do_writev will have increased the total_sectors value to include
+ * the VM state - the VM state is however not an actual part of the block
+ * device, therefore, we need to restore the old value. */
+ bs->total_sectors = total_sectors;
return ret;
}
@@ -1722,16 +2117,212 @@ static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf,
{
BDRVQcowState *s = bs->opaque;
int growable = bs->growable;
+ bool zero_beyond_eof = bs->zero_beyond_eof;
int ret;
BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_LOAD);
bs->growable = 1;
+ bs->zero_beyond_eof = false;
ret = bdrv_pread(bs, qcow2_vm_state_offset(s) + pos, buf, size);
bs->growable = growable;
+ bs->zero_beyond_eof = zero_beyond_eof;
return ret;
}
+/*
+ * Downgrades an image's version. To achieve this, any incompatible features
+ * have to be removed.
+ */
+static int qcow2_downgrade(BlockDriverState *bs, int target_version)
+{
+ BDRVQcowState *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;
+ }
+
+ if (s->refcount_order != 4) {
+ /* we would have to convert the image to a refcount_order == 4 image
+ * here; however, since qemu (at the time of writing this) does not
+ * support anything different than 4 anyway, there is no point in doing
+ * so right now; however, we should error out (if qemu supports this in
+ * the future and this code has not been adapted) */
+ error_report("qcow2_downgrade: Image refcount orders other than 4 are "
+ "currently not supported.");
+ return -ENOTSUP;
+ }
+
+ /* clear incompatible features */
+ if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
+ ret = qcow2_mark_clean(bs);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ /* with QCOW2_INCOMPAT_CORRUPT, it is pretty much impossible to get here in
+ * the first place; if that happens nonetheless, returning -ENOTSUP is the
+ * best thing to do anyway */
+
+ if (s->incompatible_features) {
+ return -ENOTSUP;
+ }
+
+ /* since we can ignore compatible features, we can set them to 0 as well */
+ s->compatible_features = 0;
+ /* if lazy refcounts have been used, they have already been fixed through
+ * clearing the dirty flag */
+
+ /* clearing autoclear features is trivial */
+ s->autoclear_features = 0;
+
+ ret = qcow2_expand_zero_clusters(bs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ s->qcow_version = target_version;
+ ret = qcow2_update_header(bs);
+ if (ret < 0) {
+ s->qcow_version = current_version;
+ return ret;
+ }
+ return 0;
+}
+
+static int qcow2_amend_options(BlockDriverState *bs,
+ QEMUOptionParameter *options)
+{
+ BDRVQcowState *s = bs->opaque;
+ int old_version = s->qcow_version, new_version = old_version;
+ uint64_t new_size = 0;
+ const char *backing_file = NULL, *backing_format = NULL;
+ bool lazy_refcounts = s->use_lazy_refcounts;
+ int ret;
+ int i;
+
+ for (i = 0; options[i].name; i++)
+ {
+ if (!options[i].assigned) {
+ /* only change explicitly defined options */
+ continue;
+ }
+
+ if (!strcmp(options[i].name, "compat")) {
+ if (!options[i].value.s) {
+ /* preserve default */
+ } else if (!strcmp(options[i].value.s, "0.10")) {
+ new_version = 2;
+ } else if (!strcmp(options[i].value.s, "1.1")) {
+ new_version = 3;
+ } else {
+ fprintf(stderr, "Unknown compatibility level %s.\n",
+ options[i].value.s);
+ return -EINVAL;
+ }
+ } else if (!strcmp(options[i].name, "preallocation")) {
+ fprintf(stderr, "Cannot change preallocation mode.\n");
+ return -ENOTSUP;
+ } else if (!strcmp(options[i].name, "size")) {
+ new_size = options[i].value.n;
+ } else if (!strcmp(options[i].name, "backing_file")) {
+ backing_file = options[i].value.s;
+ } else if (!strcmp(options[i].name, "backing_fmt")) {
+ backing_format = options[i].value.s;
+ } else if (!strcmp(options[i].name, "encryption")) {
+ if ((options[i].value.n != !!s->crypt_method)) {
+ fprintf(stderr, "Changing the encryption flag is not "
+ "supported.\n");
+ return -ENOTSUP;
+ }
+ } else if (!strcmp(options[i].name, "cluster_size")) {
+ if (options[i].value.n != s->cluster_size) {
+ fprintf(stderr, "Changing the cluster size is not "
+ "supported.\n");
+ return -ENOTSUP;
+ }
+ } else if (!strcmp(options[i].name, "lazy_refcounts")) {
+ lazy_refcounts = options[i].value.n;
+ } else {
+ /* if this assertion fails, this probably means a new option was
+ * added without having it covered here */
+ assert(false);
+ }
+ }
+
+ if (new_version != old_version) {
+ if (new_version > old_version) {
+ /* Upgrade */
+ s->qcow_version = new_version;
+ ret = qcow2_update_header(bs);
+ if (ret < 0) {
+ s->qcow_version = old_version;
+ return ret;
+ }
+ } else {
+ ret = qcow2_downgrade(bs, new_version);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ }
+
+ if (backing_file || backing_format) {
+ ret = qcow2_change_backing_file(bs, backing_file ?: bs->backing_file,
+ backing_format ?: bs->backing_format);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ if (s->use_lazy_refcounts != lazy_refcounts) {
+ if (lazy_refcounts) {
+ if (s->qcow_version < 3) {
+ fprintf(stderr, "Lazy refcounts only supported with compatibility "
+ "level 1.1 and above (use compat=1.1 or greater)\n");
+ return -EINVAL;
+ }
+ s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS;
+ ret = qcow2_update_header(bs);
+ if (ret < 0) {
+ s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS;
+ return ret;
+ }
+ s->use_lazy_refcounts = true;
+ } else {
+ /* make image clean first */
+ ret = qcow2_mark_clean(bs);
+ if (ret < 0) {
+ return ret;
+ }
+ /* now disallow lazy refcounts */
+ s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS;
+ ret = qcow2_update_header(bs);
+ if (ret < 0) {
+ s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS;
+ return ret;
+ }
+ s->use_lazy_refcounts = false;
+ }
+ }
+
+ if (new_size) {
+ ret = bdrv_truncate(bs, new_size);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static QEMUOptionParameter qcow2_create_options[] = {
{
.name = BLOCK_OPT_SIZE,
@@ -1786,9 +2377,8 @@ static BlockDriver bdrv_qcow2 = {
.bdrv_reopen_prepare = qcow2_reopen_prepare,
.bdrv_create = qcow2_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
- .bdrv_co_is_allocated = qcow2_co_is_allocated,
+ .bdrv_co_get_block_status = qcow2_co_get_block_status,
.bdrv_set_key = qcow2_set_key,
- .bdrv_make_empty = qcow2_make_empty,
.bdrv_co_readv = qcow2_co_readv,
.bdrv_co_writev = qcow2_co_writev,
@@ -1805,16 +2395,19 @@ static BlockDriver bdrv_qcow2 = {
.bdrv_snapshot_list = qcow2_snapshot_list,
.bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp,
.bdrv_get_info = qcow2_get_info,
+ .bdrv_get_specific_info = qcow2_get_specific_info,
.bdrv_save_vmstate = qcow2_save_vmstate,
.bdrv_load_vmstate = qcow2_load_vmstate,
.bdrv_change_backing_file = qcow2_change_backing_file,
+ .bdrv_refresh_limits = qcow2_refresh_limits,
.bdrv_invalidate_cache = qcow2_invalidate_cache,
.create_options = qcow2_create_options,
.bdrv_check = qcow2_check,
+ .bdrv_amend_options = qcow2_amend_options,
};
static void bdrv_qcow2_init(void)