Block layer patches:

- qcow2: Support for external data files
 - qcow2: Default to 4KB for the qcow2 cache entry size
 - Apply block driver whitelist for -drive format=help
 - Several qemu-iotests improvements
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJcgmYDAAoJEH8JsnLIjy/WiEgP/jirn5n4bFHDSzpofRxgpcEG
 2SoBpYtJQvAVgXmg8P1ZKeaX5yiiZpJS475ShuH6C3dnWuaHBBjvQfDkLLrh3v05
 4KJyZQUFx0WQZqkEiHxtgmE3iIOAsWe8VzBCZjsFITdp+fN8HlRjKVofyYP0y48G
 k9PzMlEe30Wu2s+lq6PEIXgpwIZqOVl1V3C7wDF8Vg/1OOsv+rK0vKD8yra/oQAc
 mthM+hjkpa+ohjE5aTeCsDVxO56AStvXv0d3bE0aF/ZtCvQdbVh5coYj1ldNz6VY
 Bvx+UP7kPHJw0wesZJXLeVyZUMyHAuu9vW6zKlDYJ7PoIXSLXC7rYyoEofvkAzQL
 awSluFj4HpYU1dKseJ8LzrMyUqjJ2eLJ+K48iNIh0vNlVuvX8aR62dIv0Obo3rod
 Y7zgyd6mSgDGulDa3xVsD0+eAUUEbLaUDBV80+M7S2/V9YP4Bt7lKbm0SQB4dUxm
 eOJfnbMTpIYUUjYtpCkURED3MlTIA51fy28O87TMJwa11sJTXRscysE9oNQNsvwW
 2UPhMPLykMI023glhV0vCwgXQ5kktOvpaB3U7LGQhhHd1ed9sdLEB1bO9eKWYr4N
 h6QPLBRLGUYd+BFMMIBLQtx8r+mAn1hUZoX4zxTn0lD50Rch3pRnCjMoC1iWqRkS
 J204Lzum5kgNPheyVxhy
 =GCRI
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging

Block layer patches:

- qcow2: Support for external data files
- qcow2: Default to 4KB for the qcow2 cache entry size
- Apply block driver whitelist for -drive format=help
- Several qemu-iotests improvements

# gpg: Signature made Fri 08 Mar 2019 12:54:27 GMT
# gpg:                using RSA key 7F09B272C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" [full]
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74  56FE 7F09 B272 C88F 2FD6

* remotes/kevin/tags/for-upstream: (33 commits)
  qcow2 spec: Describe string header extensions
  qemu-iotests: Add dependency to qemu-nbd tool
  ahci-test: Add dependency to qemu-img tool
  qemu-iotests: amend with external data file
  qemu-iotests: General tests for qcow2 with external data file
  qemu-iotests: Preallocation with external data file
  qcow2: Implement data-file-raw create option
  qcow2: Store data file name in the image
  qcow2: Creating images with external data file
  qcow2: Add basic data-file infrastructure
  qcow2: Support external data file in qemu-img check
  qcow2: Return error for snapshot operation with data file
  qcow2: External file I/O
  qcow2: Prepare qcow2_co_block_status() for data file
  qcow2: Return 0/-errno in qcow2_alloc_compressed_cluster_offset()
  qcow2: Don't assume 0 is an invalid cluster offset
  qcow2: Prepare count_contiguous_clusters() for external data file
  qcow2: Prepare qcow2_get_cluster_type() for external data file
  qcow2: Pass bs to qcow2_get_cluster_type()
  qcow2: Basic definitions for external data files
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-03-09 14:43:39 +00:00
commit 4c76137484
215 changed files with 1583 additions and 421 deletions

23
block.c
View File

@ -426,7 +426,7 @@ BlockDriver *bdrv_find_format(const char *format_name)
return bdrv_do_find_format(format_name); return bdrv_do_find_format(format_name);
} }
int bdrv_is_whitelisted(BlockDriver *drv, bool read_only) static int bdrv_format_is_whitelisted(const char *format_name, bool read_only)
{ {
static const char *whitelist_rw[] = { static const char *whitelist_rw[] = {
CONFIG_BDRV_RW_WHITELIST CONFIG_BDRV_RW_WHITELIST
@ -441,13 +441,13 @@ int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
} }
for (p = whitelist_rw; *p; p++) { for (p = whitelist_rw; *p; p++) {
if (!strcmp(drv->format_name, *p)) { if (!strcmp(format_name, *p)) {
return 1; return 1;
} }
} }
if (read_only) { if (read_only) {
for (p = whitelist_ro; *p; p++) { for (p = whitelist_ro; *p; p++) {
if (!strcmp(drv->format_name, *p)) { if (!strcmp(format_name, *p)) {
return 1; return 1;
} }
} }
@ -455,6 +455,11 @@ int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
return 0; return 0;
} }
int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
{
return bdrv_format_is_whitelisted(drv->format_name, read_only);
}
bool bdrv_uses_whitelist(void) bool bdrv_uses_whitelist(void)
{ {
return use_bdrv_whitelist; return use_bdrv_whitelist;
@ -4147,7 +4152,7 @@ static int qsort_strcmp(const void *a, const void *b)
} }
void bdrv_iterate_format(void (*it)(void *opaque, const char *name), void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
void *opaque) void *opaque, bool read_only)
{ {
BlockDriver *drv; BlockDriver *drv;
int count = 0; int count = 0;
@ -4158,6 +4163,11 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
if (drv->format_name) { if (drv->format_name) {
bool found = false; bool found = false;
int i = count; int i = count;
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, read_only)) {
continue;
}
while (formats && i && !found) { while (formats && i && !found) {
found = !strcmp(formats[--i], drv->format_name); found = !strcmp(formats[--i], drv->format_name);
} }
@ -4176,6 +4186,11 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
bool found = false; bool found = false;
int j = count; int j = count;
if (use_bdrv_whitelist &&
!bdrv_format_is_whitelisted(format_name, read_only)) {
continue;
}
while (formats && j && !found) { while (formats && j && !found) {
found = !strcmp(formats[--j], format_name); found = !strcmp(formats[--j], format_name);
} }

View File

@ -778,7 +778,8 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list,
* directory in-place (actually, turn-off the extension), which is checked * directory in-place (actually, turn-off the extension), which is checked
* in qcow2_check_metadata_overlap() */ * in qcow2_check_metadata_overlap() */
ret = qcow2_pre_write_overlap_check( ret = qcow2_pre_write_overlap_check(
bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size); bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size,
false);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
@ -1224,7 +1225,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,
memset(buf + write_size, 0, s->cluster_size - write_size); memset(buf + write_size, 0, s->cluster_size - write_size);
} }
ret = qcow2_pre_write_overlap_check(bs, 0, off, s->cluster_size); ret = qcow2_pre_write_overlap_check(bs, 0, off, s->cluster_size, false);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Qcow2 overlap check failed"); error_setg_errno(errp, -ret, "Qcow2 overlap check failed");
goto fail; goto fail;
@ -1292,7 +1293,7 @@ static int store_bitmap(BlockDriverState *bs, Qcow2Bitmap *bm, Error **errp)
} }
ret = qcow2_pre_write_overlap_check(bs, 0, tb_offset, ret = qcow2_pre_write_overlap_check(bs, 0, tb_offset,
tb_size * sizeof(tb[0])); tb_size * sizeof(tb[0]), false);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Qcow2 overlap check failed"); error_setg_errno(errp, -ret, "Qcow2 overlap check failed");
goto fail; goto fail;

View File

