mirror of https://github.com/proxmox/mirror_qemu
Merge remote branch 'kwolf/for-anthony' into staging
commit
21bcc5907f
16
block.c
16
block.c
|
@ -1899,6 +1899,22 @@ int bdrv_snapshot_list(BlockDriverState *bs,
|
|||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
int bdrv_snapshot_load_tmp(BlockDriverState *bs,
|
||||
const char *snapshot_name)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
if (!drv) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
if (!bs->read_only) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (drv->bdrv_snapshot_load_tmp) {
|
||||
return drv->bdrv_snapshot_load_tmp(bs, snapshot_name);
|
||||
}
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
#define NB_SUFFIXES 4
|
||||
|
||||
char *get_human_readable_size(char *buf, int buf_size, int64_t size)
|
||||
|
|
2
block.h
2
block.h
|
@ -211,6 +211,8 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
|||
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
|
||||
int bdrv_snapshot_list(BlockDriverState *bs,
|
||||
QEMUSnapshotInfo **psn_info);
|
||||
int bdrv_snapshot_load_tmp(BlockDriverState *bs,
|
||||
const char *snapshot_name);
|
||||
char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn);
|
||||
|
||||
char *get_human_readable_size(char *buf, int buf_size, int64_t size);
|
||||
|
|
|
@ -53,7 +53,8 @@ static AIOPool blkverify_aio_pool = {
|
|||
.cancel = blkverify_aio_cancel,
|
||||
};
|
||||
|
||||
static void blkverify_err(BlkverifyAIOCB *acb, const char *fmt, ...)
|
||||
static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyAIOCB *acb,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
|
@ -299,7 +300,7 @@ static void blkverify_verify_readv(BlkverifyAIOCB *acb)
|
|||
{
|
||||
ssize_t offset = blkverify_iovec_compare(acb->qiov, &acb->raw_qiov);
|
||||
if (offset != -1) {
|
||||
blkverify_err(acb, "contents mismatch in sector %ld",
|
||||
blkverify_err(acb, "contents mismatch in sector %lld",
|
||||
acb->sector_num + (offset / BDRV_SECTOR_SIZE));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include "block_int.h"
|
||||
#include "block/qcow2.h"
|
||||
|
||||
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size)
|
||||
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int new_l1_size, new_l1_size2, ret, i;
|
||||
|
@ -36,15 +36,22 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size)
|
|||
int64_t new_l1_table_offset;
|
||||
uint8_t data[12];
|
||||
|
||||
new_l1_size = s->l1_size;
|
||||
if (min_size <= new_l1_size)
|
||||
if (min_size <= s->l1_size)
|
||||
return 0;
|
||||
|
||||
if (exact_size) {
|
||||
new_l1_size = min_size;
|
||||
} else {
|
||||
/* Bump size up to reduce the number of times we have to grow */
|
||||
new_l1_size = s->l1_size;
|
||||
if (new_l1_size == 0) {
|
||||
new_l1_size = 1;
|
||||
}
|
||||
while (min_size > new_l1_size) {
|
||||
new_l1_size = (new_l1_size * 3 + 1) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ALLOC2
|
||||
printf("grow l1_table from %d to %d\n", s->l1_size, new_l1_size);
|
||||
#endif
|
||||
|
@ -550,7 +557,7 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
|
|||
|
||||
l1_index = offset >> (s->l2_bits + s->cluster_bits);
|
||||
if (l1_index >= s->l1_size) {
|
||||
ret = qcow2_grow_l1_table(bs, l1_index + 1);
|
||||
ret = qcow2_grow_l1_table(bs, l1_index + 1, false);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -327,7 +327,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
|
|||
if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1) < 0)
|
||||
goto fail;
|
||||
|
||||
if (qcow2_grow_l1_table(bs, sn->l1_size) < 0)
|
||||
if (qcow2_grow_l1_table(bs, sn->l1_size, true) < 0)
|
||||
goto fail;
|
||||
|
||||
s->l1_size = sn->l1_size;
|
||||
|
@ -418,3 +418,34 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
|||
return s->nb_snapshots;
|
||||
}
|
||||
|
||||
int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name)
|
||||
{
|
||||
int i, snapshot_index, l1_size2;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowSnapshot *sn;
|
||||
|
||||
snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_name);
|
||||
if (snapshot_index < 0) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
sn = &s->snapshots[snapshot_index];
|
||||
s->l1_size = sn->l1_size;
|
||||
l1_size2 = s->l1_size * sizeof(uint64_t);
|
||||
if (s->l1_table != NULL) {
|
||||
qemu_free(s->l1_table);
|
||||
}
|
||||
|
||||
s->l1_table_offset = sn->l1_table_offset;
|
||||
s->l1_table = qemu_mallocz(align_offset(l1_size2, 512));
|
||||
|
||||
if (bdrv_pread(bs->file, sn->l1_table_offset,
|
||||
s->l1_table, l1_size2) != l1_size2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(i = 0;i < s->l1_size; i++) {
|
||||
be64_to_cpus(&s->l1_table[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
288
block/qcow2.c
288
block/qcow2.c
|
@ -27,6 +27,7 @@
|
|||
#include <zlib.h>
|
||||
#include "aes.h"
|
||||
#include "block/qcow2.h"
|
||||
#include "qemu-error.h"
|
||||
|
||||
/*
|
||||
Differences with QCOW:
|
||||
|
@ -794,28 +795,6 @@ static int qcow2_change_backing_file(BlockDriverState *bs,
|
|||
return qcow2_update_ext_header(bs, backing_file, backing_fmt);
|
||||
}
|
||||
|
||||
static int get_bits_from_size(size_t size)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
if (size == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (size != 1) {
|
||||
/* Not a power of two */
|
||||
if (size & 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size >>= 1;
|
||||
res++;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static int preallocate(BlockDriverState *bs)
|
||||
{
|
||||
uint64_t nb_sectors;
|
||||
|
@ -871,199 +850,127 @@ static int preallocate(BlockDriverState *bs)
|
|||
|
||||
static int qcow_create2(const char *filename, int64_t total_size,
|
||||
const char *backing_file, const char *backing_format,
|
||||
int flags, size_t cluster_size, int prealloc)
|
||||
int flags, size_t cluster_size, int prealloc,
|
||||
QEMUOptionParameter *options)
|
||||
{
|
||||
/* Calulate cluster_bits */
|
||||
int cluster_bits;
|
||||
cluster_bits = ffs(cluster_size) - 1;
|
||||
if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS ||
|
||||
(1 << cluster_bits) != cluster_size)
|
||||
{
|
||||
error_report(
|
||||
"Cluster size must be a power of two between %d and %dk\n",
|
||||
1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int fd, header_size, backing_filename_len, l1_size, i, shift, l2_bits;
|
||||
int ref_clusters, reftable_clusters, backing_format_len = 0;
|
||||
int rounded_ext_bf_len = 0;
|
||||
/*
|
||||
* Open the image file and write a minimal qcow2 header.
|
||||
*
|
||||
* We keep things simple and start with a zero-sized image. We also
|
||||
* do without refcount blocks or a L1 table for now. We'll fix the
|
||||
* inconsistency later.
|
||||
*
|
||||
* We do need a refcount table because growing the refcount table means
|
||||
* allocating two new refcount blocks - the seconds of which would be at
|
||||
* 2 GB for 64k clusters, and we don't want to have a 2 GB initial file
|
||||
* size for any qcow2 image.
|
||||
*/
|
||||
BlockDriverState* bs;
|
||||
QCowHeader header;
|
||||
uint64_t tmp, offset;
|
||||
uint64_t old_ref_clusters;
|
||||
QCowCreateState s1, *s = &s1;
|
||||
QCowExtension ext_bf = {0, 0};
|
||||
uint8_t* refcount_table;
|
||||
int ret;
|
||||
|
||||
memset(s, 0, sizeof(*s));
|
||||
ret = bdrv_create_file(filename, options);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
ret = bdrv_file_open(&bs, filename, BDRV_O_RDWR);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Write the header */
|
||||
memset(&header, 0, sizeof(header));
|
||||
header.magic = cpu_to_be32(QCOW_MAGIC);
|
||||
header.version = cpu_to_be32(QCOW_VERSION);
|
||||
header.size = cpu_to_be64(total_size * 512);
|
||||
header_size = sizeof(header);
|
||||
backing_filename_len = 0;
|
||||
if (backing_file) {
|
||||
if (backing_format) {
|
||||
ext_bf.magic = QCOW_EXT_MAGIC_BACKING_FORMAT;
|
||||
backing_format_len = strlen(backing_format);
|
||||
ext_bf.len = backing_format_len;
|
||||
rounded_ext_bf_len = (sizeof(ext_bf) + ext_bf.len + 7) & ~7;
|
||||
header_size += rounded_ext_bf_len;
|
||||
}
|
||||
header.backing_file_offset = cpu_to_be64(header_size);
|
||||
backing_filename_len = strlen(backing_file);
|
||||
header.backing_file_size = cpu_to_be32(backing_filename_len);
|
||||
header_size += backing_filename_len;
|
||||
}
|
||||
header.cluster_bits = cpu_to_be32(cluster_bits);
|
||||
header.size = cpu_to_be64(0);
|
||||
header.l1_table_offset = cpu_to_be64(0);
|
||||
header.l1_size = cpu_to_be32(0);
|
||||
header.refcount_table_offset = cpu_to_be64(cluster_size);
|
||||
header.refcount_table_clusters = cpu_to_be32(1);
|
||||
|
||||
/* Cluster size */
|
||||
s->cluster_bits = get_bits_from_size(cluster_size);
|
||||
if (s->cluster_bits < MIN_CLUSTER_BITS ||
|
||||
s->cluster_bits > MAX_CLUSTER_BITS)
|
||||
{
|
||||
fprintf(stderr, "Cluster size must be a power of two between "
|
||||
"%d and %dk\n",
|
||||
1 << MIN_CLUSTER_BITS,
|
||||
1 << (MAX_CLUSTER_BITS - 10));
|
||||
return -EINVAL;
|
||||
}
|
||||
s->cluster_size = 1 << s->cluster_bits;
|
||||
|
||||
header.cluster_bits = cpu_to_be32(s->cluster_bits);
|
||||
header_size = (header_size + 7) & ~7;
|
||||
if (flags & BLOCK_FLAG_ENCRYPT) {
|
||||
header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
|
||||
} else {
|
||||
header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
|
||||
}
|
||||
l2_bits = s->cluster_bits - 3;
|
||||
shift = s->cluster_bits + l2_bits;
|
||||
l1_size = (((total_size * 512) + (1LL << shift) - 1) >> shift);
|
||||
offset = align_offset(header_size, s->cluster_size);
|
||||
s->l1_table_offset = offset;
|
||||
header.l1_table_offset = cpu_to_be64(s->l1_table_offset);
|
||||
header.l1_size = cpu_to_be32(l1_size);
|
||||
offset += align_offset(l1_size * sizeof(uint64_t), s->cluster_size);
|
||||
|
||||
/* count how many refcount blocks needed */
|
||||
|
||||
#define NUM_CLUSTERS(bytes) \
|
||||
(((bytes) + (s->cluster_size) - 1) / (s->cluster_size))
|
||||
|
||||
ref_clusters = NUM_CLUSTERS(NUM_CLUSTERS(offset) * sizeof(uint16_t));
|
||||
|
||||
do {
|
||||
uint64_t image_clusters;
|
||||
old_ref_clusters = ref_clusters;
|
||||
|
||||
/* Number of clusters used for the refcount table */
|
||||
reftable_clusters = NUM_CLUSTERS(ref_clusters * sizeof(uint64_t));
|
||||
|
||||
/* Number of clusters that the whole image will have */
|
||||
image_clusters = NUM_CLUSTERS(offset) + ref_clusters
|
||||
+ reftable_clusters;
|
||||
|
||||
/* Number of refcount blocks needed for the image */
|
||||
ref_clusters = NUM_CLUSTERS(image_clusters * sizeof(uint16_t));
|
||||
|
||||
} while (ref_clusters != old_ref_clusters);
|
||||
|
||||
s->refcount_table = qemu_mallocz(reftable_clusters * s->cluster_size);
|
||||
|
||||
s->refcount_table_offset = offset;
|
||||
header.refcount_table_offset = cpu_to_be64(offset);
|
||||
header.refcount_table_clusters = cpu_to_be32(reftable_clusters);
|
||||
offset += (reftable_clusters * s->cluster_size);
|
||||
s->refcount_block_offset = offset;
|
||||
|
||||
for (i=0; i < ref_clusters; i++) {
|
||||
s->refcount_table[i] = cpu_to_be64(offset);
|
||||
offset += s->cluster_size;
|
||||
ret = bdrv_pwrite(bs, 0, &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
s->refcount_block = qemu_mallocz(ref_clusters * s->cluster_size);
|
||||
/* Write an empty refcount table */
|
||||
refcount_table = qemu_mallocz(cluster_size);
|
||||
ret = bdrv_pwrite(bs, cluster_size, refcount_table, cluster_size);
|
||||
qemu_free(refcount_table);
|
||||
|
||||
/* update refcounts */
|
||||
qcow2_create_refcount_update(s, 0, header_size);
|
||||
qcow2_create_refcount_update(s, s->l1_table_offset,
|
||||
l1_size * sizeof(uint64_t));
|
||||
qcow2_create_refcount_update(s, s->refcount_table_offset,
|
||||
reftable_clusters * s->cluster_size);
|
||||
qcow2_create_refcount_update(s, s->refcount_block_offset,
|
||||
ref_clusters * s->cluster_size);
|
||||
|
||||
/* write all the data */
|
||||
ret = qemu_write_full(fd, &header, sizeof(header));
|
||||
if (ret != sizeof(header)) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
bdrv_close(bs);
|
||||
|
||||
/*
|
||||
* And now open the image and make it consistent first (i.e. increase the
|
||||
* refcount of the cluster that is occupied by the header and the refcount
|
||||
* table)
|
||||
*/
|
||||
BlockDriver* drv = bdrv_find_format("qcow2");
|
||||
assert(drv != NULL);
|
||||
ret = bdrv_open(bs, filename, BDRV_O_RDWR | BDRV_O_NO_FLUSH, drv);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = qcow2_alloc_clusters(bs, 2 * cluster_size);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
|
||||
} else if (ret != 0) {
|
||||
error_report("Huh, first cluster in empty image is already in use?");
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Okay, now that we have a valid image, let's give it the right size */
|
||||
ret = bdrv_truncate(bs, total_size * BDRV_SECTOR_SIZE);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Want a backing file? There you go.*/
|
||||
if (backing_file) {
|
||||
if (backing_format_len) {
|
||||
char zero[16];
|
||||
int padding = rounded_ext_bf_len - (ext_bf.len + sizeof(ext_bf));
|
||||
|
||||
memset(zero, 0, sizeof(zero));
|
||||
cpu_to_be32s(&ext_bf.magic);
|
||||
cpu_to_be32s(&ext_bf.len);
|
||||
ret = qemu_write_full(fd, &ext_bf, sizeof(ext_bf));
|
||||
if (ret != sizeof(ext_bf)) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
ret = bdrv_change_backing_file(bs, backing_file, backing_format);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
ret = qemu_write_full(fd, backing_format, backing_format_len);
|
||||
if (ret != backing_format_len) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
if (padding > 0) {
|
||||
ret = qemu_write_full(fd, zero, padding);
|
||||
if (ret != padding) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
ret = qemu_write_full(fd, backing_file, backing_filename_len);
|
||||
if (ret != backing_filename_len) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
lseek(fd, s->l1_table_offset, SEEK_SET);
|
||||
tmp = 0;
|
||||
for(i = 0;i < l1_size; i++) {
|
||||
ret = qemu_write_full(fd, &tmp, sizeof(tmp));
|
||||
if (ret != sizeof(tmp)) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
lseek(fd, s->refcount_table_offset, SEEK_SET);
|
||||
ret = qemu_write_full(fd, s->refcount_table,
|
||||
reftable_clusters * s->cluster_size);
|
||||
if (ret != reftable_clusters * s->cluster_size) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
lseek(fd, s->refcount_block_offset, SEEK_SET);
|
||||
ret = qemu_write_full(fd, s->refcount_block,
|
||||
ref_clusters * s->cluster_size);
|
||||
if (ret != ref_clusters * s->cluster_size) {
|
||||
ret = -errno;
|
||||
goto exit;
|
||||
/* And if we're supposed to preallocate metadata, do that now */
|
||||
if (prealloc) {
|
||||
ret = preallocate(bs);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
exit:
|
||||
qemu_free(s->refcount_table);
|
||||
qemu_free(s->refcount_block);
|
||||
close(fd);
|
||||
|
||||
/* Preallocate metadata */
|
||||
if (ret == 0 && prealloc) {
|
||||
BlockDriverState *bs;
|
||||
BlockDriver *drv = bdrv_find_format("qcow2");
|
||||
bs = bdrv_new("");
|
||||
bdrv_open(bs, filename, BDRV_O_CACHE_WB | BDRV_O_RDWR, drv);
|
||||
ret = preallocate(bs);
|
||||
bdrv_close(bs);
|
||||
}
|
||||
|
||||
out:
|
||||
bdrv_delete(bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1111,7 +1018,7 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
|
|||
}
|
||||
|
||||
return qcow_create2(filename, sectors, backing_file, backing_fmt, flags,
|
||||
cluster_size, prealloc);
|
||||
cluster_size, prealloc, options);
|
||||
}
|
||||
|
||||
static int qcow_make_empty(BlockDriverState *bs)
|
||||
|
@ -1154,7 +1061,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset)
|
|||
}
|
||||
|
||||
new_l1_size = size_to_l1(s, offset);
|
||||
ret = qcow2_grow_l1_table(bs, new_l1_size);
|
||||
ret = qcow2_grow_l1_table(bs, new_l1_size, true);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -1379,6 +1286,7 @@ static BlockDriver bdrv_qcow2 = {
|
|||
.bdrv_snapshot_goto = qcow2_snapshot_goto,
|
||||
.bdrv_snapshot_delete = qcow2_snapshot_delete,
|
||||
.bdrv_snapshot_list = qcow2_snapshot_list,
|
||||
.bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp,
|
||||
.bdrv_get_info = qcow_get_info,
|
||||
|
||||
.bdrv_save_vmstate = qcow_save_vmstate,
|
||||
|
|
|
@ -188,7 +188,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
|||
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res);
|
||||
|
||||
/* qcow2-cluster.c functions */
|
||||
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size);
|
||||
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size);
|
||||
void qcow2_l2_cache_reset(BlockDriverState *bs);
|
||||
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
|
||||
void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||
|
@ -211,6 +211,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
|
|||
int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id);
|
||||
int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
|
||||
int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab);
|
||||
int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name);
|
||||
|
||||
void qcow2_free_snapshots(BlockDriverState *bs);
|
||||
int qcow2_read_snapshots(BlockDriverState *bs);
|
||||
|
|
|
@ -93,6 +93,8 @@ struct BlockDriver {
|
|||
int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id);
|
||||
int (*bdrv_snapshot_list)(BlockDriverState *bs,
|
||||
QEMUSnapshotInfo **psn_info);
|
||||
int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs,
|
||||
const char *snapshot_name);
|
||||
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
|
||||
|
||||
int (*bdrv_save_vmstate)(BlockDriverState *bs, const uint8_t *buf,
|
||||
|
|
|
@ -65,6 +65,7 @@ static void ide_dma_start(IDEState *s, BlockDriverCompletionFunc *dma_cb);
|
|||
static void ide_dma_restart(IDEState *s, int is_read);
|
||||
static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret);
|
||||
static int ide_handle_rw_error(IDEState *s, int error, int op);
|
||||
static void ide_flush_cache(IDEState *s);
|
||||
|
||||
static void padstr(char *str, const char *src, int len)
|
||||
{
|
||||
|
@ -146,8 +147,8 @@ static void ide_identify(IDEState *s)
|
|||
put_le16(p + 68, 120);
|
||||
put_le16(p + 80, 0xf0); /* ata3 -> ata6 supported */
|
||||
put_le16(p + 81, 0x16); /* conforms to ata5 */
|
||||
/* 14=NOP supported, 0=SMART supported */
|
||||
put_le16(p + 82, (1 << 14) | 1);
|
||||
/* 14=NOP supported, 5=WCACHE supported, 0=SMART supported */
|
||||
put_le16(p + 82, (1 << 14) | (1 << 5) | 1);
|
||||
/* 13=flush_cache_ext,12=flush_cache,10=lba48 */
|
||||
put_le16(p + 83, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
|
||||
/* 14=set to 1, 1=SMART self test, 0=SMART error logging */
|
||||
|
@ -688,6 +689,8 @@ static void ide_dma_restart_bh(void *opaque)
|
|||
} else {
|
||||
ide_sector_write(bmdma_active_if(bm));
|
||||
}
|
||||
} else if (bm->status & BM_STATUS_RETRY_FLUSH) {
|
||||
ide_flush_cache(bmdma_active_if(bm));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -795,12 +798,26 @@ static void ide_flush_cb(void *opaque, int ret)
|
|||
{
|
||||
IDEState *s = opaque;
|
||||
|
||||
/* XXX: how do we signal I/O errors here? */
|
||||
if (ret < 0) {
|
||||
/* XXX: What sector number to set here? */
|
||||
if (ide_handle_rw_error(s, -ret, BM_STATUS_RETRY_FLUSH)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
s->status = READY_STAT | SEEK_STAT;
|
||||
ide_set_irq(s->bus);
|
||||
}
|
||||
|
||||
static void ide_flush_cache(IDEState *s)
|
||||
{
|
||||
if (s->bs) {
|
||||
bdrv_aio_flush(s->bs, ide_flush_cb, s);
|
||||
} else {
|
||||
ide_flush_cb(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void cpu_to_ube16(uint8_t *buf, int val)
|
||||
{
|
||||
buf[0] = val >> 8;
|
||||
|
@ -2031,10 +2048,7 @@ void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
|||
break;
|
||||
case WIN_FLUSH_CACHE:
|
||||
case WIN_FLUSH_CACHE_EXT:
|
||||
if (s->bs)
|
||||
bdrv_aio_flush(s->bs, ide_flush_cb, s);
|
||||
else
|
||||
ide_flush_cb(s, 0);
|
||||
ide_flush_cache(s);
|
||||
break;
|
||||
case WIN_STANDBY:
|
||||
case WIN_STANDBY2:
|
||||
|
|
|
@ -473,6 +473,7 @@ struct IDEDeviceInfo {
|
|||
#define BM_STATUS_DMA_RETRY 0x08
|
||||
#define BM_STATUS_PIO_RETRY 0x10
|
||||
#define BM_STATUS_RETRY_READ 0x20
|
||||
#define BM_STATUS_RETRY_FLUSH 0x40
|
||||
|
||||
#define BM_CMD_START 0x01
|
||||
#define BM_CMD_READ 0x08
|
||||
|
|
|
@ -106,7 +106,13 @@ static void virtio_blk_flush_complete(void *opaque, int ret)
|
|||
{
|
||||
VirtIOBlockReq *req = opaque;
|
||||
|
||||
virtio_blk_req_complete(req, ret ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK);
|
||||
if (ret) {
|
||||
if (virtio_blk_handle_rw_error(req, -ret, 0)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
|
||||
}
|
||||
|
||||
static VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s)
|
||||
|
|
|
@ -28,9 +28,9 @@ STEXI
|
|||
ETEXI
|
||||
|
||||
DEF("convert", img_convert,
|
||||
"convert [-c] [-f fmt] [-O output_fmt] [-o options] filename [filename2 [...]] output_filename")
|
||||
"convert [-c] [-f fmt] [-O output_fmt] [-o options] [-s snapshot_name] filename [filename2 [...]] output_filename")
|
||||
STEXI
|
||||
@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
||||
@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
||||
ETEXI
|
||||
|
||||
DEF("info", img_info,
|
||||
|
|
26
qemu-img.c
26
qemu-img.c
|
@ -645,14 +645,16 @@ static int img_convert(int argc, char **argv)
|
|||
const uint8_t *buf1;
|
||||
BlockDriverInfo bdi;
|
||||
QEMUOptionParameter *param = NULL, *create_options = NULL;
|
||||
QEMUOptionParameter *out_baseimg_param;
|
||||
char *options = NULL;
|
||||
const char *snapshot_name = NULL;
|
||||
|
||||
fmt = NULL;
|
||||
out_fmt = "raw";
|
||||
out_baseimg = NULL;
|
||||
flags = 0;
|
||||
for(;;) {
|
||||
c = getopt(argc, argv, "f:O:B:hce6o:");
|
||||
c = getopt(argc, argv, "f:O:B:s:hce6o:");
|
||||
if (c == -1)
|
||||
break;
|
||||
switch(c) {
|
||||
|
@ -680,6 +682,9 @@ static int img_convert(int argc, char **argv)
|
|||
case 'o':
|
||||
options = optarg;
|
||||
break;
|
||||
case 's':
|
||||
snapshot_name = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -711,6 +716,19 @@ static int img_convert(int argc, char **argv)
|
|||
total_sectors += bs_sectors;
|
||||
}
|
||||
|
||||
if (snapshot_name != NULL) {
|
||||
if (bs_n > 1) {
|
||||
error("No support for concatenating multiple snapshot\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
if (bdrv_snapshot_load_tmp(bs[0], snapshot_name) < 0) {
|
||||
error("Failed to load snapshot\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find driver and parse its options */
|
||||
drv = bdrv_find_format(out_fmt);
|
||||
if (!drv) {
|
||||
|
@ -752,6 +770,12 @@ static int img_convert(int argc, char **argv)
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* Get backing file name if -o backing_file was used */
|
||||
out_baseimg_param = get_option_parameter(param, BLOCK_OPT_BACKING_FILE);
|
||||
if (out_baseimg_param) {
|
||||
out_baseimg = out_baseimg_param->value.s;
|
||||
}
|
||||
|
||||
/* Check if compression is supported */
|
||||
if (flags & BLOCK_FLAG_COMPRESS) {
|
||||
QEMUOptionParameter *encryption =
|
||||
|
|
|
@ -77,9 +77,9 @@ it doesn't need to be specified separately in this case.
|
|||
|
||||
Commit the changes recorded in @var{filename} in its base image.
|
||||
|
||||
@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
||||
@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
||||
|
||||
Convert the disk image @var{filename} to disk image @var{output_filename}
|
||||
Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename}
|
||||
using format @var{output_fmt}. It can be optionally compressed (@code{-c}
|
||||
option) or use any format specific options like encryption (@code{-o} option).
|
||||
|
||||
|
|
39
qemu-io.c
39
qemu-io.c
|
@ -1442,6 +1442,44 @@ static const cmdinfo_t alloc_cmd = {
|
|||
.oneline = "checks if a sector is present in the file",
|
||||
};
|
||||
|
||||
static int
|
||||
map_f(int argc, char **argv)
|
||||
{
|
||||
int64_t offset;
|
||||
int64_t nb_sectors;
|
||||
char s1[64];
|
||||
int num, num_checked;
|
||||
int ret;
|
||||
const char *retstr;
|
||||
|
||||
offset = 0;
|
||||
nb_sectors = bs->total_sectors;
|
||||
|
||||
do {
|
||||
num_checked = MIN(nb_sectors, INT_MAX);
|
||||
ret = bdrv_is_allocated(bs, offset, num_checked, &num);
|
||||
retstr = ret ? " allocated" : "not allocated";
|
||||
cvtstr(offset << 9ULL, s1, sizeof(s1));
|
||||
printf("[% 24" PRId64 "] % 8d/% 8d sectors %s at offset %s (%d)\n",
|
||||
offset << 9ULL, num, num_checked, retstr, s1, ret);
|
||||
|
||||
offset += num;
|
||||
nb_sectors -= num;
|
||||
} while(offset < bs->total_sectors);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const cmdinfo_t map_cmd = {
|
||||
.name = "map",
|
||||
.argmin = 0,
|
||||
.argmax = 0,
|
||||
.cfunc = map_f,
|
||||
.args = "",
|
||||
.oneline = "prints the allocated areas of a file",
|
||||
};
|
||||
|
||||
|
||||
static int
|
||||
close_f(int argc, char **argv)
|
||||
{
|
||||
|
@ -1680,6 +1718,7 @@ int main(int argc, char **argv)
|
|||
add_command(&length_cmd);
|
||||
add_command(&info_cmd);
|
||||
add_command(&alloc_cmd);
|
||||
add_command(&map_cmd);
|
||||
|
||||
add_args_command(init_args_command);
|
||||
add_check_command(init_check_command);
|
||||
|
|
Loading…
Reference in New Issue