@ -205,13 +205,13 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
if (c == s->refcount_block_cache) { if (c == s->refcount_block_cache) {
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK, ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK,
c->entries[i].offset, c->table_size); c->entries[i].offset, c->table_size, false);
} else if (c == s->l2_table_cache) { } else if (c == s->l2_table_cache) {
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2, ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2,
c->entries[i].offset, c->table_size); c->entries[i].offset, c->table_size, false);
} else { } else {
ret = qcow2_pre_write_overlap_check(bs, 0, ret = qcow2_pre_write_overlap_check(bs, 0,
c->entries[i].offset, c->table_size); c->entries[i].offset, c->table_size, false);
} }
if (ret < 0) { if (ret < 0) {

View File

@ -153,7 +153,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
/* the L1 position has not yet been updated, so these clusters must /* the L1 position has not yet been updated, so these clusters must
* indeed be completely free */ * indeed be completely free */
ret = qcow2_pre_write_overlap_check(bs, 0, new_l1_table_offset, ret = qcow2_pre_write_overlap_check(bs, 0, new_l1_table_offset,
new_l1_size2); new_l1_size2, false);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
@ -238,7 +238,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
} }
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1, ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1,
s->l1_table_offset + 8 * l1_start_index, sizeof(buf)); s->l1_table_offset + 8 * l1_start_index, sizeof(buf), false);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
@ -380,8 +380,8 @@ fail:
* as contiguous. (This allows it, for example, to stop at the first compressed * as contiguous. (This allows it, for example, to stop at the first compressed
* cluster which may require a different handling) * cluster which may require a different handling)
*/ */
static int count_contiguous_clusters(int nb_clusters, int cluster_size, static int count_contiguous_clusters(BlockDriverState *bs, int nb_clusters,
uint64_t *l2_slice, uint64_t stop_flags) int cluster_size, uint64_t *l2_slice, uint64_t stop_flags)
{ {
int i; int i;
QCow2ClusterType first_cluster_type; QCow2ClusterType first_cluster_type;
@ -389,12 +389,12 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size,
uint64_t first_entry = be64_to_cpu(l2_slice[0]); uint64_t first_entry = be64_to_cpu(l2_slice[0]);
uint64_t offset = first_entry & mask; uint64_t offset = first_entry & mask;
if (!offset) { first_cluster_type = qcow2_get_cluster_type(bs, first_entry);
if (first_cluster_type == QCOW2_CLUSTER_UNALLOCATED) {
return 0; return 0;
} }
/* must be allocated */ /* must be allocated */
first_cluster_type = qcow2_get_cluster_type(first_entry);
assert(first_cluster_type == QCOW2_CLUSTER_NORMAL || assert(first_cluster_type == QCOW2_CLUSTER_NORMAL ||
first_cluster_type == QCOW2_CLUSTER_ZERO_ALLOC); first_cluster_type == QCOW2_CLUSTER_ZERO_ALLOC);
@ -412,7 +412,8 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size,
* Checks how many consecutive unallocated clusters in a given L2 * Checks how many consecutive unallocated clusters in a given L2
* slice have the same cluster type. * slice have the same cluster type.
*/ */
static int count_contiguous_clusters_unallocated(int nb_clusters, static int count_contiguous_clusters_unallocated(BlockDriverState *bs,
int nb_clusters,
uint64_t *l2_slice, uint64_t *l2_slice,
QCow2ClusterType wanted_type) QCow2ClusterType wanted_type)
{ {
@ -422,7 +423,7 @@ static int count_contiguous_clusters_unallocated(int nb_clusters,
wanted_type == QCOW2_CLUSTER_UNALLOCATED); wanted_type == QCOW2_CLUSTER_UNALLOCATED);
for (i = 0; i < nb_clusters; i++) { for (i = 0; i < nb_clusters; i++) {
uint64_t entry = be64_to_cpu(l2_slice[i]); uint64_t entry = be64_to_cpu(l2_slice[i]);
QCow2ClusterType type = qcow2_get_cluster_type(entry); QCow2ClusterType type = qcow2_get_cluster_type(bs, entry);
if (type != wanted_type) { if (type != wanted_type) {
break; break;
@ -489,6 +490,7 @@ static int coroutine_fn do_perform_cow_write(BlockDriverState *bs,
unsigned offset_in_cluster, unsigned offset_in_cluster,
QEMUIOVector *qiov) QEMUIOVector *qiov)
{ {
BDRVQcow2State *s = bs->opaque;
int ret; int ret;
if (qiov->size == 0) { if (qiov->size == 0) {
@ -496,13 +498,13 @@ static int coroutine_fn do_perform_cow_write(BlockDriverState *bs,
} }
ret = qcow2_pre_write_overlap_check(bs, 0, ret = qcow2_pre_write_overlap_check(bs, 0,
cluster_offset + offset_in_cluster, qiov->size); cluster_offset + offset_in_cluster, qiov->size, true);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE); BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE);
ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster, ret = bdrv_co_pwritev(s->data_file, cluster_offset + offset_in_cluster,
qiov->size, qiov, 0); qiov->size, qiov, 0);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
@ -595,7 +597,7 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
* true */ * true */
assert(nb_clusters <= INT_MAX); assert(nb_clusters <= INT_MAX);
type = qcow2_get_cluster_type(*cluster_offset); type = qcow2_get_cluster_type(bs, *cluster_offset);
if (s->qcow_version < 3 && (type == QCOW2_CLUSTER_ZERO_PLAIN || if (s->qcow_version < 3 && (type == QCOW2_CLUSTER_ZERO_PLAIN ||
type == QCOW2_CLUSTER_ZERO_ALLOC)) { type == QCOW2_CLUSTER_ZERO_ALLOC)) {
qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found" qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found"
@ -606,6 +608,14 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
} }
switch (type) { switch (type) {
case QCOW2_CLUSTER_COMPRESSED: case QCOW2_CLUSTER_COMPRESSED:
if (has_data_file(bs)) {
qcow2_signal_corruption(bs, true, -1, -1, "Compressed cluster "
"entry found in image with external data "
"file (L2 offset: %#" PRIx64 ", L2 index: "
"%#x)", l2_offset, l2_index);
ret = -EIO;
goto fail;
}
/* Compressed clusters can only be processed one by one */ /* Compressed clusters can only be processed one by one */
c = 1; c = 1;
*cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK; *cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK;
@ -613,14 +623,14 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
case QCOW2_CLUSTER_ZERO_PLAIN: case QCOW2_CLUSTER_ZERO_PLAIN:
case QCOW2_CLUSTER_UNALLOCATED: case QCOW2_CLUSTER_UNALLOCATED:
/* how many empty clusters ? */ /* how many empty clusters ? */
c = count_contiguous_clusters_unallocated(nb_clusters, c = count_contiguous_clusters_unallocated(bs, nb_clusters,
&l2_slice[l2_index], type); &l2_slice[l2_index], type);
*cluster_offset = 0; *cluster_offset = 0;
break; break;
case QCOW2_CLUSTER_ZERO_ALLOC: case QCOW2_CLUSTER_ZERO_ALLOC:
case QCOW2_CLUSTER_NORMAL: case QCOW2_CLUSTER_NORMAL:
/* how many allocated clusters ? */ /* how many allocated clusters ? */
c = count_contiguous_clusters(nb_clusters, s->cluster_size, c = count_contiguous_clusters(bs, nb_clusters, s->cluster_size,
&l2_slice[l2_index], QCOW_OFLAG_ZERO); &l2_slice[l2_index], QCOW_OFLAG_ZERO);
*cluster_offset &= L2E_OFFSET_MASK; *cluster_offset &= L2E_OFFSET_MASK;
if (offset_into_cluster(s, *cluster_offset)) { if (offset_into_cluster(s, *cluster_offset)) {
@ -632,6 +642,17 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
ret = -EIO; ret = -EIO;
goto fail; goto fail;
} }
if (has_data_file(bs) && *cluster_offset != offset - offset_in_cluster)
{
qcow2_signal_corruption(bs, true, -1, -1,
"External data file host cluster offset %#"
PRIx64 " does not match guest cluster "
"offset: %#" PRIx64
", L2 index: %#x)", *cluster_offset,
offset - offset_in_cluster, l2_index);
ret = -EIO;
goto fail;
}
break; break;
default: default:
abort(); abort();
@ -735,19 +756,16 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
/* /*
* alloc_compressed_cluster_offset * alloc_compressed_cluster_offset
* *
* For a given offset of the disk image, return cluster offset in * For a given offset on the virtual disk, allocate a new compressed cluster
* qcow2 file. * and put the host offset of the cluster into *host_offset. If a cluster is
* * already allocated at the offset, return an error.
* If the offset is not found, allocate a new compressed cluster.
*
* Return the cluster offset if successful,
* Return 0, otherwise.
* *
* Return 0 on success and -errno in error cases
*/ */
int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, uint64_t offset,
uint64_t offset, int compressed_size,
int compressed_size) uint64_t *host_offset)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
int l2_index, ret; int l2_index, ret;
@ -755,9 +773,13 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
int64_t cluster_offset; int64_t cluster_offset;
int nb_csectors; int nb_csectors;
if (has_data_file(bs)) {
return 0;
}
ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); ret = get_cluster_table(bs, offset, &l2_slice, &l2_index);
if (ret < 0) { if (ret < 0) {
return 0; return ret;
} }
/* Compression can't overwrite anything. Fail if the cluster was already /* Compression can't overwrite anything. Fail if the cluster was already
@ -765,13 +787,13 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
cluster_offset = be64_to_cpu(l2_slice[l2_index]); cluster_offset = be64_to_cpu(l2_slice[l2_index]);
if (cluster_offset & L2E_OFFSET_MASK) { if (cluster_offset & L2E_OFFSET_MASK) {
qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice);
return 0; return -EIO;
} }
cluster_offset = qcow2_alloc_bytes(bs, compressed_size); cluster_offset = qcow2_alloc_bytes(bs, compressed_size);
if (cluster_offset < 0) { if (cluster_offset < 0) {
qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice);
return 0; return cluster_offset;
} }
nb_csectors = ((cluster_offset + compressed_size - 1) >> 9) - nb_csectors = ((cluster_offset + compressed_size - 1) >> 9) -
@ -789,7 +811,8 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
l2_slice[l2_index] = cpu_to_be64(cluster_offset); l2_slice[l2_index] = cpu_to_be64(cluster_offset);
qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice);
return cluster_offset; *host_offset = cluster_offset & s->cluster_offset_mask;
return 0;
} }
static int perform_cow(BlockDriverState *bs, QCowL2Meta *m) static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
@ -1013,14 +1036,14 @@ void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m)
* write, but require COW to be performed (this includes yet unallocated space, * write, but require COW to be performed (this includes yet unallocated space,
* which must copy from the backing file) * which must copy from the backing file)
*/ */
static int count_cow_clusters(BDRVQcow2State *s, int nb_clusters, static int count_cow_clusters(BlockDriverState *bs, int nb_clusters,
uint64_t *l2_slice, int l2_index) uint64_t *l2_slice, int l2_index)
{ {
int i; int i;
for (i = 0; i < nb_clusters; i++) { for (i = 0; i < nb_clusters; i++) {
uint64_t l2_entry = be64_to_cpu(l2_slice[l2_index + i]); uint64_t l2_entry = be64_to_cpu(l2_slice[l2_index + i]);
QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry); QCow2ClusterType cluster_type = qcow2_get_cluster_type(bs, l2_entry);
switch(cluster_type) { switch(cluster_type) {
case QCOW2_CLUSTER_NORMAL: case QCOW2_CLUSTER_NORMAL:
@ -1108,9 +1131,9 @@ static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset,
/* /*
* Checks how many already allocated clusters that don't require a copy on * Checks how many already allocated clusters that don't require a copy on
* write there are at the given guest_offset (up to *bytes). If * write there are at the given guest_offset (up to *bytes). If *host_offset is
* *host_offset is not zero, only physically contiguous clusters beginning at * not INV_OFFSET, only physically contiguous clusters beginning at this host
* this host offset are counted. * offset are counted.
* *
* Note that guest_offset may not be cluster aligned. In this case, the * Note that guest_offset may not be cluster aligned. In this case, the
* returned *host_offset points to exact byte referenced by guest_offset and * returned *host_offset points to exact byte referenced by guest_offset and
@ -1142,8 +1165,8 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset,
trace_qcow2_handle_copied(qemu_coroutine_self(), guest_offset, *host_offset, trace_qcow2_handle_copied(qemu_coroutine_self(), guest_offset, *host_offset,
*bytes); *bytes);
assert(*host_offset == 0 || offset_into_cluster(s, guest_offset) assert(*host_offset == INV_OFFSET || offset_into_cluster(s, guest_offset)
== offset_into_cluster(s, *host_offset)); == offset_into_cluster(s, *host_offset));
/* /*
* Calculate the number of clusters to look for. We stop at L2 slice * Calculate the number of clusters to look for. We stop at L2 slice
@ -1165,7 +1188,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset,
cluster_offset = be64_to_cpu(l2_slice[l2_index]); cluster_offset = be64_to_cpu(l2_slice[l2_index]);
/* Check how many clusters are already allocated and don't need COW */ /* Check how many clusters are already allocated and don't need COW */
if (qcow2_get_cluster_type(cluster_offset) == QCOW2_CLUSTER_NORMAL if (qcow2_get_cluster_type(bs, cluster_offset) == QCOW2_CLUSTER_NORMAL
&& (cluster_offset & QCOW_OFLAG_COPIED)) && (cluster_offset & QCOW_OFLAG_COPIED))
{ {
/* If a specific host_offset is required, check it */ /* If a specific host_offset is required, check it */
@ -1181,7 +1204,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset,
goto out; goto out;
} }
if (*host_offset != 0 && !offset_matches) { if (*host_offset != INV_OFFSET && !offset_matches) {
*bytes = 0; *bytes = 0;
ret = 0; ret = 0;
goto out; goto out;
@ -1189,7 +1212,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset,
/* We keep all QCOW_OFLAG_COPIED clusters */ /* We keep all QCOW_OFLAG_COPIED clusters */
keep_clusters = keep_clusters =
count_contiguous_clusters(nb_clusters, s->cluster_size, count_contiguous_clusters(bs, nb_clusters, s->cluster_size,
&l2_slice[l2_index], &l2_slice[l2_index],
QCOW_OFLAG_COPIED | QCOW_OFLAG_ZERO); QCOW_OFLAG_COPIED | QCOW_OFLAG_ZERO);
assert(keep_clusters <= nb_clusters); assert(keep_clusters <= nb_clusters);
@ -1224,10 +1247,10 @@ out:
* contain the number of clusters that have been allocated and are contiguous * contain the number of clusters that have been allocated and are contiguous
* in the image file. * in the image file.
* *
* If *host_offset is non-zero, it specifies the offset in the image file at * If *host_offset is not INV_OFFSET, it specifies the offset in the image file
* which the new clusters must start. *nb_clusters can be 0 on return in this * at which the new clusters must start. *nb_clusters can be 0 on return in
* case if the cluster at host_offset is already in use. If *host_offset is * this case if the cluster at host_offset is already in use. If *host_offset
* zero, the clusters can be allocated anywhere in the image file. * is INV_OFFSET, the clusters can be allocated anywhere in the image file.
* *
* *host_offset is updated to contain the offset into the image file at which * *host_offset is updated to contain the offset into the image file at which
* the first allocated cluster starts. * the first allocated cluster starts.
@ -1244,9 +1267,16 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset, trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset,
*host_offset, *nb_clusters); *host_offset, *nb_clusters);
if (has_data_file(bs)) {
assert(*host_offset == INV_OFFSET ||
*host_offset == start_of_cluster(s, guest_offset));
*host_offset = start_of_cluster(s, guest_offset);
return 0;
}
/* Allocate new clusters */ /* Allocate new clusters */
trace_qcow2_cluster_alloc_phys(qemu_coroutine_self()); trace_qcow2_cluster_alloc_phys(qemu_coroutine_self());
if (*host_offset == 0) { if (*host_offset == INV_OFFSET) {
int64_t cluster_offset = int64_t cluster_offset =
qcow2_alloc_clusters(bs, *nb_clusters * s->cluster_size); qcow2_alloc_clusters(bs, *nb_clusters * s->cluster_size);
if (cluster_offset < 0) { if (cluster_offset < 0) {
@ -1266,8 +1296,8 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
/* /*
* Allocates new clusters for an area that either is yet unallocated or needs a * Allocates new clusters for an area that either is yet unallocated or needs a
* copy on write. If *host_offset is non-zero, clusters are only allocated if * copy on write. If *host_offset is not INV_OFFSET, clusters are only
* the new allocation can match the specified host offset. * allocated if the new allocation can match the specified host offset.
* *
* Note that guest_offset may not be cluster aligned. In this case, the * Note that guest_offset may not be cluster aligned. In this case, the
* returned *host_offset points to exact byte referenced by guest_offset and * returned *host_offset points to exact byte referenced by guest_offset and
@ -1295,7 +1325,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
int ret; int ret;
bool keep_old_clusters = false; bool keep_old_clusters = false;
uint64_t alloc_cluster_offset = 0; uint64_t alloc_cluster_offset = INV_OFFSET;
trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset, trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset,
*bytes); *bytes);
@ -1324,7 +1354,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
if (entry & QCOW_OFLAG_COMPRESSED) { if (entry & QCOW_OFLAG_COMPRESSED) {
nb_clusters = 1; nb_clusters = 1;
} else { } else {
nb_clusters = count_cow_clusters(s, nb_clusters, l2_slice, l2_index); nb_clusters = count_cow_clusters(bs, nb_clusters, l2_slice, l2_index);
} }
/* This function is only called when there were no non-COW clusters, so if /* This function is only called when there were no non-COW clusters, so if
@ -1332,9 +1362,9 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
* wrong with our code. */ * wrong with our code. */
assert(nb_clusters > 0); assert(nb_clusters > 0);
if (qcow2_get_cluster_type(entry) == QCOW2_CLUSTER_ZERO_ALLOC && if (qcow2_get_cluster_type(bs, entry) == QCOW2_CLUSTER_ZERO_ALLOC &&
(entry & QCOW_OFLAG_COPIED) && (entry & QCOW_OFLAG_COPIED) &&
(!*host_offset || (*host_offset == INV_OFFSET ||
start_of_cluster(s, *host_offset) == (entry & L2E_OFFSET_MASK))) start_of_cluster(s, *host_offset) == (entry & L2E_OFFSET_MASK)))
{ {
int preallocated_nb_clusters; int preallocated_nb_clusters;
@ -1352,7 +1382,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
* would be fine, too, but count_cow_clusters() above has limited * would be fine, too, but count_cow_clusters() above has limited
* nb_clusters already to a range of COW clusters */ * nb_clusters already to a range of COW clusters */
preallocated_nb_clusters = preallocated_nb_clusters =
count_contiguous_clusters(nb_clusters, s->cluster_size, count_contiguous_clusters(bs, nb_clusters, s->cluster_size,
&l2_slice[l2_index], QCOW_OFLAG_COPIED); &l2_slice[l2_index], QCOW_OFLAG_COPIED);
assert(preallocated_nb_clusters > 0); assert(preallocated_nb_clusters > 0);
@ -1366,9 +1396,10 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice);
if (!alloc_cluster_offset) { if (alloc_cluster_offset == INV_OFFSET) {
/* Allocate, if necessary at a given offset in the image file */ /* Allocate, if necessary at a given offset in the image file */
alloc_cluster_offset = start_of_cluster(s, *host_offset); alloc_cluster_offset = *host_offset == INV_OFFSET ? INV_OFFSET :
start_of_cluster(s, *host_offset);
ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset, ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset,
&nb_clusters); &nb_clusters);
if (ret < 0) { if (ret < 0) {
@ -1381,16 +1412,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
return 0; return 0;
} }
/* !*host_offset would overwrite the image header and is reserved for assert(alloc_cluster_offset != INV_OFFSET);
* "no host offset preferred". If 0 was a valid host offset, it'd
* trigger the following overlap check; do that now to avoid having an
* invalid value in *host_offset. */
if (!alloc_cluster_offset) {
ret = qcow2_pre_write_overlap_check(bs, 0, alloc_cluster_offset,
nb_clusters * s->cluster_size);
assert(ret < 0);
goto fail;
}
} }
/* /*
@ -1482,14 +1504,14 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
again: again:
start = offset; start = offset;
remaining = *bytes; remaining = *bytes;
cluster_offset = 0; cluster_offset = INV_OFFSET;
*host_offset = 0; *host_offset = INV_OFFSET;
cur_bytes = 0; cur_bytes = 0;
*m = NULL; *m = NULL;
while (true) { while (true) {
if (!*host_offset) { if (*host_offset == INV_OFFSET && cluster_offset != INV_OFFSET) {
*host_offset = start_of_cluster(s, cluster_offset); *host_offset = start_of_cluster(s, cluster_offset);
} }
@ -1497,7 +1519,10 @@ again:
start += cur_bytes; start += cur_bytes;
remaining -= cur_bytes; remaining -= cur_bytes;
cluster_offset += cur_bytes;
if (cluster_offset != INV_OFFSET) {
cluster_offset += cur_bytes;
}
if (remaining == 0) { if (remaining == 0) {
break; break;
@ -1569,7 +1594,7 @@ again:
*bytes -= remaining; *bytes -= remaining;
assert(*bytes > 0); assert(*bytes > 0);
assert(*host_offset != 0); assert(*host_offset != INV_OFFSET);
return 0; return 0;
} }
@ -1616,7 +1641,7 @@ static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset,
* If full_discard is true, the sector should not read back as zeroes, * If full_discard is true, the sector should not read back as zeroes,
* but rather fall through to the backing file. * but rather fall through to the backing file.
*/ */
switch (qcow2_get_cluster_type(old_l2_entry)) { switch (qcow2_get_cluster_type(bs, old_l2_entry)) {
case QCOW2_CLUSTER_UNALLOCATED: case QCOW2_CLUSTER_UNALLOCATED:
if (full_discard || !bs->backing) { if (full_discard || !bs->backing) {
continue; continue;
@ -1729,7 +1754,7 @@ static int zero_in_l2_slice(BlockDriverState *bs, uint64_t offset,
* Minimize L2 changes if the cluster already reads back as * Minimize L2 changes if the cluster already reads back as
* zeroes with correct allocation. * zeroes with correct allocation.
*/ */
cluster_type = qcow2_get_cluster_type(old_offset); cluster_type = qcow2_get_cluster_type(bs, old_offset);
if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN || if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN ||
(cluster_type == QCOW2_CLUSTER_ZERO_ALLOC && !unmap)) { (cluster_type == QCOW2_CLUSTER_ZERO_ALLOC && !unmap)) {
continue; continue;
@ -1758,6 +1783,16 @@ int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset,
int64_t cleared; int64_t cleared;
int ret; int ret;
/* If we have to stay in sync with an external data file, zero out
* s->data_file first. */
if (data_file_is_raw(bs)) {
assert(has_data_file(bs));
ret = bdrv_co_pwrite_zeroes(s->data_file, offset, bytes, flags);
if (ret < 0) {
return ret;
}
}
/* Caller must pass aligned values, except at image end */ /* Caller must pass aligned values, except at image end */
assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) || assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) ||
@ -1871,7 +1906,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
uint64_t l2_entry = be64_to_cpu(l2_slice[j]); uint64_t l2_entry = be64_to_cpu(l2_slice[j]);
int64_t offset = l2_entry & L2E_OFFSET_MASK; int64_t offset = l2_entry & L2E_OFFSET_MASK;
QCow2ClusterType cluster_type = QCow2ClusterType cluster_type =
qcow2_get_cluster_type(l2_entry); qcow2_get_cluster_type(bs, l2_entry);
if (cluster_type != QCOW2_CLUSTER_ZERO_PLAIN && if (cluster_type != QCOW2_CLUSTER_ZERO_PLAIN &&
cluster_type != QCOW2_CLUSTER_ZERO_ALLOC) { cluster_type != QCOW2_CLUSTER_ZERO_ALLOC) {
@ -1925,7 +1960,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
} }
ret = qcow2_pre_write_overlap_check(bs, 0, offset, ret = qcow2_pre_write_overlap_check(bs, 0, offset,
s->cluster_size); s->cluster_size, true);
if (ret < 0) { if (ret < 0) {
if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) {
qcow2_free_clusters(bs, offset, s->cluster_size, qcow2_free_clusters(bs, offset, s->cluster_size,
@ -1934,7 +1969,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
goto fail; goto fail;
} }
ret = bdrv_pwrite_zeroes(bs->file, offset, s->cluster_size, 0); ret = bdrv_pwrite_zeroes(s->data_file, offset,
s->cluster_size, 0);
if (ret < 0) { if (ret < 0) {
if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) {
qcow2_free_clusters(bs, offset, s->cluster_size, qcow2_free_clusters(bs, offset, s->cluster_size,
@ -1961,7 +1997,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
if (l2_dirty) { if (l2_dirty) {
ret = qcow2_pre_write_overlap_check( ret = qcow2_pre_write_overlap_check(
bs, QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2, bs, QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2,
slice_offset, slice_size2); slice_offset, slice_size2, false);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }

View File

@ -1156,8 +1156,20 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
int nb_clusters, enum qcow2_discard_type type) int nb_clusters, enum qcow2_discard_type type)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
QCow2ClusterType ctype = qcow2_get_cluster_type(bs, l2_entry);
switch (qcow2_get_cluster_type(l2_entry)) { if (has_data_file(bs)) {
if (s->discard_passthrough[type] &&
(ctype == QCOW2_CLUSTER_NORMAL ||
ctype == QCOW2_CLUSTER_ZERO_ALLOC))
{
bdrv_pdiscard(s->data_file, l2_entry & L2E_OFFSET_MASK,
nb_clusters << s->cluster_bits);
}
return;
}
switch (ctype) {
case QCOW2_CLUSTER_COMPRESSED: case QCOW2_CLUSTER_COMPRESSED:
{ {
int nb_csectors; int nb_csectors;
@ -1300,7 +1312,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
entry &= ~QCOW_OFLAG_COPIED; entry &= ~QCOW_OFLAG_COPIED;
offset = entry & L2E_OFFSET_MASK; offset = entry & L2E_OFFSET_MASK;
switch (qcow2_get_cluster_type(entry)) { switch (qcow2_get_cluster_type(bs, entry)) {
case QCOW2_CLUSTER_COMPRESSED: case QCOW2_CLUSTER_COMPRESSED:
nb_csectors = ((entry >> s->csize_shift) & nb_csectors = ((entry >> s->csize_shift) &
s->csize_mask) + 1; s->csize_mask) + 1;
@ -1582,7 +1594,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
for(i = 0; i < s->l2_size; i++) { for(i = 0; i < s->l2_size; i++) {
l2_entry = be64_to_cpu(l2_table[i]); l2_entry = be64_to_cpu(l2_table[i]);
switch (qcow2_get_cluster_type(l2_entry)) { switch (qcow2_get_cluster_type(bs, l2_entry)) {
case QCOW2_CLUSTER_COMPRESSED: case QCOW2_CLUSTER_COMPRESSED:
/* Compressed clusters don't have QCOW_OFLAG_COPIED */ /* Compressed clusters don't have QCOW_OFLAG_COPIED */
if (l2_entry & QCOW_OFLAG_COPIED) { if (l2_entry & QCOW_OFLAG_COPIED) {
@ -1593,6 +1605,13 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
res->corruptions++; res->corruptions++;
} }
if (has_data_file(bs)) {
fprintf(stderr, "ERROR compressed cluster %d with data file, "
"entry=0x%" PRIx64 "\n", i, l2_entry);
res->corruptions++;
break;
}
/* Mark cluster as used */ /* Mark cluster as used */
nb_csectors = ((l2_entry >> s->csize_shift) & nb_csectors = ((l2_entry >> s->csize_shift) &
s->csize_mask) + 1; s->csize_mask) + 1;
@ -1633,7 +1652,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
/* Correct offsets are cluster aligned */ /* Correct offsets are cluster aligned */
if (offset_into_cluster(s, offset)) { if (offset_into_cluster(s, offset)) {
if (qcow2_get_cluster_type(l2_entry) == if (qcow2_get_cluster_type(bs, l2_entry) ==
QCOW2_CLUSTER_ZERO_ALLOC) QCOW2_CLUSTER_ZERO_ALLOC)
{ {
fprintf(stderr, "%s offset=%" PRIx64 ": Preallocated zero " fprintf(stderr, "%s offset=%" PRIx64 ": Preallocated zero "
@ -1649,7 +1668,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
l2_table[i] = cpu_to_be64(l2_entry); l2_table[i] = cpu_to_be64(l2_entry);
ret = qcow2_pre_write_overlap_check(bs, ret = qcow2_pre_write_overlap_check(bs,
QCOW2_OL_ACTIVE_L2 | QCOW2_OL_INACTIVE_L2, QCOW2_OL_ACTIVE_L2 | QCOW2_OL_INACTIVE_L2,
l2e_offset, sizeof(uint64_t)); l2e_offset, sizeof(uint64_t), false);
if (ret < 0) { if (ret < 0) {
fprintf(stderr, "ERROR: Overlap check failed\n"); fprintf(stderr, "ERROR: Overlap check failed\n");
res->check_errors++; res->check_errors++;
@ -1683,11 +1702,13 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
} }
/* Mark cluster as used */ /* Mark cluster as used */
ret = qcow2_inc_refcounts_imrt(bs, res, if (!has_data_file(bs)) {
refcount_table, refcount_table_size, ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table,
offset, s->cluster_size); refcount_table_size,
if (ret < 0) { offset, s->cluster_size);
goto fail; if (ret < 0) {
goto fail;
}
} }
break; break;
} }
@ -1868,16 +1889,20 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
for (j = 0; j < s->l2_size; j++) { for (j = 0; j < s->l2_size; j++) {
uint64_t l2_entry = be64_to_cpu(l2_table[j]); uint64_t l2_entry = be64_to_cpu(l2_table[j]);
uint64_t data_offset = l2_entry & L2E_OFFSET_MASK; uint64_t data_offset = l2_entry & L2E_OFFSET_MASK;
QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry); QCow2ClusterType cluster_type = qcow2_get_cluster_type(bs, l2_entry);
if (cluster_type == QCOW2_CLUSTER_NORMAL || if (cluster_type == QCOW2_CLUSTER_NORMAL ||
cluster_type == QCOW2_CLUSTER_ZERO_ALLOC) { cluster_type == QCOW2_CLUSTER_ZERO_ALLOC) {
ret = qcow2_get_refcount(bs, if (has_data_file(bs)) {
data_offset >> s->cluster_bits, refcount = 1;
&refcount); } else {
if (ret < 0) { ret = qcow2_get_refcount(bs,
/* don't print message nor increment check_errors */ data_offset >> s->cluster_bits,
continue; &refcount);
if (ret < 0) {
/* don't print message nor increment check_errors */
continue;
}
} }
if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) {
fprintf(stderr, "%s OFLAG_COPIED data cluster: " fprintf(stderr, "%s OFLAG_COPIED data cluster: "
@ -1898,7 +1923,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
if (l2_dirty) { if (l2_dirty) {
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2, ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2,
l2_offset, s->cluster_size); l2_offset, s->cluster_size,
false);
if (ret < 0) { if (ret < 0) {
fprintf(stderr, "ERROR: Could not write L2 table; metadata " fprintf(stderr, "ERROR: Could not write L2 table; metadata "
"overlap check failed: %s\n", strerror(-ret)); "overlap check failed: %s\n", strerror(-ret));
@ -2070,6 +2096,12 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
} }
/* snapshots */ /* snapshots */
if (has_data_file(bs) && s->nb_snapshots) {
fprintf(stderr, "ERROR %d snapshots in image with data file\n",
s->nb_snapshots);
res->corruptions++;
}
for (i = 0; i < s->nb_snapshots; i++) { for (i = 0; i < s->nb_snapshots; i++) {
sn = s->snapshots + i; sn = s->snapshots + i;
if (offset_into_cluster(s, sn->l1_table_offset)) { if (offset_into_cluster(s, sn->l1_table_offset)) {
@ -2366,7 +2398,7 @@ write_refblocks:
} }
ret = qcow2_pre_write_overlap_check(bs, 0, refblock_offset, ret = qcow2_pre_write_overlap_check(bs, 0, refblock_offset,
s->cluster_size); s->cluster_size, false);
if (ret < 0) { if (ret < 0) {
fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret)); fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret));
goto fail; goto fail;
@ -2417,7 +2449,8 @@ write_refblocks:
} }
ret = qcow2_pre_write_overlap_check(bs, 0, reftable_offset, ret = qcow2_pre_write_overlap_check(bs, 0, reftable_offset,
reftable_size * sizeof(uint64_t)); reftable_size * sizeof(uint64_t),
false);
if (ret < 0) { if (ret < 0) {
fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret)); fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret));
goto fail; goto fail;
@ -2751,10 +2784,15 @@ QEMU_BUILD_BUG_ON(QCOW2_OL_MAX_BITNR != ARRAY_SIZE(metadata_ol_names));
* overlaps; or a negative value (-errno) on error. * overlaps; or a negative value (-errno) on error.
*/ */
int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
int64_t size) int64_t size, bool data_file)
{ {
int ret = qcow2_check_metadata_overlap(bs, ign, offset, size); int ret;
if (data_file && has_data_file(bs)) {
return 0;
}
ret = qcow2_check_metadata_overlap(bs, ign, offset, size);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} else if (ret > 0) { } else if (ret > 0) {
@ -2855,7 +2893,8 @@ static int flush_refblock(BlockDriverState *bs, uint64_t **reftable,
if (reftable_index < *reftable_size && (*reftable)[reftable_index]) { if (reftable_index < *reftable_size && (*reftable)[reftable_index]) {
offset = (*reftable)[reftable_index]; offset = (*reftable)[reftable_index];
ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size); ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size,
false);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Overlap check failed"); error_setg_errno(errp, -ret, "Overlap check failed");
return ret; return ret;
@ -3121,7 +3160,8 @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order,
/* Write the new reftable */ /* Write the new reftable */
ret = qcow2_pre_write_overlap_check(bs, 0, new_reftable_offset, ret = qcow2_pre_write_overlap_check(bs, 0, new_reftable_offset,
new_reftable_size * sizeof(uint64_t)); new_reftable_size * sizeof(uint64_t),
false);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Overlap check failed"); error_setg_errno(errp, -ret, "Overlap check failed");
goto done; goto done;

View File

@ -184,7 +184,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
/* The snapshot list position has not yet been updated, so these clusters /* The snapshot list position has not yet been updated, so these clusters
* must indeed be completely free */ * must indeed be completely free */
ret = qcow2_pre_write_overlap_check(bs, 0, offset, snapshots_size); ret = qcow2_pre_write_overlap_check(bs, 0, offset, snapshots_size, false);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
@ -353,6 +353,10 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
return -EFBIG; return -EFBIG;
} }
if (has_data_file(bs)) {
return -ENOTSUP;
}
memset(sn, 0, sizeof(*sn)); memset(sn, 0, sizeof(*sn));
/* Generate an ID */ /* Generate an ID */
@ -389,7 +393,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
} }
ret = qcow2_pre_write_overlap_check(bs, 0, sn->l1_table_offset, ret = qcow2_pre_write_overlap_check(bs, 0, sn->l1_table_offset,
s->l1_size * sizeof(uint64_t)); s->l1_size * sizeof(uint64_t), false);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
@ -466,6 +470,10 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
int ret; int ret;
uint64_t *sn_l1_table = NULL; uint64_t *sn_l1_table = NULL;
if (has_data_file(bs)) {
return -ENOTSUP;
}
/* Search the snapshot */ /* Search the snapshot */
snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id); snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
if (snapshot_index < 0) { if (snapshot_index < 0) {
@ -528,7 +536,8 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
} }
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1, ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1,
s->l1_table_offset, cur_l1_bytes); s->l1_table_offset, cur_l1_bytes,
false);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
@ -598,6 +607,10 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
QCowSnapshot sn; QCowSnapshot sn;
int snapshot_index, ret; int snapshot_index, ret;
if (has_data_file(bs)) {
return -ENOTSUP;
}
/* Search the snapshot */ /* Search the snapshot */
snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name); snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name);
if (snapshot_index < 0) { if (snapshot_index < 0) {
@ -669,6 +682,9 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
QCowSnapshot *sn; QCowSnapshot *sn;
int i; int i;
if (has_data_file(bs)) {
return -ENOTSUP;
}
if (!s->nb_snapshots) { if (!s->nb_snapshots) {
*psn_tab = NULL; *psn_tab = NULL;
return s->nb_snapshots; return s->nb_snapshots;

View File

@ -73,6 +73,7 @@ typedef struct {
#define QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857 #define QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857
#define QCOW2_EXT_MAGIC_CRYPTO_HEADER 0x0537be77 #define QCOW2_EXT_MAGIC_CRYPTO_HEADER 0x0537be77
#define QCOW2_EXT_MAGIC_BITMAPS 0x23852875 #define QCOW2_EXT_MAGIC_BITMAPS 0x23852875
#define QCOW2_EXT_MAGIC_DATA_FILE 0x44415441
static int coroutine_fn static int coroutine_fn
qcow2_co_preadv_compressed(BlockDriverState *bs, qcow2_co_preadv_compressed(BlockDriverState *bs,
@ -139,7 +140,7 @@ static ssize_t qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen,
/* Zero fill remaining space in cluster so it has predictable /* Zero fill remaining space in cluster so it has predictable
* content in case of future spec changes */ * content in case of future spec changes */
clusterlen = size_to_clusters(s, headerlen) * s->cluster_size; clusterlen = size_to_clusters(s, headerlen) * s->cluster_size;
assert(qcow2_pre_write_overlap_check(bs, 0, ret, clusterlen) == 0); assert(qcow2_pre_write_overlap_check(bs, 0, ret, clusterlen, false) == 0);
ret = bdrv_pwrite_zeroes(bs->file, ret = bdrv_pwrite_zeroes(bs->file,
ret + headerlen, ret + headerlen,
clusterlen - headerlen, 0); clusterlen - headerlen, 0);
@ -397,6 +398,21 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
#endif #endif
break; break;
case QCOW2_EXT_MAGIC_DATA_FILE:
{
s->image_data_file = g_malloc0(ext.len + 1);
ret = bdrv_pread(bs->file, offset, s->image_data_file, ext.len);
if (ret < 0) {
error_setg_errno(errp, -ret,
"ERROR: Could not read data file name");
return ret;
}
#ifdef DEBUG_EXT
printf("Qcow2: Got external data file %s\n", s->image_data_file);
#endif
break;
}
default: default:
/* unknown magic - save it in case we need to rewrite the header */ /* unknown magic - save it in case we need to rewrite the header */
/* If you add a new feature, make sure to also update the fast /* If you add a new feature, make sure to also update the fast
@ -788,6 +804,7 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
uint64_t combined_cache_size, l2_cache_max_setting; uint64_t combined_cache_size, l2_cache_max_setting;
bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set; bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set;
bool l2_cache_entry_size_set;
int min_refcount_cache = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; int min_refcount_cache = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size;
uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE;
uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8); uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8);
@ -795,6 +812,7 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
combined_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_CACHE_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); l2_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_L2_CACHE_SIZE);
refcount_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_REFCOUNT_CACHE_SIZE); refcount_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_REFCOUNT_CACHE_SIZE);
l2_cache_entry_size_set = qemu_opt_get(opts, QCOW2_OPT_L2_CACHE_ENTRY_SIZE);
combined_cache_size = qemu_opt_get_size(opts, QCOW2_OPT_CACHE_SIZE, 0); combined_cache_size = qemu_opt_get_size(opts, QCOW2_OPT_CACHE_SIZE, 0);
l2_cache_max_setting = qemu_opt_get_size(opts, QCOW2_OPT_L2_CACHE_SIZE, l2_cache_max_setting = qemu_opt_get_size(opts, QCOW2_OPT_L2_CACHE_SIZE,
@ -841,6 +859,16 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
} }
} }
} }
/*
* If the L2 cache is not enough to cover the whole disk then
* default to 4KB entries. Smaller entries reduce the cost of
* loads and evictions and increase I/O performance.
*/
if (*l2_cache_size < max_l2_cache && !l2_cache_entry_size_set) {
*l2_cache_entry_size = MIN(s->cluster_size, 4096);
}
/* l2_cache_size and refcount_cache_size are ensured to have at least /* l2_cache_size and refcount_cache_size are ensured to have at least
* their minimum values in qcow2_update_options_prepare() */ * their minimum values in qcow2_update_options_prepare() */
@ -1440,6 +1468,47 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
goto fail; goto fail;
} }
/* Open external data file */
s->data_file = bdrv_open_child(NULL, options, "data-file", bs, &child_file,
true, &local_err);
if (local_err) {
error_propagate(errp, local_err);
ret = -EINVAL;
goto fail;
}
if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) {
if (!s->data_file && s->image_data_file) {
s->data_file = bdrv_open_child(s->image_data_file, options,
"data-file", bs, &child_file,
false, errp);
if (!s->data_file) {
ret = -EINVAL;
goto fail;
}
}
if (!s->data_file) {
error_setg(errp, "'data-file' is required for this image");
ret = -EINVAL;
goto fail;
}
} else {
if (s->data_file) {
error_setg(errp, "'data-file' can only be set for images with an "
"external data file");
ret = -EINVAL;
goto fail;
}
s->data_file = bs->file;
if (data_file_is_raw(bs)) {
error_setg(errp, "data-file-raw requires a data file");
ret = -EINVAL;
goto fail;
}
}
/* qcow2_read_extension may have set up the crypto context /* qcow2_read_extension may have set up the crypto context
* if the crypt method needs a header region, some methods * if the crypt method needs a header region, some methods
* don't need header extensions, so must check here * don't need header extensions, so must check here
@ -1611,6 +1680,10 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
return ret; return ret;
fail: fail:
g_free(s->image_data_file);
if (has_data_file(bs)) {
bdrv_unref_child(bs, s->data_file);
}
g_free(s->unknown_header_fields); g_free(s->unknown_header_fields);
cleanup_unknown_header_ext(bs); cleanup_unknown_header_ext(bs);
qcow2_free_snapshots(bs); qcow2_free_snapshots(bs);
@ -1813,11 +1886,11 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs,
*pnum = bytes; *pnum = bytes;
if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED && if ((ret == QCOW2_CLUSTER_NORMAL || ret == QCOW2_CLUSTER_ZERO_ALLOC) &&
!s->crypto) { !s->crypto) {
index_in_cluster = offset & (s->cluster_size - 1); index_in_cluster = offset & (s->cluster_size - 1);
*map = cluster_offset | index_in_cluster; *map = cluster_offset | index_in_cluster;
*file = bs->file->bs; *file = s->data_file->bs;
status |= BDRV_BLOCK_OFFSET_VALID; status |= BDRV_BLOCK_OFFSET_VALID;
} }
if (ret == QCOW2_CLUSTER_ZERO_PLAIN || ret == QCOW2_CLUSTER_ZERO_ALLOC) { if (ret == QCOW2_CLUSTER_ZERO_PLAIN || ret == QCOW2_CLUSTER_ZERO_ALLOC) {
@ -1949,7 +2022,7 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
*/ */
if (!cluster_data) { if (!cluster_data) {
cluster_data = cluster_data =
qemu_try_blockalign(bs->file->bs, qemu_try_blockalign(s->data_file->bs,
QCOW_MAX_CRYPT_CLUSTERS QCOW_MAX_CRYPT_CLUSTERS
* s->cluster_size); * s->cluster_size);
if (cluster_data == NULL) { if (cluster_data == NULL) {
@ -1965,7 +2038,7 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
ret = bdrv_co_preadv(bs->file, ret = bdrv_co_preadv(s->data_file,
cluster_offset + offset_in_cluster, cluster_offset + offset_in_cluster,
cur_bytes, &hd_qiov, 0); cur_bytes, &hd_qiov, 0);
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
@ -2124,7 +2197,7 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
} }
ret = qcow2_pre_write_overlap_check(bs, 0, ret = qcow2_pre_write_overlap_check(bs, 0,
cluster_offset + offset_in_cluster, cur_bytes); cluster_offset + offset_in_cluster, cur_bytes, true);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
@ -2138,7 +2211,7 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
trace_qcow2_writev_data(qemu_coroutine_self(), trace_qcow2_writev_data(qemu_coroutine_self(),
cluster_offset + offset_in_cluster); cluster_offset + offset_in_cluster);
ret = bdrv_co_pwritev(bs->file, ret = bdrv_co_pwritev(s->data_file,
cluster_offset + offset_in_cluster, cluster_offset + offset_in_cluster,
cur_bytes, &hd_qiov, 0); cur_bytes, &hd_qiov, 0);
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
@ -2227,9 +2300,14 @@ static void qcow2_close(BlockDriverState *bs)
g_free(s->unknown_header_fields); g_free(s->unknown_header_fields);
cleanup_unknown_header_ext(bs); cleanup_unknown_header_ext(bs);
g_free(s->image_data_file);
g_free(s->image_backing_file); g_free(s->image_backing_file);
g_free(s->image_backing_format); g_free(s->image_backing_format);
if (has_data_file(bs)) {
bdrv_unref_child(bs, s->data_file);
}
qcow2_refcount_close(bs); qcow2_refcount_close(bs);
qcow2_free_snapshots(bs); qcow2_free_snapshots(bs);
} }
@ -2399,6 +2477,19 @@ int qcow2_update_header(BlockDriverState *bs)
buflen -= ret; buflen -= ret;
} }
/* External data file header extension */
if (has_data_file(bs) && s->image_data_file) {
ret = header_ext_add(buf, QCOW2_EXT_MAGIC_DATA_FILE,
s->image_data_file, strlen(s->image_data_file),
buflen);
if (ret < 0) {
goto fail;
}
buf += ret;
buflen -= ret;
}
/* Full disk encryption header pointer extension */ /* Full disk encryption header pointer extension */
if (s->crypto_header.offset != 0) { if (s->crypto_header.offset != 0) {
s->crypto_header.offset = cpu_to_be64(s->crypto_header.offset); s->crypto_header.offset = cpu_to_be64(s->crypto_header.offset);
@ -2428,6 +2519,11 @@ int qcow2_update_header(BlockDriverState *bs)
.bit = QCOW2_INCOMPAT_CORRUPT_BITNR, .bit = QCOW2_INCOMPAT_CORRUPT_BITNR,
.name = "corrupt bit", .name = "corrupt bit",
}, },
{
.type = QCOW2_FEAT_TYPE_INCOMPATIBLE,
.bit = QCOW2_INCOMPAT_DATA_FILE_BITNR,
.name = "external data file",
},
{ {
.type = QCOW2_FEAT_TYPE_COMPATIBLE, .type = QCOW2_FEAT_TYPE_COMPATIBLE,
.bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR, .bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR,
@ -2516,6 +2612,12 @@ static int qcow2_change_backing_file(BlockDriverState *bs,
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
/* Adding a backing file means that the external data file alone won't be
* enough to make sense of the content */
if (backing_file && data_file_is_raw(bs)) {
return -EINVAL;
}
if (backing_file && strlen(backing_file) > 1023) { if (backing_file && strlen(backing_file) > 1023) {
return -EINVAL; return -EINVAL;
} }
@ -2829,6 +2931,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
*/ */
BlockBackend *blk = NULL; BlockBackend *blk = NULL;
BlockDriverState *bs = NULL; BlockDriverState *bs = NULL;
BlockDriverState *data_bs = NULL;
QCowHeader *header; QCowHeader *header;
size_t cluster_size; size_t cluster_size;
int version; int version;
@ -2925,6 +3028,32 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
} }
refcount_order = ctz32(qcow2_opts->refcount_bits); refcount_order = ctz32(qcow2_opts->refcount_bits);
if (qcow2_opts->data_file_raw && !qcow2_opts->data_file) {
error_setg(errp, "data-file-raw requires data-file");
ret = -EINVAL;
goto out;
}
if (qcow2_opts->data_file_raw && qcow2_opts->has_backing_file) {
error_setg(errp, "Backing file and data-file-raw cannot be used at "
"the same time");
ret = -EINVAL;
goto out;
}
if (qcow2_opts->data_file) {
if (version < 3) {
error_setg(errp, "External data files are only supported with "
"compatibility level 1.1 and above (use version=v3 or "
"greater)");
ret = -EINVAL;
goto out;
}
data_bs = bdrv_open_blockdev_ref(qcow2_opts->data_file, errp);
if (bs == NULL) {
ret = -EIO;
goto out;
}
}
/* Create BlockBackend to write to the image */ /* Create BlockBackend to write to the image */
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
@ -2940,19 +3069,6 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
goto out; 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 */ /* Write the header */
QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header)); QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header));
header = g_malloc0(cluster_size); header = g_malloc0(cluster_size);
@ -2976,6 +3092,14 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
header->compatible_features |= header->compatible_features |=
cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS); cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
} }
if (data_bs) {
header->incompatible_features |=
cpu_to_be64(QCOW2_INCOMPAT_DATA_FILE);
}
if (qcow2_opts->data_file_raw) {
header->autoclear_features |=
cpu_to_be64(QCOW2_AUTOCLEAR_DATA_FILE_RAW);
}
ret = blk_pwrite(blk, 0, header, cluster_size, 0); ret = blk_pwrite(blk, 0, header, cluster_size, 0);
g_free(header); g_free(header);
@ -3006,6 +3130,9 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
options = qdict_new(); options = qdict_new();
qdict_put_str(options, "driver", "qcow2"); qdict_put_str(options, "driver", "qcow2");
qdict_put_str(options, "file", bs->node_name); qdict_put_str(options, "file", bs->node_name);
if (data_bs) {
qdict_put_str(options, "data-file", data_bs->node_name);
}
blk = blk_new_open(NULL, NULL, options, blk = blk_new_open(NULL, NULL, options,
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH, BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH,
&local_err); &local_err);
@ -3026,6 +3153,12 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
abort(); abort();
} }
/* Set the external data file if necessary */
if (data_bs) {
BDRVQcow2State *s = blk_bs(blk)->opaque;
s->image_data_file = g_strdup(data_bs->filename);
}
/* Create a full header (including things like feature table) */ /* Create a full header (including things like feature table) */
ret = qcow2_update_header(blk_bs(blk)); ret = qcow2_update_header(blk_bs(blk));
if (ret < 0) { if (ret < 0) {
@ -3034,7 +3167,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
} }
/* Okay, now that we have a valid image, let's give it the right size */ /* Okay, now that we have a valid image, let's give it the right size */
ret = blk_truncate(blk, qcow2_opts->size, PREALLOC_MODE_OFF, errp); ret = blk_truncate(blk, qcow2_opts->size, qcow2_opts->preallocation, errp);
if (ret < 0) { if (ret < 0) {
error_prepend(errp, "Could not resize image: "); error_prepend(errp, "Could not resize image: ");
goto out; goto out;
@ -3066,19 +3199,6 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
} }
} }
/* And if we're supposed to preallocate metadata, do that now */
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;
}
}
blk_unref(blk); blk_unref(blk);
blk = NULL; blk = NULL;
@ -3091,6 +3211,9 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
options = qdict_new(); options = qdict_new();
qdict_put_str(options, "driver", "qcow2"); qdict_put_str(options, "driver", "qcow2");
qdict_put_str(options, "file", bs->node_name); qdict_put_str(options, "file", bs->node_name);
if (data_bs) {
qdict_put_str(options, "data-file", data_bs->node_name);
}
blk = blk_new_open(NULL, NULL, options, blk = blk_new_open(NULL, NULL, options,
BDRV_O_RDWR | BDRV_O_NO_BACKING | BDRV_O_NO_IO, BDRV_O_RDWR | BDRV_O_NO_BACKING | BDRV_O_NO_IO,
&local_err); &local_err);
@ -3104,6 +3227,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
out: out:
blk_unref(blk); blk_unref(blk);
bdrv_unref(bs); bdrv_unref(bs);
bdrv_unref(data_bs);
return ret; return ret;
} }
@ -3114,6 +3238,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
QDict *qdict; QDict *qdict;
Visitor *v; Visitor *v;
BlockDriverState *bs = NULL; BlockDriverState *bs = NULL;
BlockDriverState *data_bs = NULL;
Error *local_err = NULL; Error *local_err = NULL;
const char *val; const char *val;
int ret; int ret;
@ -3156,6 +3281,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
{ BLOCK_OPT_REFCOUNT_BITS, "refcount-bits" }, { BLOCK_OPT_REFCOUNT_BITS, "refcount-bits" },
{ BLOCK_OPT_ENCRYPT, BLOCK_OPT_ENCRYPT_FORMAT }, { BLOCK_OPT_ENCRYPT, BLOCK_OPT_ENCRYPT_FORMAT },
{ BLOCK_OPT_COMPAT_LEVEL, "version" }, { BLOCK_OPT_COMPAT_LEVEL, "version" },
{ BLOCK_OPT_DATA_FILE_RAW, "data-file-raw" },
{ NULL, NULL }, { NULL, NULL },
}; };
@ -3177,6 +3303,26 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
goto finish; goto finish;
} }
/* Create and open an external data file (protocol layer) */
val = qdict_get_try_str(qdict, BLOCK_OPT_DATA_FILE);
if (val) {
ret = bdrv_create_file(val, opts, errp);
if (ret < 0) {
goto finish;
}
data_bs = bdrv_open(val, NULL, NULL,
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
errp);
if (data_bs == NULL) {
ret = -EIO;
goto finish;
}
qdict_del(qdict, BLOCK_OPT_DATA_FILE);
qdict_put_str(qdict, "data-file", data_bs->node_name);
}
/* Set 'driver' and 'node' options */ /* Set 'driver' and 'node' options */
qdict_put_str(qdict, "driver", "qcow2"); qdict_put_str(qdict, "driver", "qcow2");
qdict_put_str(qdict, "file", bs->node_name); qdict_put_str(qdict, "file", bs->node_name);
@ -3211,6 +3357,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
finish: finish:
qobject_unref(qdict); qobject_unref(qdict);
bdrv_unref(bs); bdrv_unref(bs);
bdrv_unref(data_bs);
qapi_free_BlockdevCreateOptions(create_options); qapi_free_BlockdevCreateOptions(create_options);
return ret; return ret;
} }
@ -3361,7 +3508,7 @@ qcow2_co_copy_range_from(BlockDriverState *bs,
goto out; goto out;
case QCOW2_CLUSTER_NORMAL: case QCOW2_CLUSTER_NORMAL:
child = bs->file; child = s->data_file;
copy_offset += offset_into_cluster(s, src_offset); copy_offset += offset_into_cluster(s, src_offset);
if ((copy_offset & 511) != 0) { if ((copy_offset & 511) != 0) {
ret = -EIO; ret = -EIO;
@ -3431,14 +3578,14 @@ qcow2_co_copy_range_to(BlockDriverState *bs,
assert((cluster_offset & 511) == 0); assert((cluster_offset & 511) == 0);
ret = qcow2_pre_write_overlap_check(bs, 0, ret = qcow2_pre_write_overlap_check(bs, 0,
cluster_offset + offset_in_cluster, cur_bytes); cluster_offset + offset_in_cluster, cur_bytes, true);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
ret = bdrv_co_copy_range_to(src, src_offset, ret = bdrv_co_copy_range_to(src, src_offset,
bs->file, s->data_file,
cluster_offset + offset_in_cluster, cluster_offset + offset_in_cluster,
cur_bytes, read_flags, write_flags); cur_bytes, read_flags, write_flags);
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
@ -3593,6 +3740,17 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
int64_t old_file_size, new_file_size; int64_t old_file_size, new_file_size;
uint64_t nb_new_data_clusters, nb_new_l2_tables; uint64_t nb_new_data_clusters, nb_new_l2_tables;
/* With a data file, preallocation means just allocating the metadata
* and forwarding the truncate request to the data file */
if (has_data_file(bs)) {
ret = preallocate_co(bs, old_length, offset);
if (ret < 0) {
error_setg_errno(errp, -ret, "Preallocation failed");
goto fail;
}
break;
}
old_file_size = bdrv_getlength(bs->file->bs); old_file_size = bdrv_getlength(bs->file->bs);
if (old_file_size < 0) { if (old_file_size < 0) {
error_setg_errno(errp, -old_file_size, error_setg_errno(errp, -old_file_size,
@ -3701,6 +3859,16 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
bs->total_sectors = offset / BDRV_SECTOR_SIZE; bs->total_sectors = offset / BDRV_SECTOR_SIZE;
if (has_data_file(bs)) {
if (prealloc == PREALLOC_MODE_METADATA) {
prealloc = PREALLOC_MODE_OFF;
}
ret = bdrv_co_truncate(s->data_file, offset, prealloc, errp);
if (ret < 0) {
goto fail;
}
}
/* write updated header.size */ /* write updated header.size */
offset = cpu_to_be64(offset); offset = cpu_to_be64(offset);
ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size), ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size),
@ -3901,17 +4069,20 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
int ret; int ret;
size_t out_len; size_t out_len;
uint8_t *buf, *out_buf; uint8_t *buf, *out_buf;
int64_t cluster_offset; uint64_t cluster_offset;
if (has_data_file(bs)) {
return -ENOTSUP;
}
if (bytes == 0) { if (bytes == 0) {
/* align end of file to a sector boundary to ease reading with /* align end of file to a sector boundary to ease reading with
sector based I/Os */ sector based I/Os */
cluster_offset = bdrv_getlength(bs->file->bs); int64_t len = bdrv_getlength(bs->file->bs);
if (cluster_offset < 0) { if (len < 0) {
return cluster_offset; return len;
} }
return bdrv_co_truncate(bs->file, cluster_offset, PREALLOC_MODE_OFF, return bdrv_co_truncate(bs->file, len, PREALLOC_MODE_OFF, NULL);
NULL);
} }
if (offset_into_cluster(s, offset)) { if (offset_into_cluster(s, offset)) {
@ -3948,16 +4119,14 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
} }
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
cluster_offset = ret = qcow2_alloc_compressed_cluster_offset(bs, offset, out_len,
qcow2_alloc_compressed_cluster_offset(bs, offset, out_len); &cluster_offset);
if (!cluster_offset) { if (ret < 0) {
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
ret = -EIO;
goto fail; goto fail;
} }
cluster_offset &= s->cluster_offset_mask;
ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset, out_len); ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset, out_len, true);
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
@ -3965,8 +4134,8 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
qemu_iovec_init_buf(&hd_qiov, out_buf, out_len); qemu_iovec_init_buf(&hd_qiov, out_buf, out_len);
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED); BLKDBG_EVENT(s->data_file, BLKDBG_WRITE_COMPRESSED);
ret = bdrv_co_pwritev(bs->file, cluster_offset, out_len, &hd_qiov, 0); ret = bdrv_co_pwritev(s->data_file, cluster_offset, out_len, &hd_qiov, 0);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
@ -4479,6 +4648,10 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs,
.refcount_bits = s->refcount_bits, .refcount_bits = s->refcount_bits,
.has_bitmaps = !!bitmaps, .has_bitmaps = !!bitmaps,
.bitmaps = bitmaps, .bitmaps = bitmaps,
.has_data_file = !!s->image_data_file,
.data_file = g_strdup(s->image_data_file),
.has_data_file_raw = has_data_file(bs),
.data_file_raw = data_file_is_raw(bs),
}; };
} else { } else {
/* if this assertion fails, this probably means a new version was /* if this assertion fails, this probably means a new version was
@ -4555,6 +4728,11 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
return -ENOTSUP; return -ENOTSUP;
} }
if (has_data_file(bs)) {
error_setg(errp, "Cannot downgrade an image with a data file");
return -ENOTSUP;
}
/* clear incompatible features */ /* clear incompatible features */
if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
ret = qcow2_mark_clean(bs); ret = qcow2_mark_clean(bs);
@ -4676,8 +4854,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
int old_version = s->qcow_version, new_version = old_version; int old_version = s->qcow_version, new_version = old_version;
uint64_t new_size = 0; uint64_t new_size = 0;
const char *backing_file = NULL, *backing_format = NULL; const char *backing_file = NULL, *backing_format = NULL, *data_file = NULL;
bool lazy_refcounts = s->use_lazy_refcounts; bool lazy_refcounts = s->use_lazy_refcounts;
bool data_file_raw = data_file_is_raw(bs);
const char *compat = NULL; const char *compat = NULL;
uint64_t cluster_size = s->cluster_size; uint64_t cluster_size = s->cluster_size;
bool encrypt; bool encrypt;
@ -4758,6 +4937,21 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
"may not exceed 64 bits"); "may not exceed 64 bits");
return -EINVAL; return -EINVAL;
} }
} else if (!strcmp(desc->name, BLOCK_OPT_DATA_FILE)) {
data_file = qemu_opt_get(opts, BLOCK_OPT_DATA_FILE);
if (data_file && !has_data_file(bs)) {
error_setg(errp, "data-file can only be set for images that "
"use an external data file");
return -EINVAL;
}
} else if (!strcmp(desc->name, BLOCK_OPT_DATA_FILE_RAW)) {
data_file_raw = qemu_opt_get_bool(opts, BLOCK_OPT_DATA_FILE_RAW,
data_file_raw);
if (data_file_raw && !data_file_is_raw(bs)) {
error_setg(errp, "data-file-raw cannot be set on existing "
"images");
return -EINVAL;
}
} else { } else {
/* if this point is reached, this probably means a new option was /* if this point is reached, this probably means a new option was
* added without having it covered here */ * added without having it covered here */
@ -4804,6 +4998,24 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
} }
} }
/* data-file-raw blocks backing files, so clear it first if requested */
if (data_file_raw) {
s->autoclear_features |= QCOW2_AUTOCLEAR_DATA_FILE_RAW;
} else {
s->autoclear_features &= ~QCOW2_AUTOCLEAR_DATA_FILE_RAW;
}
if (data_file) {
g_free(s->image_data_file);
s->image_data_file = *data_file ? g_strdup(data_file) : NULL;
}
ret = qcow2_update_header(bs);
if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to update the image header");
return ret;
}
if (backing_file || backing_format) { if (backing_file || backing_format) {
ret = qcow2_change_backing_file(bs, ret = qcow2_change_backing_file(bs,
backing_file ?: s->image_backing_file, backing_file ?: s->image_backing_file,
@ -4951,6 +5163,16 @@ static QemuOptsList qcow2_create_opts = {
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
.help = "Image format of the base image" .help = "Image format of the base image"
}, },
{
.name = BLOCK_OPT_DATA_FILE,
.type = QEMU_OPT_STRING,
.help = "File name of an external data file"
},
{
.name = BLOCK_OPT_DATA_FILE_RAW,
.type = QEMU_OPT_BOOL,
.help = "The external data file must stay valid as a raw image"
},
{ {
.name = BLOCK_OPT_ENCRYPT, .name = BLOCK_OPT_ENCRYPT,
.type = QEMU_OPT_BOOL, .type = QEMU_OPT_BOOL,

View File

@ -91,6 +91,7 @@
#define DEFAULT_CLUSTER_SIZE 65536 #define DEFAULT_CLUSTER_SIZE 65536
#define QCOW2_OPT_DATA_FILE "data-file"
#define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts" #define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts"
#define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request" #define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request"
#define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot" #define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot"
@ -197,13 +198,16 @@ enum {
/* Incompatible feature bits */ /* Incompatible feature bits */
enum { enum {
QCOW2_INCOMPAT_DIRTY_BITNR = 0, QCOW2_INCOMPAT_DIRTY_BITNR = 0,
QCOW2_INCOMPAT_CORRUPT_BITNR = 1, QCOW2_INCOMPAT_CORRUPT_BITNR = 1,
QCOW2_INCOMPAT_DIRTY = 1 << QCOW2_INCOMPAT_DIRTY_BITNR, QCOW2_INCOMPAT_DATA_FILE_BITNR = 2,
QCOW2_INCOMPAT_CORRUPT = 1 << QCOW2_INCOMPAT_CORRUPT_BITNR, QCOW2_INCOMPAT_DIRTY = 1 << QCOW2_INCOMPAT_DIRTY_BITNR,
QCOW2_INCOMPAT_CORRUPT = 1 << QCOW2_INCOMPAT_CORRUPT_BITNR,
QCOW2_INCOMPAT_DATA_FILE = 1 << QCOW2_INCOMPAT_DATA_FILE_BITNR,
QCOW2_INCOMPAT_MASK = QCOW2_INCOMPAT_DIRTY QCOW2_INCOMPAT_MASK = QCOW2_INCOMPAT_DIRTY
| QCOW2_INCOMPAT_CORRUPT, | QCOW2_INCOMPAT_CORRUPT
| QCOW2_INCOMPAT_DATA_FILE,
}; };
/* Compatible feature bits */ /* Compatible feature bits */
@ -216,10 +220,13 @@ enum {
/* Autoclear feature bits */ /* Autoclear feature bits */
enum { enum {
QCOW2_AUTOCLEAR_BITMAPS_BITNR = 0, QCOW2_AUTOCLEAR_BITMAPS_BITNR = 0,
QCOW2_AUTOCLEAR_BITMAPS = 1 << QCOW2_AUTOCLEAR_BITMAPS_BITNR, QCOW2_AUTOCLEAR_DATA_FILE_RAW_BITNR = 1,
QCOW2_AUTOCLEAR_BITMAPS = 1 << QCOW2_AUTOCLEAR_BITMAPS_BITNR,
QCOW2_AUTOCLEAR_DATA_FILE_RAW = 1 << QCOW2_AUTOCLEAR_DATA_FILE_RAW_BITNR,
QCOW2_AUTOCLEAR_MASK = QCOW2_AUTOCLEAR_BITMAPS, QCOW2_AUTOCLEAR_MASK = QCOW2_AUTOCLEAR_BITMAPS
| QCOW2_AUTOCLEAR_DATA_FILE_RAW,
}; };
enum qcow2_discard_type { enum qcow2_discard_type {
@ -337,9 +344,12 @@ typedef struct BDRVQcow2State {
* override) */ * override) */
char *image_backing_file; char *image_backing_file;
char *image_backing_format; char *image_backing_format;
char *image_data_file;
CoQueue compress_wait_queue; CoQueue compress_wait_queue;
int nb_compress_threads; int nb_compress_threads;
BdrvChild *data_file;
} BDRVQcow2State; } BDRVQcow2State;
typedef struct Qcow2COWRegion { typedef struct Qcow2COWRegion {
@ -457,6 +467,20 @@ typedef enum QCow2MetadataOverlap {
#define REFT_OFFSET_MASK 0xfffffffffffffe00ULL #define REFT_OFFSET_MASK 0xfffffffffffffe00ULL
#define INV_OFFSET (-1ULL)
static inline bool has_data_file(BlockDriverState *bs)
{
BDRVQcow2State *s = bs->opaque;
return (s->data_file != bs->file);
}
static inline bool data_file_is_raw(BlockDriverState *bs)
{
BDRVQcow2State *s = bs->opaque;
return !!(s->autoclear_features & QCOW2_AUTOCLEAR_DATA_FILE_RAW);
}
static inline int64_t start_of_cluster(BDRVQcow2State *s, int64_t offset) static inline int64_t start_of_cluster(BDRVQcow2State *s, int64_t offset)
{ {
return offset & ~(s->cluster_size - 1); return offset & ~(s->cluster_size - 1);
@ -498,7 +522,8 @@ static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s)
return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
} }
static inline QCow2ClusterType qcow2_get_cluster_type(uint64_t l2_entry) static inline QCow2ClusterType qcow2_get_cluster_type(BlockDriverState *bs,
uint64_t l2_entry)
{ {
if (l2_entry & QCOW_OFLAG_COMPRESSED) { if (l2_entry & QCOW_OFLAG_COMPRESSED) {
return QCOW2_CLUSTER_COMPRESSED; return QCOW2_CLUSTER_COMPRESSED;
@ -508,7 +533,15 @@ static inline QCow2ClusterType qcow2_get_cluster_type(uint64_t l2_entry)
} }
return QCOW2_CLUSTER_ZERO_PLAIN; return QCOW2_CLUSTER_ZERO_PLAIN;
} else if (!(l2_entry & L2E_OFFSET_MASK)) { } else if (!(l2_entry & L2E_OFFSET_MASK)) {
return QCOW2_CLUSTER_UNALLOCATED; /* Offset 0 generally means unallocated, but it is ambiguous with
* external data files because 0 is a valid offset there. However, all
* clusters in external data files always have refcount 1, so we can
* rely on QCOW_OFLAG_COPIED to disambiguate. */
if (has_data_file(bs) && (l2_entry & QCOW_OFLAG_COPIED)) {
return QCOW2_CLUSTER_NORMAL;
} else {
return QCOW2_CLUSTER_UNALLOCATED;
}
} else { } else {
return QCOW2_CLUSTER_NORMAL; return QCOW2_CLUSTER_NORMAL;
} }
@ -599,7 +632,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret);
int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
int64_t size); int64_t size);
int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
int64_t size); int64_t size, bool data_file);
int qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res, int qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res,
void **refcount_table, void **refcount_table,
int64_t *refcount_table_size, int64_t *refcount_table_size,
@ -624,9 +657,10 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
unsigned int *bytes, uint64_t *host_offset, unsigned int *bytes, uint64_t *host_offset,
QCowL2Meta **m); QCowL2Meta **m);
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
uint64_t offset, uint64_t offset,
int compressed_size); int compressed_size,
uint64_t *host_offset);
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m); void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m);

View File

@ -531,7 +531,9 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
if ((buf = qemu_opt_get(opts, "format")) != NULL) { if ((buf = qemu_opt_get(opts, "format")) != NULL) {
if (is_help_option(buf)) { if (is_help_option(buf)) {
error_printf("Supported formats:"); error_printf("Supported formats:");
bdrv_iterate_format(bdrv_format_print, NULL); bdrv_iterate_format(bdrv_format_print, NULL, false);
error_printf("\nSupported formats (read-only):");
bdrv_iterate_format(bdrv_format_print, NULL, true);
error_printf("\n"); error_printf("\n");
goto early_err; goto early_err;
} }

View File

@ -97,7 +97,19 @@ in the description of a field.
be written to (unless for regaining be written to (unless for regaining
consistency). consistency).
Bits 2-63: Reserved (set to 0) Bit 2: External data file bit. If this bit is set, an
external data file is used. Guest clusters are
then stored in the external data file. For such
images, clusters in the external data file are
not refcounted. The offset field in the
Standard Cluster Descriptor must match the
guest offset and neither compressed clusters
nor internal snapshots are supported.
An External Data File Name header extension may
be present if this bit is set.
Bits 3-63: Reserved (set to 0)
80 - 87: compatible_features 80 - 87: compatible_features
Bitmask of compatible features. An implementation can Bitmask of compatible features. An implementation can
@ -126,7 +138,21 @@ in the description of a field.
bit is unset, the bitmaps extension data must be bit is unset, the bitmaps extension data must be
considered inconsistent. considered inconsistent.
Bits 1-63: Reserved (set to 0) Bit 1: If this bit is set, the external data file can
be read as a consistent standalone raw image
without looking at the qcow2 metadata.
Setting this bit has a performance impact for
some operations on the image (e.g. writing
zeros requires writing to the data file instead
of only setting the zero flag in the L2 table
entry) and conflicts with backing files.
This bit may only be set if the External Data
File bit (incompatible feature bit 1) is also
set.
Bits 2-63: Reserved (set to 0)
96 - 99: refcount_order 96 - 99: refcount_order
Describes the width of a reference count block entry (width Describes the width of a reference count block entry (width
@ -144,10 +170,11 @@ be stored. Each extension has a structure like the following:
Byte 0 - 3: Header extension type: Byte 0 - 3: Header extension type:
0x00000000 - End of the header extension area 0x00000000 - End of the header extension area
0xE2792ACA - Backing file format name 0xE2792ACA - Backing file format name string
0x6803f857 - Feature name table 0x6803f857 - Feature name table
0x23852875 - Bitmaps extension 0x23852875 - Bitmaps extension
0x0537be77 - Full disk encryption header pointer 0x0537be77 - Full disk encryption header pointer
0x44415441 - External data file name string
other - Unknown header extension, can be safely other - Unknown header extension, can be safely
ignored ignored
@ -169,6 +196,16 @@ data of compatible features that it doesn't support. Compatible features that
need space for additional data can use a header extension. need space for additional data can use a header extension.
== String header extensions ==
Some header extensions (such as the backing file format name and the external
data file name) are just a single string. In this case, the header extension
length is the string length and the string is not '\0' terminated. (The header
extension padding can make it look like a string is '\0' terminated, but
neither is padding always necessary nor is there a guarantee that zero bytes
are used for padding.)
== Feature name table == == Feature name table ==
The feature name table is an optional header extension that contains the name The feature name table is an optional header extension that contains the name
@ -437,6 +474,11 @@ L2 table entry:
This information is only accurate in L2 tables This information is only accurate in L2 tables
that are reachable from the active L1 table. that are reachable from the active L1 table.
With external data files, all guest clusters have an
implicit refcount of 1 (because of the fixed host = guest
mapping for guest cluster offsets), so this bit should be 1
for all allocated clusters.
Standard Cluster Descriptor: Standard Cluster Descriptor:
Bit 0: If set to 1, the cluster reads as all zeros. The host Bit 0: If set to 1, the cluster reads as all zeros. The host
@ -450,8 +492,10 @@ Standard Cluster Descriptor:
1 - 8: Reserved (set to 0) 1 - 8: Reserved (set to 0)
9 - 55: Bits 9-55 of host cluster offset. Must be aligned to a 9 - 55: Bits 9-55 of host cluster offset. Must be aligned to a
cluster boundary. If the offset is 0, the cluster is cluster boundary. If the offset is 0 and bit 63 is clear,
unallocated. the cluster is unallocated. The offset may only be 0 with
bit 63 set (indicating a host cluster offset of 0) when an
external data file is used.
56 - 61: Reserved (set to 0) 56 - 61: Reserved (set to 0)

View File

@ -158,10 +158,10 @@ refcount cache is as small as possible unless overridden by the user.
Using smaller cache entries Using smaller cache entries
--------------------------- ---------------------------
The qcow2 L2 cache stores complete tables by default. This means that The qcow2 L2 cache can store complete tables. This means that if QEMU
if QEMU needs an entry from an L2 table then the whole table is read needs an entry from an L2 table then the whole table is read from disk
from disk and is kept in the cache. If the cache is full then a and is kept in the cache. If the cache is full then a complete table
complete table needs to be evicted first. needs to be evicted first.
This can be inefficient with large cluster sizes since it results in This can be inefficient with large cluster sizes since it results in
more disk I/O and wastes more cache memory. more disk I/O and wastes more cache memory.
@ -172,6 +172,9 @@ it smaller than the cluster size. This can be configured using the
-drive file=hd.qcow2,l2-cache-size=2097152,l2-cache-entry-size=4096 -drive file=hd.qcow2,l2-cache-size=2097152,l2-cache-entry-size=4096
Since QEMU 4.0 the value of l2-cache-entry-size defaults to 4KB (or
the cluster size if it's smaller).
Some things to take into account: Some things to take into account:
- The L2 cache entry size has the same restrictions as the cluster - The L2 cache entry size has the same restrictions as the cluster
@ -185,7 +188,8 @@ Some things to take into account:
- Try different entry sizes to see which one gives faster performance - Try different entry sizes to see which one gives faster performance
in your case. The block size of the host filesystem is generally a in your case. The block size of the host filesystem is generally a
good default (usually 4096 bytes in the case of ext4). good default (usually 4096 bytes in the case of ext4, hence the
default).
- Only the L2 cache can be configured this way. The refcount cache - Only the L2 cache can be configured this way. The refcount cache
always uses the cluster size as the entry size. always uses the cluster size as the entry size.
@ -194,7 +198,8 @@ Some things to take into account:
(as explained in the "Choosing the right cache sizes" and "How to (as explained in the "Choosing the right cache sizes" and "How to
configure the cache sizes" sections in this document) then none of configure the cache sizes" sections in this document) then none of
this is necessary and you can omit the "l2-cache-entry-size" this is necessary and you can omit the "l2-cache-entry-size"
parameter altogether. parameter altogether. In this case QEMU makes the entry size
equal to the cluster size by default.
Reducing the memory usage Reducing the memory usage

View File

@ -472,7 +472,7 @@ void bdrv_next_cleanup(BdrvNextIterator *it);
BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs); BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs);
bool bdrv_is_encrypted(BlockDriverState *bs); bool bdrv_is_encrypted(BlockDriverState *bs);
void bdrv_iterate_format(void (*it)(void *opaque, const char *name), void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
void *opaque); void *opaque, bool read_only);
const char *bdrv_get_node_name(const BlockDriverState *bs); const char *bdrv_get_node_name(const BlockDriverState *bs);
const char *bdrv_get_device_name(const BlockDriverState *bs); const char *bdrv_get_device_name(const BlockDriverState *bs);
const char *bdrv_get_device_or_node_name(const BlockDriverState *bs); const char *bdrv_get_device_or_node_name(const BlockDriverState *bs);

View File

@ -56,6 +56,8 @@
#define BLOCK_OPT_NOCOW "nocow" #define BLOCK_OPT_NOCOW "nocow"
#define BLOCK_OPT_OBJECT_SIZE "object_size" #define BLOCK_OPT_OBJECT_SIZE "object_size"
#define BLOCK_OPT_REFCOUNT_BITS "refcount_bits" #define BLOCK_OPT_REFCOUNT_BITS "refcount_bits"
#define BLOCK_OPT_DATA_FILE "data_file"
#define BLOCK_OPT_DATA_FILE_RAW "data_file_raw"
#define BLOCK_PROBE_BUF_SIZE 512 #define BLOCK_PROBE_BUF_SIZE 512

View File

@ -59,6 +59,13 @@
# #
# @compat: compatibility level # @compat: compatibility level
# #
# @data-file: the filename of the external data file that is stored in the
# image and used as a default for opening the image (since: 4.0)
#
# @data-file-raw: True if the external data file must stay valid as a
# standalone (read-only) raw image without looking at qcow2
# metadata (since: 4.0)
#
# @lazy-refcounts: on or off; only valid for compat >= 1.1 # @lazy-refcounts: on or off; only valid for compat >= 1.1
# #
# @corrupt: true if the image has been marked corrupt; only valid for # @corrupt: true if the image has been marked corrupt; only valid for
@ -76,6 +83,8 @@
{ 'struct': 'ImageInfoSpecificQCow2', { 'struct': 'ImageInfoSpecificQCow2',
'data': { 'data': {
'compat': 'str', 'compat': 'str',
'*data-file': 'str',
'*data-file-raw': 'bool',
'*lazy-refcounts': 'bool', '*lazy-refcounts': 'bool',
'*corrupt': 'bool', '*corrupt': 'bool',
'refcount-bits': 'int', 'refcount-bits': 'int',
@ -3080,6 +3089,12 @@
# encrypted images, except when doing a metadata-only # encrypted images, except when doing a metadata-only
# probe of the image. (since 2.10) # probe of the image. (since 2.10)
# #
# @data-file: reference to or definition of the external data file.
# This may only be specified for images that require an
# external data file. If it is not specified for such
# an image, the data file name is loaded from the image
# file. (since 4.0)
#
# Since: 2.9 # Since: 2.9
## ##
{ 'struct': 'BlockdevOptionsQcow2', { 'struct': 'BlockdevOptionsQcow2',
@ -3094,7 +3109,8 @@
'*l2-cache-entry-size': 'int', '*l2-cache-entry-size': 'int',
'*refcount-cache-size': 'int', '*refcount-cache-size': 'int',
'*cache-clean-interval': 'int', '*cache-clean-interval': 'int',
'*encrypt': 'BlockdevQcow2Encryption' } } '*encrypt': 'BlockdevQcow2Encryption',
'*data-file': 'BlockdevRef' } }
## ##
# @SshHostKeyCheckMode: # @SshHostKeyCheckMode:
@ -4130,6 +4146,12 @@
# Driver specific image creation options for qcow2. # Driver specific image creation options for qcow2.
# #
# @file Node to create the image format on # @file Node to create the image format on
# @data-file Node to use as an external data file in which all guest
# data is stored so that only metadata remains in the qcow2
# file (since: 4.0)
# @data-file-raw True if the external data file must stay valid as a
# standalone (read-only) raw image without looking at qcow2
# metadata (default: false; since: 4.0)
# @size Size of the virtual disk in bytes # @size Size of the virtual disk in bytes
# @version Compatibility level (default: v3) # @version Compatibility level (default: v3)
# @backing-file File name of the backing file if a backing file # @backing-file File name of the backing file if a backing file
@ -4145,6 +4167,8 @@
## ##
{ 'struct': 'BlockdevCreateOptionsQcow2', { 'struct': 'BlockdevCreateOptionsQcow2',
'data': { 'file': 'BlockdevRef', 'data': { 'file': 'BlockdevRef',
'*data-file': 'BlockdevRef',
'*data-file-raw': 'bool',
'size': 'size', 'size': 'size',
'*version': 'BlockdevQcow2Version', '*version': 'BlockdevQcow2Version',
'*backing-file': 'str', '*backing-file': 'str',

View File

@ -198,7 +198,7 @@ static void QEMU_NORETURN help(void)
" 'skip=N' skip N bs-sized blocks at the start of input\n"; " 'skip=N' skip N bs-sized blocks at the start of input\n";
printf("%s\nSupported formats:", help_msg); printf("%s\nSupported formats:", help_msg);
bdrv_iterate_format(format_print, NULL); bdrv_iterate_format(format_print, NULL, false);
printf("\n\n" QEMU_HELP_BOTTOM "\n"); printf("\n\n" QEMU_HELP_BOTTOM "\n");
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }

View File

@ -780,7 +780,7 @@ tests/prom-env-test$(EXESUF): tests/prom-env-test.o $(libqos-obj-y)
tests/rtas-test$(EXESUF): tests/rtas-test.o $(libqos-spapr-obj-y) tests/rtas-test$(EXESUF): tests/rtas-test.o $(libqos-spapr-obj-y)
tests/fdc-test$(EXESUF): tests/fdc-test.o tests/fdc-test$(EXESUF): tests/fdc-test.o
tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y) tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y)
tests/ahci-test$(EXESUF): tests/ahci-test.o $(libqos-pc-obj-y) tests/ahci-test$(EXESUF): tests/ahci-test.o $(libqos-pc-obj-y) qemu-img$(EXESUF)
tests/ipmi-kcs-test$(EXESUF): tests/ipmi-kcs-test.o tests/ipmi-kcs-test$(EXESUF): tests/ipmi-kcs-test.o
tests/ipmi-bt-test$(EXESUF): tests/ipmi-bt-test.o tests/ipmi-bt-test$(EXESUF): tests/ipmi-bt-test.o
tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
@ -1101,7 +1101,7 @@ clean-tcg: $(CLEAN_TCG_TARGET_RULES)
QEMU_IOTESTS_HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = tests/qemu-iotests/socket_scm_helper$(EXESUF) QEMU_IOTESTS_HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = tests/qemu-iotests/socket_scm_helper$(EXESUF)
.PHONY: check-tests/qemu-iotests-quick.sh .PHONY: check-tests/qemu-iotests-quick.sh
check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF) $(QEMU_IOTESTS_HELPERS-y) check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF) qemu-nbd$(EXESUF) $(QEMU_IOTESTS_HELPERS-y)
$< $<
.PHONY: $(patsubst %, check-%, $(check-qapi-schema-y)) .PHONY: $(patsubst %, check-%, $(check-qapi-schema-y))

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com> # Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com>
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test simple read/write using plain bdrv_read/bdrv_write # Test simple read/write using plain bdrv_read/bdrv_write
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test simple read/write using plain bdrv_pread/bdrv_pwrite # Test simple read/write using plain bdrv_pread/bdrv_pwrite
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test simple read/write using bdrv_aio_readv/bdrv_aio_writev # Test simple read/write using bdrv_aio_readv/bdrv_aio_writev
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Make sure we can't read and write outside of the image size. # Make sure we can't read and write outside of the image size.
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Make sure qemu-img can create 5TB images # Make sure qemu-img can create 5TB images
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Check for one possible case of qcow2 refcount corruption. # Check for one possible case of qcow2 refcount corruption.
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test simple asynchronous read/write operations. # Test simple asynchronous read/write operations.
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Nolan I qcow2 corruption - incorrectly reports free clusters # Nolan I qcow2 corruption - incorrectly reports free clusters
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Nolan II qcow2 corruption - wrong used cluster # Nolan II qcow2 corruption - wrong used cluster
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test for AIO allocation on the same cluster # Test for AIO allocation on the same cluster
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Make sure we can open read-only images # Make sure we can open read-only images
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# qcow2 pattern test, empty and compressed image - 4k cluster patterns # qcow2 pattern test, empty and compressed image - 4k cluster patterns
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# qcow2 pattern test, complex patterns including compression and snapshots # qcow2 pattern test, complex patterns including compression and snapshots
# Using patterns for 4k cluster size. # Using patterns for 4k cluster size.

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Combined test to grow the refcount table and test snapshots. # Combined test to grow the refcount table and test snapshots.
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Simple backing file reads # Simple backing file reads
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Merge backing file into test image when converting the image # Merge backing file into test image when converting the image
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# When using a backing file for the output image in qemu-img convert, # When using a backing file for the output image in qemu-img convert,
# the backing file clusters must not copied. The data must still be # the backing file clusters must not copied. The data must still be

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Commit changes to backing file # Commit changes to backing file
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test handling of invalid patterns arguments to qemu-io # Test handling of invalid patterns arguments to qemu-io
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test bdrv_load/save_vmstate using the usual patterns # Test bdrv_load/save_vmstate using the usual patterns
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# qcow2 pattern test with various cluster sizes # qcow2 pattern test with various cluster sizes
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Rebasing COW images # Rebasing COW images
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Resizing images # Resizing images
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# qcow2 error path testing # qcow2 error path testing
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test that sub-cluster allocating writes zero the rest of the cluster # Test that sub-cluster allocating writes zero the rest of the cluster
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test that backing files can be smaller than the image # Test that backing files can be smaller than the image
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# qcow2 internal snapshots/VM state tests # qcow2 internal snapshots/VM state tests
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test that all qcow2 header extensions survive a header rewrite # Test that all qcow2 header extensions survive a header rewrite
# #

View File

@ -117,7 +117,7 @@ header_length 104
Header extension: Header extension:
magic 0x6803f857 magic 0x6803f857
length 144 length 192
data <binary> data <binary>
Header extension: Header extension:
@ -150,7 +150,7 @@ header_length 104
Header extension: Header extension:
magic 0x6803f857 magic 0x6803f857
length 144 length 192
data <binary> data <binary>
Header extension: Header extension:
@ -164,7 +164,7 @@ No errors were found on the image.
magic 0x514649fb magic 0x514649fb
version 3 version 3
backing_file_offset 0x148 backing_file_offset 0x178
backing_file_size 0x17 backing_file_size 0x17
cluster_bits 16 cluster_bits 16
size 67108864 size 67108864
@ -188,7 +188,7 @@ data 'host_device'
Header extension: Header extension:
magic 0x6803f857 magic 0x6803f857
length 144 length 192
data <binary> data <binary>
Header extension: Header extension:

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test that AIO requests are drained before an image is closed. This used # Test that AIO requests are drained before an image is closed. This used
# to segfault because the request coroutine kept running even after the # to segfault because the request coroutine kept running even after the

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test aligned and misaligned write zeroes operations. # Test aligned and misaligned write zeroes operations.
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test bdrv_pwrite_zeroes with backing files (see also 154) # Test bdrv_pwrite_zeroes with backing files (see also 154)
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Let a few AIO requests run in parallel and have them access different L2 # Let a few AIO requests run in parallel and have them access different L2
# tables so that the cache has a chance to get used up. # tables so that the cache has a chance to get used up.

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test qcow2 feature bits # Test qcow2 feature bits
# #

View File

@ -58,7 +58,7 @@ header_length 104
Header extension: Header extension:
magic 0x6803f857 magic 0x6803f857
length 144 length 192
data <binary> data <binary>
@ -86,7 +86,7 @@ header_length 104
Header extension: Header extension:
magic 0x6803f857 magic 0x6803f857
length 144 length 192
data <binary> data <binary>
*** done *** done

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test COW from backing files # Test COW from backing files
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test COW from backing files with AIO # Test COW from backing files with AIO
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test qcow2 lazy refcounts # Test qcow2 lazy refcounts
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test qemu-img operation on zero size images # Test qemu-img operation on zero size images
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test that qemu-img info --backing-chain detects infinite loops # Test that qemu-img info --backing-chain detects infinite loops
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test concurrent cluster allocations # Test concurrent cluster allocations
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Regression test for commit b7ab0fea (which was a corruption fix, # Regression test for commit b7ab0fea (which was a corruption fix,
# despite the commit message claiming otherwise) # despite the commit message claiming otherwise)

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
## ##
## qemu-img compare test ## qemu-img compare test
## ##

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Check qemu-img option parsing # Check qemu-img option parsing
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test qemu-img rebase with zero clusters # Test qemu-img rebase with zero clusters
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test command line configuration of block devices and driver-specific options # Test command line configuration of block devices and driver-specific options
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test bdrv_read/bdrv_write using BDRV_O_SNAPSHOT # Test bdrv_read/bdrv_write using BDRV_O_SNAPSHOT
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test qemu-img convert when image length is not a multiple of cluster size # Test qemu-img convert when image length is not a multiple of cluster size
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test huge qcow2 images # Test huge qcow2 images
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test export internal snapshot by qemu-nbd, convert it by qemu-img. # Test export internal snapshot by qemu-nbd, convert it by qemu-img.
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test case for vmdk # Test case for vmdk
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test case for image corruption (overlapping data structures) in qcow2 # Test case for image corruption (overlapping data structures) in qcow2
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test case for image option amendment in qcow2. # Test case for image option amendment in qcow2.
# #
@ -28,7 +28,8 @@ status=1 # failure is the default!
_cleanup() _cleanup()
{ {
_cleanup_test_img _cleanup_test_img
rm -f $TEST_IMG.data
} }
trap "_cleanup; exit \$status" 0 1 2 3 15 trap "_cleanup; exit \$status" 0 1 2 3 15
@ -250,6 +251,48 @@ $QEMU_IMG snapshot -c foo "$TEST_IMG"
$QEMU_IMG amend -p -o "compat=0.10" "$TEST_IMG" $QEMU_IMG amend -p -o "compat=0.10" "$TEST_IMG"
_check_test_img _check_test_img
echo
echo "=== Testing version downgrade with external data file ==="
echo
IMGOPTS="compat=1.1,data_file=$TEST_IMG.data" _make_test_img 64M
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
_img_info --format-specific
_check_test_img
echo
echo "=== Try changing the external data file ==="
echo
IMGOPTS="compat=1.1" _make_test_img 64M
$QEMU_IMG amend -o "data_file=foo" "$TEST_IMG"
echo
IMGOPTS="compat=1.1,data_file=$TEST_IMG.data" _make_test_img 64M
$QEMU_IMG amend -o "data_file=foo" "$TEST_IMG"
_img_info --format-specific
TEST_IMG="data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" _img_info --format-specific --image-opts
echo
$QEMU_IMG amend -o "data_file=" --image-opts "data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG"
_img_info --format-specific
TEST_IMG="data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" _img_info --format-specific --image-opts
echo
echo "=== Clearing and setting data-file-raw ==="
echo
IMGOPTS="compat=1.1,data_file=$TEST_IMG.data,data_file_raw=on" _make_test_img 64M
$QEMU_IMG amend -o "data_file_raw=on" "$TEST_IMG"
_img_info --format-specific
_check_test_img
$QEMU_IMG amend -o "data_file_raw=off" "$TEST_IMG"
_img_info --format-specific
_check_test_img
$QEMU_IMG amend -o "data_file_raw=on" "$TEST_IMG"
_img_info --format-specific
_check_test_img
# success, all done # success, all done
echo "*** done" echo "*** done"
rm -f $seq.full rm -f $seq.full

View File

@ -26,7 +26,7 @@ header_length 104
Header extension: Header extension:
magic 0x6803f857 magic 0x6803f857
length 144 length 192
data <binary> data <binary>
magic 0x514649fb magic 0x514649fb
@ -84,7 +84,7 @@ header_length 104
Header extension: Header extension:
magic 0x6803f857 magic 0x6803f857
length 144 length 192
data <binary> data <binary>
magic 0x514649fb magic 0x514649fb
@ -144,7 +144,7 @@ header_length 104
Header extension: Header extension:
magic 0x6803f857 magic 0x6803f857
length 144 length 192
data <binary> data <binary>
ERROR cluster 5 refcount=0 reference=1 ERROR cluster 5 refcount=0 reference=1
@ -199,7 +199,7 @@ header_length 104
Header extension: Header extension:
magic 0x6803f857 magic 0x6803f857
length 144 length 192
data <binary> data <binary>
magic 0x514649fb magic 0x514649fb
@ -268,7 +268,7 @@ header_length 104
Header extension: Header extension:
magic 0x6803f857 magic 0x6803f857
length 144 length 192
data <binary> data <binary>
read 65536/65536 bytes at offset 44040192 read 65536/65536 bytes at offset 44040192
@ -306,7 +306,7 @@ header_length 104
Header extension: Header extension:
magic 0x6803f857 magic 0x6803f857
length 144 length 192
data <binary> data <binary>
ERROR cluster 5 refcount=0 reference=1 ERROR cluster 5 refcount=0 reference=1
@ -335,7 +335,7 @@ header_length 104
Header extension: Header extension:
magic 0x6803f857 magic 0x6803f857
length 144 length 192
data <binary> data <binary>
read 131072/131072 bytes at offset 0 read 131072/131072 bytes at offset 0
@ -488,4 +488,93 @@ wrote 65536/65536 bytes at offset 3221225472
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
(0.00/100%) (6.25/100%) (12.50/100%) (18.75/100%) (25.00/100%) (31.25/100%) (37.50/100%) (43.75/100%) (50.00/100%) (56.25/100%) (62.50/100%) (68.75/100%) (75.00/100%) (81.25/100%) (87.50/100%) (93.75/100%) (100.00/100%) (100.00/100%) (0.00/100%) (6.25/100%) (12.50/100%) (18.75/100%) (25.00/100%) (31.25/100%) (37.50/100%) (43.75/100%) (50.00/100%) (56.25/100%) (62.50/100%) (68.75/100%) (75.00/100%) (81.25/100%) (87.50/100%) (93.75/100%) (100.00/100%) (100.00/100%)
No errors were found on the image. No errors were found on the image.
=== Testing version downgrade with external data file ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data
qemu-img: Cannot downgrade an image with a data file
image: TEST_DIR/t.IMGFMT
file format: IMGFMT
virtual size: 64M (67108864 bytes)
cluster_size: 65536
Format specific information:
compat: 1.1
lazy refcounts: false
refcount bits: 16
data file: TEST_DIR/t.IMGFMT.data
data file raw: false
corrupt: false
No errors were found on the image.
=== Try changing the external data file ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qemu-img: data-file can only be set for images that use an external data file
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data
qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Could not open 'foo': No such file or directory
image: TEST_DIR/t.IMGFMT
file format: IMGFMT
virtual size: 64M (67108864 bytes)
cluster_size: 65536
Format specific information:
compat: 1.1
lazy refcounts: false
refcount bits: 16
data file: foo
data file raw: false
corrupt: false
qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'data-file' is required for this image
image: TEST_DIR/t.IMGFMT
file format: IMGFMT
virtual size: 64M (67108864 bytes)
cluster_size: 65536
Format specific information:
compat: 1.1
lazy refcounts: false
refcount bits: 16
data file raw: false
corrupt: false
=== Clearing and setting data-file-raw ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data data_file_raw=on
image: TEST_DIR/t.IMGFMT
file format: IMGFMT
virtual size: 64M (67108864 bytes)
cluster_size: 65536
Format specific information:
compat: 1.1
lazy refcounts: false
refcount bits: 16
data file: TEST_DIR/t.IMGFMT.data
data file raw: true
corrupt: false
No errors were found on the image.
image: TEST_DIR/t.IMGFMT
file format: IMGFMT
virtual size: 64M (67108864 bytes)
cluster_size: 65536
Format specific information:
compat: 1.1
lazy refcounts: false
refcount bits: 16
data file: TEST_DIR/t.IMGFMT.data
data file raw: false
corrupt: false
No errors were found on the image.
qemu-img: data-file-raw cannot be set on existing images
image: TEST_DIR/t.IMGFMT
file format: IMGFMT
virtual size: 64M (67108864 bytes)
cluster_size: 65536
Format specific information:
compat: 1.1
lazy refcounts: false
refcount bits: 16
data file: TEST_DIR/t.IMGFMT.data
data file raw: false
corrupt: false
No errors were found on the image.
*** done *** done

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test case for snapshotting images with unallocated zero clusters in # Test case for snapshotting images with unallocated zero clusters in
# qcow2 # qcow2

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# test of qemu-img convert -n - convert without creation # test of qemu-img convert -n - convert without creation
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test VHDX read/write from a sample image created with Hyper-V # Test VHDX read/write from a sample image created with Hyper-V
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test case for preallocated zero clusters in qcow2 # Test case for preallocated zero clusters in qcow2
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test automatic deletion of BDSes created by -drive/drive_add # Test automatic deletion of BDSes created by -drive/drive_add
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test case for loading a saved VM state from a qcow2 image # Test case for loading a saved VM state from a qcow2 image
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test case for deleting a backing file # Test case for deleting a backing file
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test VHDX log replay from an image with a journal that needs to be # Test VHDX log replay from an image with a journal that needs to be
# replayed # replayed

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test case for the QMP blkdebug and blkverify interfaces # Test case for the QMP blkdebug and blkverify interfaces
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test case for nested image formats # Test case for nested image formats
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test count_contiguous_clusters in qcow2 # Test count_contiguous_clusters in qcow2
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
## ##
## qemu-img compare test (qcow2 only ones) ## qemu-img compare test (qcow2 only ones)
## ##

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# cloop format input validation tests # cloop format input validation tests
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# parallels format input validation tests # parallels format input validation tests
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test concurrent pread/pwrite # Test concurrent pread/pwrite
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# bochs format input validation tests # bochs format input validation tests
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test qcow2 preallocation with different cluster_sizes # Test qcow2 preallocation with different cluster_sizes
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# qcow2 format input validation tests # qcow2 format input validation tests
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test Quorum block driver # Test Quorum block driver
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test qemu-img command line parsing # Test qemu-img command line parsing
# #

View File

@ -48,6 +48,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -69,6 +71,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -90,6 +94,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -111,6 +117,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -132,6 +140,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -153,6 +163,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -174,6 +186,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -195,6 +209,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -231,6 +247,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -304,6 +322,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -325,6 +345,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -346,6 +368,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -367,6 +391,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -388,6 +414,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -409,6 +437,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -430,6 +460,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -451,6 +483,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -487,6 +521,8 @@ Supported options:
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -568,6 +604,8 @@ Creation options for 'qcow2':
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -590,6 +628,8 @@ Creation options for 'qcow2':
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -612,6 +652,8 @@ Creation options for 'qcow2':
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -634,6 +676,8 @@ Creation options for 'qcow2':
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -656,6 +700,8 @@ Creation options for 'qcow2':
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -678,6 +724,8 @@ Creation options for 'qcow2':
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -700,6 +748,8 @@ Creation options for 'qcow2':
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -722,6 +772,8 @@ Creation options for 'qcow2':
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
@ -761,6 +813,8 @@ Creation options for 'qcow2':
backing_fmt=<str> - Image format of the base image backing_fmt=<str> - Image format of the base image
cluster_size=<size> - qcow2 cluster size cluster_size=<size> - qcow2 cluster size
compat=<str> - Compatibility level (0.10 or 1.1) compat=<str> - Compatibility level (0.10 or 1.1)
data_file=<str> - File name of an external data file
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
encrypt.cipher-alg=<str> - Name of encryption cipher algorithm encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
encrypt.cipher-mode=<str> - Name of encryption cipher mode encrypt.cipher-mode=<str> - Name of encryption cipher mode
encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks' encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test NBD client unexpected disconnect # Test NBD client unexpected disconnect
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test case for VDI header corruption; image too large, and too many blocks. # Test case for VDI header corruption; image too large, and too many blocks.
# Also simple test for creating dynamic and static VDI images. # Also simple test for creating dynamic and static VDI images.

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Live snapshot tests # Live snapshot tests
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test qemu-img progress output # Test qemu-img progress output
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test unsupported blockdev-add cases # Test unsupported blockdev-add cases
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# vpc (VHD) format input validation tests # vpc (VHD) format input validation tests
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test case for support of JSON filenames # Test case for support of JSON filenames
# #

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test for discarding compressed clusters on qcow2 images # Test for discarding compressed clusters on qcow2 images
# #

Some files were not shown because too many files have changed in this diff Show More